Ignore:
Timestamp:
2011-12-17T13:50:01Z (13 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Branches:
master
Children:
18c6d36
Parents:
87dddee (diff), 17f057d (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Mainline merge.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • protocols/twitter/twitter_lib.c

    r87dddee r6e9ae72  
    7878        if (txu == NULL)
    7979                return;
     80
    8081        g_free(txu->name);
    8182        g_free(txu->screen_name);
     
    8384}
    8485
    85 
    8686/**
    8787 * Frees a twitter_xml_status struct.
     
    8989static void txs_free(struct twitter_xml_status *txs)
    9090{
     91        if (txs == NULL)
     92                return;
     93
    9194        g_free(txs->text);
    9295        txu_free(txs->user);
     
    103106        if (txl == NULL)
    104107                return;
    105         for (l = txl->list; l; l = g_slist_next(l))
    106                 if (txl->type == TXL_STATUS)
     108
     109        for (l = txl->list; l; l = g_slist_next(l)) {
     110                if (txl->type == TXL_STATUS) {
    107111                        txs_free((struct twitter_xml_status *) l->data);
    108                 else if (txl->type == TXL_ID)
     112                } else if (txl->type == TXL_ID) {
    109113                        g_free(l->data);
    110                 else if (txl->type == TXL_USER)
     114                } else if (txl->type == TXL_USER) {
    111115                        txu_free(l->data);
     116                }
     117        }
     118
    112119        g_slist_free(txl->list);
    113120        g_free(txl);
     
    115122
    116123/**
    117  * Add a buddy if it is not allready added, set the status to logged in.
     124 * Compare status elements
     125 */
     126static gint twitter_compare_elements(gconstpointer a, gconstpointer b)
     127{
     128        struct twitter_xml_status *a_status = (struct twitter_xml_status *) a;
     129        struct twitter_xml_status *b_status = (struct twitter_xml_status *) b;
     130
     131        if (a_status->created_at < b_status->created_at) {
     132                return -1;
     133        } else if (a_status->created_at > b_status->created_at) {
     134                return 1;
     135        } else {
     136                return 0;
     137        }
     138}
     139
     140/**
     141 * Add a buddy if it is not already added, set the status to logged in.
    118142 */
    119143static void twitter_add_buddy(struct im_connection *ic, char *name, const char *fullname)
     
    132156                           exact Twitter username. */
    133157                        imcb_buddy_nick_hint(ic, name, name);
    134                         imcb_chat_add_buddy(td->home_timeline_gc, name);
     158                        imcb_chat_add_buddy(td->timeline_gc, name);
    135159                } else if (g_strcasecmp(mode, "many") == 0)
    136160                        imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL);
     
    260284
    261285        /* Create the room now that we "logged in". */
    262         if (!td->home_timeline_gc && g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
     286        if (!td->timeline_gc && g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
    263287                twitter_groupchat_init(ic);
    264288
     
    436460{
    437461        struct xt_node *child, *rt = NULL;
    438         gboolean truncated = FALSE;
    439462
    440463        // Walk over the nodes children.
     
    442465                if (g_strcasecmp("text", child->name) == 0) {
    443466                        txs->text = g_memdup(child->text, child->text_len + 1);
    444                 } else if (g_strcasecmp("truncated", child->name) == 0 && child->text) {
    445                         truncated = bool2int(child->text);
    446467                } else if (g_strcasecmp("retweeted_status", child->name) == 0) {
    447468                        rt = child;
     
    464485        }
    465486
    466         /* If it's a truncated retweet, get the original because dots suck. */
    467         if (truncated && rt) {
     487        /* If it's a (truncated) retweet, get the original. Even if the API claims it
     488           wasn't truncated because it may be lying. */
     489        if (rt) {
    468490                struct twitter_xml_status *rtxs = g_new0(struct twitter_xml_status, 1);
    469491                if (twitter_xt_get_status(rt, rtxs) != XT_HANDLED) {
     
    475497                txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text);
    476498                txs_free(rtxs);
     499        } else {
     500                struct xt_node *urls, *url;
     501               
     502                urls = xt_find_path(node, "entities/urls");
     503                for (url = urls ? urls->children : NULL; url; url = url->next) {
     504                        /* "short" is a reserved word. :-P */
     505                        struct xt_node *kort = xt_find_node(url->children, "url");
     506                        struct xt_node *disp = xt_find_node(url->children, "display_url");
     507                        char *pos, *new;
     508                       
     509                        if (!kort || !kort->text || !disp || !disp->text ||
     510                            !(pos = strstr(txs->text, kort->text)))
     511                                continue;
     512                       
     513                        *pos = '\0';
     514                        new = g_strdup_printf("%s%s &lt;%s&gt;%s", txs->text, kort->text,
     515                                              disp->text, pos + strlen(kort->text));
     516                       
     517                        g_free(txs->text);
     518                        txs->text = new;
     519                }
    477520        }
    478521
     
    520563
    521564        return XT_HANDLED;
    522 }
    523 
    524 static void twitter_http_get_home_timeline(struct http_request *req);
    525 
    526 /**
    527  * Get the timeline.
    528  */
    529 void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)
    530 {
    531         struct twitter_data *td = ic->proto_data;
    532 
    533         char *args[4];
    534         args[0] = "cursor";
    535         args[1] = g_strdup_printf("%lld", (long long) next_cursor);
    536         if (td->home_timeline_id) {
    537                 args[2] = "since_id";
    538                 args[3] = g_strdup_printf("%llu", (long long unsigned int) td->home_timeline_id);
    539         }
    540 
    541         twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args,
    542                      td->home_timeline_id ? 4 : 2);
    543 
    544         g_free(args[1]);
    545         if (td->home_timeline_id) {
    546                 g_free(args[3]);
    547         }
    548565}
    549566
     
    586603        GSList *l;
    587604
    588         td->home_timeline_gc = gc = imcb_chat_new(ic, "home/timeline");
     605        td->timeline_gc = gc = imcb_chat_new(ic, "twitter/timeline");
    589606
    590607        name_hint = g_strdup_printf("%s_%s", td->prefix, ic->acc->user);
     
    595612                bee_user_t *bu = l->data;
    596613                if (bu->ic == ic)
    597                         imcb_chat_add_buddy(td->home_timeline_gc, bu->handle);
     614                        imcb_chat_add_buddy(td->timeline_gc, bu->handle);
    598615        }
    599616}
     
    608625        struct twitter_xml_status *status;
    609626        struct groupchat *gc;
     627        guint64 last_id = 0;
    610628
    611629        // Create a new groupchat if it does not exsist.
    612         if (!td->home_timeline_gc)
     630        if (!td->timeline_gc)
    613631                twitter_groupchat_init(ic);
    614632
    615         gc = td->home_timeline_gc;
     633        gc = td->timeline_gc;
    616634        if (!gc->joined)
    617635                imcb_chat_add_buddy(gc, ic->acc->user);
     
    621639
    622640                status = l->data;
    623                 if (status->user == NULL || status->text == NULL)
     641                if (status->user == NULL || status->text == NULL || last_id == status->id)
    624642                        continue;
    625643
    626                 twitter_add_buddy(ic, status->user->screen_name, status->user->name);
     644                last_id = status->id;
    627645
    628646                strip_html(status->text);
     647
    629648                msg = twitter_msg_add_id(ic, status, "");
    630649
    631650                // Say it!
    632                 if (g_strcasecmp(td->user, status->user->screen_name) == 0)
     651                if (g_strcasecmp(td->user, status->user->screen_name) == 0) {
    633652                        imcb_chat_log(gc, "You: %s", msg ? msg : status->text);
    634                 else
     653                } else {
     654                        twitter_add_buddy(ic, status->user->screen_name, status->user->name);
     655
    635656                        imcb_chat_msg(gc, status->user->screen_name,
    636657                                      msg ? msg : status->text, 0, status->created_at);
     658                }
    637659
    638660                g_free(msg);
    639661
    640                 // Update the home_timeline_id to hold the highest id, so that by the next request
     662                // Update the timeline_id to hold the highest id, so that by the next request
    641663                // we won't pick up the updates already in the list.
    642                 td->home_timeline_id = MAX(td->home_timeline_id, status->id);
     664                td->timeline_id = MAX(td->timeline_id, status->id);
    643665        }
    644666}
     
    654676        char from[MAX_STRING];
    655677        gboolean mode_one;
     678        guint64 last_id = 0;
    656679
    657680        mode_one = g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "one") == 0;
     
    666689
    667690                status = l->data;
     691                if (status->user == NULL || status->text == NULL || last_id == status->id)
     692                        continue;
     693
     694                last_id = status->id;
    668695
    669696                strip_html(status->text);
     
    680707                               text ? text : status->text, 0, status->created_at);
    681708
    682                 // Update the home_timeline_id to hold the highest id, so that by the next request
     709                // Update the timeline_id to hold the highest id, so that by the next request
    683710                // we won't pick up the updates already in the list.
    684                 td->home_timeline_id = MAX(td->home_timeline_id, status->id);
     711                td->timeline_id = MAX(td->timeline_id, status->id);
    685712
    686713                g_free(text);
    687714                g_free(prefix);
     715        }
     716}
     717
     718static void twitter_http_get_home_timeline(struct http_request *req);
     719static void twitter_http_get_mentions(struct http_request *req);
     720
     721/**
     722 * Get the timeline with optionally mentions
     723 */
     724void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor)
     725{
     726        struct twitter_data *td = ic->proto_data;
     727        gboolean include_mentions = set_getbool(&ic->acc->set, "fetch_mentions");
     728
     729        if (td->flags & TWITTER_DOING_TIMELINE) {
     730                return;
     731        }
     732
     733        td->flags |= TWITTER_DOING_TIMELINE;
     734
     735        twitter_get_home_timeline(ic, next_cursor);
     736
     737        if (include_mentions) {
     738                twitter_get_mentions(ic, next_cursor);
     739        }
     740}
     741
     742/**
     743 * Call this one after receiving timeline/mentions. Show to user once we have
     744 * both.
     745 */
     746void twitter_flush_timeline(struct im_connection *ic)
     747{
     748        struct twitter_data *td = ic->proto_data;
     749        gboolean include_mentions = set_getbool(&ic->acc->set, "fetch_mentions");
     750        gboolean show_old_mentions = set_getbool(&ic->acc->set, "show_old_mentions");
     751        struct twitter_xml_list *home_timeline = td->home_timeline_obj;
     752        struct twitter_xml_list *mentions = td->mentions_obj;
     753        GSList *output = NULL;
     754        GSList *l;
     755
     756        if (!(td->flags & TWITTER_GOT_TIMELINE)) {
     757                return;
     758        }
     759
     760        if (include_mentions && !(td->flags & TWITTER_GOT_MENTIONS)) {
     761                return;
     762        }
     763
     764        if (home_timeline && home_timeline->list) {
     765                for (l = home_timeline->list; l; l = g_slist_next(l)) {
     766                        output = g_slist_insert_sorted(output, l->data, twitter_compare_elements);
     767                }
     768        }
     769
     770        if (include_mentions && mentions && mentions->list) {
     771                for (l = mentions->list; l; l = g_slist_next(l)) {
     772                        if (!show_old_mentions && output && twitter_compare_elements(l->data, output->data) < 0) {
     773                                continue;
     774                        }
     775
     776                        output = g_slist_insert_sorted(output, l->data, twitter_compare_elements);
     777                }
     778        }
     779
     780        // See if the user wants to see the messages in a groupchat window or as private messages.
     781        if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
     782                twitter_groupchat(ic, output);
     783        else
     784                twitter_private_message_chat(ic, output);
     785
     786        g_slist_free(output);
     787
     788        if (home_timeline && home_timeline->list) {
     789                txl_free(home_timeline);
     790        }
     791
     792        if (mentions && mentions->list) {
     793                txl_free(mentions);
     794        }
     795
     796        td->flags &= ~(TWITTER_DOING_TIMELINE | TWITTER_GOT_TIMELINE | TWITTER_GOT_MENTIONS);
     797}
     798
     799/**
     800 * Get the timeline.
     801 */
     802void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)
     803{
     804        struct twitter_data *td = ic->proto_data;
     805
     806        td->home_timeline_obj = NULL;
     807        td->flags &= ~TWITTER_GOT_TIMELINE;
     808
     809        char *args[6];
     810        args[0] = "cursor";
     811        args[1] = g_strdup_printf("%lld", (long long) next_cursor);
     812        args[2] = "include_entities";
     813        args[3] = "true";
     814        if (td->timeline_id) {
     815                args[4] = "since_id";
     816                args[5] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id);
     817        }
     818
     819        if (twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args,
     820                     td->timeline_id ? 6 : 4) == NULL) {
     821                if (++td->http_fails >= 5)
     822                        imcb_error(ic, "Could not retrieve %s: %s",
     823                                   TWITTER_HOME_TIMELINE_URL, "connection failed");
     824                td->flags |= TWITTER_GOT_TIMELINE;
     825                twitter_flush_timeline(ic);
     826        }
     827
     828        g_free(args[1]);
     829        if (td->timeline_id) {
     830                g_free(args[5]);
     831        }
     832}
     833
     834/**
     835 * Get mentions.
     836 */
     837void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor)
     838{
     839        struct twitter_data *td = ic->proto_data;
     840
     841        td->mentions_obj = NULL;
     842        td->flags &= ~TWITTER_GOT_MENTIONS;
     843
     844        char *args[6];
     845        args[0] = "cursor";
     846        args[1] = g_strdup_printf("%lld", (long long) next_cursor);
     847        args[2] = "include_entities";
     848        args[3] = "true";
     849        if (td->timeline_id) {
     850                args[4] = "since_id";
     851                args[5] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id);
     852        }
     853
     854        if (twitter_http(ic, TWITTER_MENTIONS_URL, twitter_http_get_mentions, ic, 0, args,
     855                     td->timeline_id ? 6 : 4) == NULL) {
     856                if (++td->http_fails >= 5)
     857                        imcb_error(ic, "Could not retrieve %s: %s",
     858                                   TWITTER_MENTIONS_URL, "connection failed");
     859                td->flags |= TWITTER_GOT_MENTIONS;
     860                twitter_flush_timeline(ic);
     861        }
     862
     863        g_free(args[1]);
     864        if (td->timeline_id) {
     865                g_free(args[5]);
    688866        }
    689867}
     
    713891                imcb_error(ic, "Authentication failure");
    714892                imc_logout(ic, FALSE);
    715                 return;
     893                goto end;
    716894        } else {
    717895                // It didn't go well, output the error and return.
     
    720898                                   TWITTER_HOME_TIMELINE_URL, twitter_parse_error(req));
    721899
    722                 return;
     900                goto end;
    723901        }
    724902
     
    733911        xt_free(parser);
    734912
    735         // See if the user wants to see the messages in a groupchat window or as private messages.
    736         if (txl->list == NULL);
    737         else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
    738                 twitter_groupchat(ic, txl->list);
    739         else
    740                 twitter_private_message_chat(ic, txl->list);
    741 
    742         // Free the structure. 
    743         txl_free(txl);
     913        td->home_timeline_obj = txl;
     914
     915      end:
     916        td->flags |= TWITTER_GOT_TIMELINE;
     917
     918        twitter_flush_timeline(ic);
     919}
     920
     921/**
     922 * Callback for getting mentions.
     923 */
     924static void twitter_http_get_mentions(struct http_request *req)
     925{
     926        struct im_connection *ic = req->data;
     927        struct twitter_data *td;
     928        struct xt_parser *parser;
     929        struct twitter_xml_list *txl;
     930
     931        // Check if the connection is still active.
     932        if (!g_slist_find(twitter_connections, ic))
     933                return;
     934
     935        td = ic->proto_data;
     936
     937        // Check if the HTTP request went well.
     938        if (req->status_code == 200) {
     939                td->http_fails = 0;
     940                if (!(ic->flags & OPT_LOGGED_IN))
     941                        imcb_connected(ic);
     942        } else if (req->status_code == 401) {
     943                imcb_error(ic, "Authentication failure");
     944                imc_logout(ic, FALSE);
     945                goto end;
     946        } else {
     947                // It didn't go well, output the error and return.
     948                if (++td->http_fails >= 5)
     949                        imcb_error(ic, "Could not retrieve %s: %s",
     950                                   TWITTER_MENTIONS_URL, twitter_parse_error(req));
     951
     952                goto end;
     953        }
     954
     955        txl = g_new0(struct twitter_xml_list, 1);
     956        txl->list = NULL;
     957
     958        // Parse the data.
     959        parser = xt_new(NULL, txl);
     960        xt_feed(parser, req->reply_body, req->body_size);
     961        // The root <statuses> node should hold the list of statuses <status>
     962        twitter_xt_get_status_list(ic, parser->root, txl);
     963        xt_free(parser);
     964
     965        td->mentions_obj = txl;
     966
     967      end:
     968        td->flags |= TWITTER_GOT_MENTIONS;
     969
     970        twitter_flush_timeline(ic);
    744971}
    745972
Note: See TracChangeset for help on using the changeset viewer.