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
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;
[5983eca]106        for (l = txl->list; l; l = g_slist_next(l))
[62d2cfb]107                if (txl->type == TXL_STATUS)
[5983eca]108                        txs_free((struct twitter_xml_status *) l->data);
[62d2cfb]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.
[5983eca]123        if (!bee_user_by_handle(ic->bee, ic, name)) {
[e88fbe27]124                char *mode = set_getstr(&ic->acc->set, "mode");
[5983eca]125
[62d2cfb]126                // The buddy is not in the list, add the buddy and set the status to logged in.
[5983eca]127                imcb_add_buddy(ic, name, NULL);
128                imcb_rename_buddy(ic, name, fullname);
129                if (g_strcasecmp(mode, "chat") == 0) {
[5c18a76]130                        /* Necessary so that nicks always get translated to the
131                           exact Twitter username. */
[5983eca]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);
[62d2cfb]136        }
137}
[1b221e0]138
[a7b9ec7]139/* Warning: May return a malloc()ed value, which will be free()d on the next
140   call. Only for short-term use. */
[6eca2eb]141char *twitter_parse_error(struct http_request *req)
[a7b9ec7]142{
143        static char *ret = NULL;
[3d93aed]144        struct xt_parser *xp = NULL;
[a7b9ec7]145        struct xt_node *node;
[5983eca]146
[a7b9ec7]147        g_free(ret);
148        ret = NULL;
[5983eca]149
150        if (req->body_size > 0) {
[3d93aed]151                xp = xt_new(NULL, NULL);
152                xt_feed(xp, req->reply_body, req->body_size);
[5983eca]153
[3d93aed]154                if ((node = xt_find_node(xp->root, "hash")) &&
[5983eca]155                    (node = xt_find_node(node->children, "error")) && node->text_len > 0) {
[3d93aed]156                        ret = g_strdup_printf("%s (%s)", req->status_string, node->text);
157                }
[5983eca]158
[a7b9ec7]159                xt_free(xp);
160        }
[5983eca]161
[6eca2eb]162        return ret ? ret : req->status_string;
[a7b9ec7]163}
164
[1b221e0]165static void twitter_http_get_friends_ids(struct http_request *req);
166
167/**
168 * Get the friends ids.
169 */
[8203da9]170void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor)
[1b221e0]171{
[5983eca]172        // Primitive, but hey! It works...     
173        char *args[2];
[1b221e0]174        args[0] = "cursor";
[5983eca]175        args[1] = g_strdup_printf("%lld", (long long) next_cursor);
[bb5ce4d1]176        twitter_http(ic, TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, args, 2);
[1b221e0]177
178        g_free(args[1]);
179}
180
181/**
182 * Function to help fill a list.
183 */
[5983eca]184static xt_status twitter_xt_next_cursor(struct xt_node *node, struct twitter_xml_list *txl)
[1b221e0]185{
[bd64716]186        char *end = NULL;
[5983eca]187
188        if (node->text)
189                txl->next_cursor = g_ascii_strtoll(node->text, &end, 10);
190        if (end == NULL)
[bd64716]191                txl->next_cursor = -1;
[1b221e0]192
193        return XT_HANDLED;
194}
195
196/**
197 * Fill a list of ids.
198 */
[5983eca]199static xt_status twitter_xt_get_friends_id_list(struct xt_node *node, struct twitter_xml_list *txl)
[1b221e0]200{
201        struct xt_node *child;
[5983eca]202
[62d2cfb]203        // Set the list type.
204        txl->type = TXL_ID;
[1b221e0]205
206        // The root <statuses> node should hold the list of statuses <status>
207        // Walk over the nodes children.
[5983eca]208        for (child = node->children; child; child = child->next) {
209                if (g_strcasecmp("id", child->name) == 0) {
[1b221e0]210                        // Add the item to the list.
[5983eca]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) {
[1b221e0]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;
[3bd4a93]229        struct twitter_data *td;
[1b221e0]230
231        ic = req->data;
232
[62d2cfb]233        // Check if the connection is still active.
[5983eca]234        if (!g_slist_find(twitter_connections, ic))
[62d2cfb]235                return;
[5983eca]236
[37aa317]237        td = ic->proto_data;
[62d2cfb]238
[1b221e0]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.
[3bd4a93]242                if (++td->http_fails >= 5)
[a7b9ec7]243                        imcb_error(ic, "Could not retrieve friends: %s", twitter_parse_error(req));
[5983eca]244
[1b221e0]245                return;
[3bd4a93]246        } else {
247                td->http_fails = 0;
[1b221e0]248        }
249
250        txl = g_new0(struct twitter_xml_list, 1);
251
252        // Parse the data.
[5983eca]253        parser = xt_new(NULL, txl);
254        xt_feed(parser, req->reply_body, req->body_size);
[1b221e0]255        twitter_xt_get_friends_id_list(parser->root, txl);
[5983eca]256        xt_free(parser);
[1b221e0]257
258        if (txl->next_cursor)
259                twitter_get_friends_ids(ic, txl->next_cursor);
260
[62d2cfb]261        txl_free(txl);
[1b221e0]262}
263
264/**
265 * Function to fill a twitter_xml_user struct.
266 * It sets:
267 *  - the name and
268 *  - the screen_name.
269 */
[5983eca]270static xt_status twitter_xt_get_user(struct xt_node *node, struct twitter_xml_user *txu)
[1b221e0]271{
272        struct xt_node *child;
273
274        // Walk over the nodes children.
[5983eca]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);
[1b221e0]280                }
281        }
282        return XT_HANDLED;
283}
284
[62d2cfb]285/**
286 * Function to fill a twitter_xml_list struct.
287 * It sets:
288 *  - all <user>s from the <users> element.
289 */
[5983eca]290static xt_status twitter_xt_get_users(struct xt_node *node, struct twitter_xml_list *txl)
[62d2cfb]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.
[5983eca]300        for (child = node->children; child; child = child->next) {
301                if (g_strcasecmp("user", child->name) == 0) {
[62d2cfb]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.
[5983eca]305                        txl->list = g_slist_prepend(txl->list, txu);
[62d2cfb]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 */
[5983eca]318static xt_status twitter_xt_get_user_list(struct xt_node *node, struct twitter_xml_list *txl)
[62d2cfb]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.
[5983eca]327        for (child = node->children; child; child = child->next) {
328                if (g_strcasecmp("users", child->name) == 0) {
[62d2cfb]329                        twitter_xt_get_users(child, txl);
[5983eca]330                } else if (g_strcasecmp("next_cursor", child->name) == 0) {
[62d2cfb]331                        twitter_xt_next_cursor(child, txl);
332                }
333        }
334
335        return XT_HANDLED;
336}
337
[2b02617]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
[62d2cfb]343
[1b221e0]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 */
[5983eca]352static xt_status twitter_xt_get_status(struct xt_node *node, struct twitter_xml_status *txs)
[1b221e0]353{
[bd599b9]354        struct xt_node *child, *rt = NULL;
[e193aeb]355        gboolean truncated = FALSE;
[1b221e0]356
357        // Walk over the nodes children.
[5983eca]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) {
[e193aeb]362                        truncated = bool2int(child->text);
[5983eca]363                } else if (g_strcasecmp("retweeted_status", child->name) == 0) {
[e193aeb]364                        rt = child;
[5983eca]365                } else if (g_strcasecmp("created_at", child->name) == 0) {
[08579a1]366                        struct tm parsed;
[5983eca]367
[08579a1]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. */
[5983eca]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) {
[1b221e0]374                        txs->user = g_new0(struct twitter_xml_user, 1);
[5983eca]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);
[ce81acd]380                }
[1b221e0]381        }
[5983eca]382
[e193aeb]383        /* If it's a truncated retweet, get the original because dots suck. */
[5983eca]384        if (truncated && rt) {
[e193aeb]385                struct twitter_xml_status *rtxs = g_new0(struct twitter_xml_status, 1);
[5983eca]386                if (twitter_xt_get_status(rt, rtxs) != XT_HANDLED) {
[e193aeb]387                        txs_free(rtxs);
388                        return XT_HANDLED;
389                }
[5983eca]390
[e193aeb]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        }
[5983eca]395
[1b221e0]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 */
[5983eca]405static xt_status twitter_xt_get_status_list(struct im_connection *ic, struct xt_node *node,
406                                            struct twitter_xml_list *txl)
[1b221e0]407{
408        struct twitter_xml_status *txs;
409        struct xt_node *child;
[203a2d2]410        bee_user_t *bu;
[1b221e0]411
[62d2cfb]412        // Set the type of the list.
413        txl->type = TXL_STATUS;
414
[1b221e0]415        // The root <statuses> node should hold the list of statuses <status>
416        // Walk over the nodes children.
[5983eca]417        for (child = node->children; child; child = child->next) {
418                if (g_strcasecmp("status", child->name) == 0) {
[1b221e0]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.
[5983eca]422                        txl->list = g_slist_prepend(txl->list, txs);
423
[203a2d2]424                        if (txs->user && txs->user->screen_name &&
[5983eca]425                            (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) {
[203a2d2]426                                struct twitter_user_data *tud = bu->data;
[5983eca]427
428                                if (txs->id > tud->last_id) {
[203a2d2]429                                        tud->last_id = txs->id;
430                                        tud->last_time = txs->created_at;
431                                }
432                        }
[5983eca]433                } else if (g_strcasecmp("next_cursor", child->name) == 0) {
[1b221e0]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 */
[8203da9]446void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)
[1b221e0]447{
448        struct twitter_data *td = ic->proto_data;
449
[5983eca]450        char *args[4];
[1b221e0]451        args[0] = "cursor";
[5983eca]452        args[1] = g_strdup_printf("%lld", (long long) next_cursor);
[1b221e0]453        if (td->home_timeline_id) {
454                args[2] = "since_id";
[5983eca]455                args[3] = g_strdup_printf("%llu", (long long unsigned int) td->home_timeline_id);
[1b221e0]456        }
457
[5983eca]458        twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args,
459                     td->home_timeline_id ? 4 : 2);
[1b221e0]460
461        g_free(args[1]);
462        if (td->home_timeline_id) {
463                g_free(args[3]);
464        }
465}
466
[ce81acd]467static char *twitter_msg_add_id(struct im_connection *ic,
[5983eca]468                                struct twitter_xml_status *txs, const char *prefix)
[ce81acd]469{
470        struct twitter_data *td = ic->proto_data;
471        char *ret = NULL;
[5983eca]472
473        if (!set_getbool(&ic->acc->set, "show_ids")) {
[ce81acd]474                if (*prefix)
475                        return g_strconcat(prefix, txs->text, NULL);
476                else
477                        return NULL;
478        }
[5983eca]479
[ce81acd]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);
[5983eca]482        if (txs->reply_to) {
[ce81acd]483                int i;
[5983eca]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);
[ce81acd]488                                break;
489                        }
490        }
491        if (ret == NULL)
[5983eca]492                ret = g_strdup_printf("\002[\002%02d\002]\002 %s%s", td->log_id, prefix, txs->text);
[ce81acd]493        td->log_id = (td->log_id + 1) % TWITTER_LOG_LENGTH;
[5983eca]494
[ce81acd]495        return ret;
496}
497
[d6aa6dd]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;
[fd65edb]503        GSList *l;
[5983eca]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) {
[fd65edb]512                bee_user_t *bu = l->data;
[5983eca]513                if (bu->ic == ic)
514                        imcb_chat_add_buddy(td->home_timeline_gc, bu->handle);
[fd65edb]515        }
[d6aa6dd]516}
517
[62d2cfb]518/**
519 * Function that is called to see the statuses in a groupchat window.
520 */
[5983eca]521static void twitter_groupchat(struct im_connection *ic, GSList * list)
[62d2cfb]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)
[d6aa6dd]530                twitter_groupchat_init(ic);
[5983eca]531
[d6aa6dd]532        gc = td->home_timeline_gc;
533        if (!gc->joined)
[5983eca]534                imcb_chat_add_buddy(gc, ic->acc->user);
[62d2cfb]535
[5983eca]536        for (l = list; l; l = g_slist_next(l)) {
[ce81acd]537                char *msg;
[5983eca]538
[62d2cfb]539                status = l->data;
[a26af5c]540                if (status->user == NULL || status->text == NULL)
541                        continue;
542
[3e69802]543                twitter_add_buddy(ic, status->user->screen_name, status->user->name);
[5983eca]544
[0b3ffb1]545                strip_html(status->text);
[ce81acd]546                msg = twitter_msg_add_id(ic, status, "");
[5983eca]547
[62d2cfb]548                // Say it!
[d569019]549                if (g_strcasecmp(td->user, status->user->screen_name) == 0)
[ce81acd]550                        imcb_chat_log(gc, "You: %s", msg ? msg : status->text);
[d569019]551                else
[ce81acd]552                        imcb_chat_msg(gc, status->user->screen_name,
[5983eca]553                                      msg ? msg : status->text, 0, status->created_at);
554
[ce81acd]555                g_free(msg);
[5983eca]556
[62d2cfb]557                // Update the home_timeline_id to hold the highest id, so that by the next request
[ce81acd]558                // we won't pick up the updates already in the list.
559                td->home_timeline_id = MAX(td->home_timeline_id, status->id);
[62d2cfb]560        }
561}
562
563/**
564 * Function that is called to see statuses as private messages.
565 */
[5983eca]566static void twitter_private_message_chat(struct im_connection *ic, GSList * list)
[62d2cfb]567{
568        struct twitter_data *td = ic->proto_data;
569        GSList *l = NULL;
570        struct twitter_xml_status *status;
[e88fbe27]571        char from[MAX_STRING];
572        gboolean mode_one;
[62d2cfb]573
[5983eca]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';
[e88fbe27]579        }
[5983eca]580
581        for (l = list; l; l = g_slist_next(l)) {
[ce81acd]582                char *prefix = NULL, *text = NULL;
[5983eca]583
[62d2cfb]584                status = l->data;
[5983eca]585
586                strip_html(status->text);
587                if (mode_one)
[ce81acd]588                        prefix = g_strdup_printf("\002<\002%s\002>\002 ",
[5983eca]589                                                 status->user->screen_name);
[55b1e69]590                else
591                        twitter_add_buddy(ic, status->user->screen_name, status->user->name);
[5983eca]592
[ce81acd]593                text = twitter_msg_add_id(ic, status, prefix ? prefix : "");
[5983eca]594
595                imcb_buddy_msg(ic,
596                               mode_one ? from : status->user->screen_name,
597                               text ? text : status->text, 0, status->created_at);
598
[62d2cfb]599                // Update the home_timeline_id to hold the highest id, so that by the next request
[ce81acd]600                // we won't pick up the updates already in the list.
[5983eca]601                td->home_timeline_id = MAX(td->home_timeline_id, status->id);
602
603                g_free(text);
604                g_free(prefix);
[62d2cfb]605        }
606}
607
[1b221e0]608/**
609 * Callback for getting the home timeline.
610 */
611static void twitter_http_get_home_timeline(struct http_request *req)
612{
[62d2cfb]613        struct im_connection *ic = req->data;
[37aa317]614        struct twitter_data *td;
[1b221e0]615        struct xt_parser *parser;
616        struct twitter_xml_list *txl;
[62d2cfb]617
618        // Check if the connection is still active.
[5983eca]619        if (!g_slist_find(twitter_connections, ic))
[62d2cfb]620                return;
[5983eca]621
[37aa317]622        td = ic->proto_data;
[1b221e0]623
624        // Check if the HTTP request went well.
[5983eca]625        if (req->status_code == 200) {
[3bd4a93]626                td->http_fails = 0;
[c2ecadc]627                if (!(ic->flags & OPT_LOGGED_IN))
[3bd4a93]628                        imcb_connected(ic);
[5983eca]629        } else if (req->status_code == 401) {
630                imcb_error(ic, "Authentication failure");
631                imc_logout(ic, FALSE);
[3bd4a93]632                return;
[5983eca]633        } else {
[1b221e0]634                // It didn't go well, output the error and return.
[3bd4a93]635                if (++td->http_fails >= 5)
[5983eca]636                        imcb_error(ic, "Could not retrieve " TWITTER_HOME_TIMELINE_URL ": %s",
637                                   twitter_parse_error(req));
638
[1b221e0]639                return;
640        }
641
642        txl = g_new0(struct twitter_xml_list, 1);
643        txl->list = NULL;
[62d2cfb]644
[1b221e0]645        // Parse the data.
[5983eca]646        parser = xt_new(NULL, txl);
647        xt_feed(parser, req->reply_body, req->body_size);
[1b221e0]648        // The root <statuses> node should hold the list of statuses <status>
[203a2d2]649        twitter_xt_get_status_list(ic, parser->root, txl);
[5983eca]650        xt_free(parser);
[1b221e0]651
[62d2cfb]652        // See if the user wants to see the messages in a groupchat window or as private messages.
[5983eca]653        if (txl->list == NULL);
[fd65edb]654        else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
[62d2cfb]655                twitter_groupchat(ic, txl->list);
[b4dd253]656        else
[62d2cfb]657                twitter_private_message_chat(ic, txl->list);
[1b221e0]658
[5983eca]659        // Free the structure. 
[62d2cfb]660        txl_free(txl);
[1b221e0]661}
662
663/**
[62d2cfb]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!
[1b221e0]669 */
[62d2cfb]670static void twitter_http_get_statuses_friends(struct http_request *req)
[1b221e0]671{
[62d2cfb]672        struct im_connection *ic = req->data;
[37aa317]673        struct twitter_data *td;
[62d2cfb]674        struct xt_parser *parser;
675        struct twitter_xml_list *txl;
[2abceca]676        GSList *l = NULL;
677        struct twitter_xml_user *user;
[62d2cfb]678
679        // Check if the connection is still active.
[5983eca]680        if (!g_slist_find(twitter_connections, ic))
[62d2cfb]681                return;
[5983eca]682
[37aa317]683        td = ic->proto_data;
[5983eca]684
[62d2cfb]685        // Check if the HTTP request went well.
[5983eca]686        if (req->status_code == 401) {
687                imcb_error(ic, "Authentication failure");
688                imc_logout(ic, FALSE);
[d6aa6dd]689                return;
690        } else if (req->status_code != 200) {
[62d2cfb]691                // It didn't go well, output the error and return.
[5983eca]692                imcb_error(ic, "Could not retrieve " TWITTER_SHOW_FRIENDS_URL ": %s",
693                           twitter_parse_error(req));
694                imc_logout(ic, TRUE);
[62d2cfb]695                return;
[3bd4a93]696        } else {
697                td->http_fails = 0;
[62d2cfb]698        }
[5983eca]699
700        if (!td->home_timeline_gc && g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
701                twitter_groupchat_init(ic);
[62d2cfb]702
703        txl = g_new0(struct twitter_xml_list, 1);
704        txl->list = NULL;
705
706        // Parse the data.
[5983eca]707        parser = xt_new(NULL, txl);
708        xt_feed(parser, req->reply_body, req->body_size);
[62d2cfb]709
710        // Get the user list from the parsed xml feed.
711        twitter_xt_get_user_list(parser->root, txl);
[5983eca]712        xt_free(parser);
[62d2cfb]713
714        // Add the users as buddies.
[5983eca]715        for (l = txl->list; l; l = g_slist_next(l)) {
[62d2cfb]716                user = l->data;
[3e69802]717                twitter_add_buddy(ic, user->screen_name, user->name);
[62d2cfb]718        }
[1b221e0]719
[62d2cfb]720        // if the next_cursor is set to something bigger then 0 there are more friends to gather.
[5983eca]721        if (txl->next_cursor > 0) {
[62d2cfb]722                twitter_get_statuses_friends(ic, txl->next_cursor);
[5983eca]723        } else {
[051372c]724                td->flags |= TWITTER_HAVE_FRIENDS;
725                twitter_login_finish(ic);
726        }
[5983eca]727
[62d2cfb]728        // Free the structure.
729        txl_free(txl);
[1b221e0]730}
731
732/**
[62d2cfb]733 * Get the friends.
[1b221e0]734 */
[8203da9]735void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor)
[1b221e0]736{
[5983eca]737        char *args[2];
[62d2cfb]738        args[0] = "cursor";
[5983eca]739        args[1] = g_strdup_printf("%lld", (long long) next_cursor);
[62d2cfb]740
[5983eca]741        twitter_http(ic, TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, args,
742                     2);
[62d2cfb]743
744        g_free(args[1]);
[1b221e0]745}
746
747/**
[7d53efb]748 * Callback to use after sending a post request to twitter.
[1b221e0]749 */
[7d53efb]750static void twitter_http_post(struct http_request *req)
[1b221e0]751{
752        struct im_connection *ic = req->data;
[7b87539]753        struct twitter_data *td;
[1b221e0]754
[62d2cfb]755        // Check if the connection is still active.
[5983eca]756        if (!g_slist_find(twitter_connections, ic))
[62d2cfb]757                return;
758
[7b87539]759        td = ic->proto_data;
760        td->last_status_id = 0;
[5983eca]761
[1b221e0]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.
[ba3233c]765                imcb_error(ic, "HTTP error: %s", twitter_parse_error(req));
[1b221e0]766                return;
767        }
[5983eca]768
769        if (req->body_size > 0) {
[7b87539]770                struct xt_parser *xp = NULL;
771                struct xt_node *node;
[5983eca]772
[7b87539]773                xp = xt_new(NULL, NULL);
774                xt_feed(xp, req->reply_body, req->body_size);
[5983eca]775
[7b87539]776                if ((node = xt_find_node(xp->root, "status")) &&
777                    (node = xt_find_node(node->children, "id")) && node->text)
[5983eca]778                        td->last_status_id = g_ascii_strtoull(node->text, NULL, 10);
779
[7b87539]780                xt_free(xp);
781        }
[1b221e0]782}
783
784/**
785 * Function to POST a new status to twitter.
[5983eca]786 */
[b890626]787void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_to)
[1b221e0]788{
[5983eca]789        char *args[4] = {
[b890626]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,
[5983eca]795                     args, in_reply_to ? 4 : 2);
[b890626]796        g_free(args[3]);
[1b221e0]797}
798
799
[62d2cfb]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{
[5983eca]805        char *args[4];
[62d2cfb]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.
[ba3233c]811        twitter_http(ic, TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post, ic, 1, args, 4);
[5983eca]812//      g_free(args[1]);
813//      g_free(args[3]);
[62d2cfb]814}
[7d53efb]815
816void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int create)
817{
[5983eca]818        char *args[2];
[7d53efb]819        args[0] = "screen_name";
820        args[1] = who;
[5983eca]821        twitter_http(ic, create ? TWITTER_FRIENDSHIPS_CREATE_URL : TWITTER_FRIENDSHIPS_DESTROY_URL,
822                     twitter_http_post, ic, 1, args, 2);
[a26af5c]823}
[7b87539]824
825void twitter_status_destroy(struct im_connection *ic, guint64 id)
826{
827        char *url;
[5983eca]828        url =
829            g_strdup_printf("%s%llu%s", TWITTER_STATUS_DESTROY_URL, (unsigned long long) id,
830                            ".xml");
[7b87539]831        twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0);
832        g_free(url);
833}
[b890626]834
835void twitter_status_retweet(struct im_connection *ic, guint64 id)
836{
837        char *url;
[5983eca]838        url =
839            g_strdup_printf("%s%llu%s", TWITTER_STATUS_RETWEET_URL, (unsigned long long) id,
840                            ".xml");
[b890626]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.