From 3b4a22a8ca8a88122fa5fbf51218a5e6e65a90c8 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 4 Nov 2012 23:23:07 +0100 Subject: s/.xml/.json/. Good luck getting that working again. --- protocols/twitter/twitter_lib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 4a09cbb1..f0a6bbb1 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -1031,7 +1031,7 @@ void twitter_status_destroy(struct im_connection *ic, guint64 id) { char *url; url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_DESTROY_URL, - (unsigned long long) id, ".xml"); + (unsigned long long) id, ".json"); twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); g_free(url); } @@ -1040,7 +1040,7 @@ void twitter_status_retweet(struct im_connection *ic, guint64 id) { char *url; url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_RETWEET_URL, - (unsigned long long) id, ".xml"); + (unsigned long long) id, ".json"); twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); g_free(url); } @@ -1066,7 +1066,7 @@ void twitter_favourite_tweet(struct im_connection *ic, guint64 id) { char *url; url = g_strdup_printf("%s%llu%s", TWITTER_FAVORITE_CREATE_URL, - (unsigned long long) id, ".xml"); + (unsigned long long) id, ".json"); twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); g_free(url); } -- cgit v1.2.3 From e08ae0cff03ff880344cb3dc75cc90576cc314b3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 5 Nov 2012 00:39:48 +0100 Subject: Can log in now. Fails to fetch timelines though, which is going to be the more annoying part anyway. Plus, I want streaming API stuff instead. --- protocols/twitter/twitter_lib.c | 114 +++++++++++++++------------------------- 1 file changed, 42 insertions(+), 72 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index f0a6bbb1..dad97495 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -36,6 +36,7 @@ #include "base64.h" #include "xmltree.h" #include "twitter_lib.h" +#include "json_util.h" #include #include @@ -187,12 +188,12 @@ char *twitter_parse_error(struct http_request *req) return ret ? ret : req->status_string; } -static struct xt_node *twitter_parse_response(struct im_connection *ic, struct http_request *req) +static json_value *twitter_parse_response(struct im_connection *ic, struct http_request *req) { gboolean logging_in = !(ic->flags & OPT_LOGGED_IN); gboolean periodic; struct twitter_data *td = ic->proto_data; - struct xt_node *ret; + json_value *ret; char path[64] = "", *s; if ((s = strchr(req->request, ' '))) { @@ -226,7 +227,7 @@ static struct xt_node *twitter_parse_response(struct im_connection *ic, struct h td->http_fails = 0; } - if ((ret = xt_from_string(req->reply_body, req->body_size)) == NULL) { + if ((ret = json_parse(req->reply_body)) == NULL) { imcb_error(ic, "Could not retrieve %s: %s", path, "XML parse error"); } @@ -249,45 +250,33 @@ void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor) g_free(args[1]); } -/** - * Function to help fill a list. - */ -static xt_status twitter_xt_next_cursor(struct xt_node *node, struct twitter_xml_list *txl) -{ - char *end = NULL; - - if (node->text) - txl->next_cursor = g_ascii_strtoll(node->text, &end, 10); - if (end == NULL) - txl->next_cursor = -1; - - return XT_HANDLED; -} - /** * Fill a list of ids. */ -static xt_status twitter_xt_get_friends_id_list(struct xt_node *node, struct twitter_xml_list *txl) +static xt_status twitter_xt_get_friends_id_list(json_value *node, struct twitter_xml_list *txl) { - struct xt_node *child; + json_value *c; + int i; // Set the list type. txl->type = TXL_ID; - // The root node should hold the list of statuses - // Walk over the nodes children. - for (child = node->children; child; child = child->next) { - if (g_strcasecmp("ids", child->name) == 0) { - struct xt_node *idc; - for (idc = child->children; idc; idc = idc->next) - if (g_strcasecmp(idc->name, "id") == 0) - txl->list = g_slist_prepend(txl->list, - g_memdup(idc->text, idc->text_len + 1)); - } else if (g_strcasecmp("next_cursor", child->name) == 0) { - twitter_xt_next_cursor(child, txl); - } - } + c = json_o_get(node, "ids"); + if (!c || c->type != json_array) + return XT_ABORT; + for (i = 0; i < c->u.array.length; i ++) { + txl->list = g_slist_prepend(txl->list, + g_strdup_printf("%ld", c->u.array.values[i]->u.integer)); + + } + + c = json_o_get(node, "next_cursor"); + if (c && c->type == json_integer) + txl->next_cursor = c->u.integer; + else + txl->next_cursor = -1; + return XT_HANDLED; } @@ -299,7 +288,7 @@ static void twitter_get_users_lookup(struct im_connection *ic); static void twitter_http_get_friends_ids(struct http_request *req) { struct im_connection *ic; - struct xt_node *parsed; + json_value *parsed; struct twitter_xml_list *txl; struct twitter_data *td; @@ -321,8 +310,9 @@ static void twitter_http_get_friends_ids(struct http_request *req) // Parse the data. if (!(parsed = twitter_parse_response(ic, req))) return; + twitter_xt_get_friends_id_list(parsed, txl); - xt_free_node(parsed); + json_value_free(parsed); td->follow_ids = txl->list; if (txl->next_cursor) @@ -337,7 +327,7 @@ static void twitter_http_get_friends_ids(struct http_request *req) txl_free(txl); } -static xt_status twitter_xt_get_users(struct xt_node *node, struct twitter_xml_list *txl); +static xt_status twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl); static void twitter_http_get_users_lookup(struct http_request *req); static void twitter_get_users_lookup(struct im_connection *ic) @@ -378,7 +368,7 @@ static void twitter_get_users_lookup(struct im_connection *ic) static void twitter_http_get_users_lookup(struct http_request *req) { struct im_connection *ic = req->data; - struct xt_node *parsed; + json_value *parsed; struct twitter_xml_list *txl; GSList *l = NULL; struct twitter_xml_user *user; @@ -394,7 +384,7 @@ static void twitter_http_get_users_lookup(struct http_request *req) if (!(parsed = twitter_parse_response(ic, req))) return; twitter_xt_get_users(parsed, txl); - xt_free_node(parsed); + json_value_free(parsed); // Add the users as buddies. for (l = txl->list; l; l = g_slist_next(l)) { @@ -408,49 +398,29 @@ static void twitter_http_get_users_lookup(struct http_request *req) twitter_get_users_lookup(ic); } -/** - * Function to fill a twitter_xml_user struct. - * It sets: - * - the name and - * - the screen_name. - */ -static xt_status twitter_xt_get_user(struct xt_node *node, struct twitter_xml_user *txu) -{ - struct xt_node *child; - - // Walk over the nodes children. - for (child = node->children; child; child = child->next) { - if (g_strcasecmp("name", child->name) == 0) { - txu->name = g_memdup(child->text, child->text_len + 1); - } else if (g_strcasecmp("screen_name", child->name) == 0) { - txu->screen_name = g_memdup(child->text, child->text_len + 1); - } - } - return XT_HANDLED; -} - /** * Function to fill a twitter_xml_list struct. * It sets: * - all s from the element. */ -static xt_status twitter_xt_get_users(struct xt_node *node, struct twitter_xml_list *txl) +static xt_status twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl) { struct twitter_xml_user *txu; - struct xt_node *child; + int i; // Set the type of the list. txl->type = TXL_USER; + if (!node || node->type != json_array) + return XT_ABORT; + // The root node should hold the list of users // Walk over the nodes children. - for (child = node->children; child; child = child->next) { - if (g_strcasecmp("user", child->name) == 0) { - txu = g_new0(struct twitter_xml_user, 1); - twitter_xt_get_user(child, txu); - // Put the item in the front of the list. - txl->list = g_slist_prepend(txl->list, txu); - } + for (i = 0; i < node->u.array.length; i ++) { + txu = g_new0(struct twitter_xml_user, 1); + txu->name = g_strdup(json_o_str(node->u.array.values[i], "name")); + txu->screen_name = g_strdup(json_o_str(node->u.array.values[i], "screen_name")); + txl->list = g_slist_prepend(txl->list, txu); } return XT_HANDLED; @@ -490,7 +460,7 @@ static xt_status twitter_xt_get_status(struct xt_node *node, struct twitter_xml_ txs->created_at = mktime_utc(&parsed); } else if (g_strcasecmp("user", child->name) == 0) { txs->user = g_new0(struct twitter_xml_user, 1); - twitter_xt_get_user(child, txs->user); +// twitter_xt_get_user(child, txs->user); } else if (g_strcasecmp("id", child->name) == 0) { txs->id = g_ascii_strtoull(child->text, NULL, 10); } else if (g_strcasecmp("in_reply_to_status_id", child->name) == 0) { @@ -578,7 +548,7 @@ static xt_status twitter_xt_get_status_list(struct im_connection *ic, struct xt_ } } } else if (g_strcasecmp("next_cursor", child->name) == 0) { - twitter_xt_next_cursor(child, txl); +// twitter_xt_next_cursor(child, txl); } } @@ -920,7 +890,7 @@ static void twitter_http_get_home_timeline(struct http_request *req) if (!(parsed = twitter_parse_response(ic, req))) goto end; twitter_xt_get_status_list(ic, parsed, txl); - xt_free_node(parsed); +// json_value_free(parsed); td->home_timeline_obj = txl; @@ -953,7 +923,7 @@ static void twitter_http_get_mentions(struct http_request *req) if (!(parsed = twitter_parse_response(ic, req))) goto end; twitter_xt_get_status_list(ic, parsed, txl); - xt_free_node(parsed); +// json_value_free(parsed); td->mentions_obj = txl; -- cgit v1.2.3 From 0688e99d12297b6dda980803d1f1a54bf8590299 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 5 Nov 2012 09:46:06 +0100 Subject: Type safety check. --- protocols/twitter/twitter_lib.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index dad97495..a4ce28e7 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -188,6 +188,7 @@ char *twitter_parse_error(struct http_request *req) return ret ? ret : req->status_string; } +/* TODO: NULL means failure, but not always that the connection is dead. This sucks for callers. */ static json_value *twitter_parse_response(struct im_connection *ic, struct http_request *req) { gboolean logging_in = !(ic->flags & OPT_LOGGED_IN); @@ -266,6 +267,9 @@ static xt_status twitter_xt_get_friends_id_list(json_value *node, struct twitter return XT_ABORT; for (i = 0; i < c->u.array.length; i ++) { + if (c->u.array.values[i]->type != json_integer) + continue; + txl->list = g_slist_prepend(txl->list, g_strdup_printf("%ld", c->u.array.values[i]->u.integer)); -- cgit v1.2.3 From 8e3b7acdfc96e086f1cd1c606e46d9a91d46a842 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 8 Nov 2012 22:38:20 +0000 Subject: It logs in and fetches statuses! \o/ But, some corruption.. --- protocols/twitter/twitter_lib.c | 135 ++++++++++++++++++++++------------------ 1 file changed, 76 insertions(+), 59 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index a4ce28e7..47264db0 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -271,7 +271,7 @@ static xt_status twitter_xt_get_friends_id_list(json_value *node, struct twitter continue; txl->list = g_slist_prepend(txl->list, - g_strdup_printf("%ld", c->u.array.values[i]->u.integer)); + g_strdup_printf("%lld", c->u.array.values[i]->u.integer)); } @@ -402,6 +402,17 @@ static void twitter_http_get_users_lookup(struct http_request *req) twitter_get_users_lookup(ic); } +struct twitter_xml_user *twitter_xt_get_user(const json_value *node) +{ + struct twitter_xml_user *txu; + + txu = g_new0(struct twitter_xml_user, 1); + txu->name = g_strdup(json_o_str(node, "name")); + txu->screen_name = g_strdup(json_o_str(node, "screen_name")); + + return txu; +} + /** * Function to fill a twitter_xml_list struct. * It sets: @@ -421,10 +432,9 @@ static xt_status twitter_xt_get_users(json_value *node, struct twitter_xml_list // The root node should hold the list of users // Walk over the nodes children. for (i = 0; i < node->u.array.length; i ++) { - txu = g_new0(struct twitter_xml_user, 1); - txu->name = g_strdup(json_o_str(node->u.array.values[i], "name")); - txu->screen_name = g_strdup(json_o_str(node->u.array.values[i], "screen_name")); - txl->list = g_slist_prepend(txl->list, txu); + txu = twitter_xt_get_user(node->u.array.values[i]); + if (txu) + txl->list = g_slist_prepend(txl->list, txu); } return XT_HANDLED; @@ -444,31 +454,38 @@ static xt_status twitter_xt_get_users(json_value *node, struct twitter_xml_list * - the status id and * - the user in a twitter_xml_user struct. */ -static xt_status twitter_xt_get_status(struct xt_node *node, struct twitter_xml_status *txs) +static xt_status twitter_xt_get_status(const json_value *node, struct twitter_xml_status *txs) { - struct xt_node *child, *rt = NULL; + const json_value *c, *rt = NULL, *entities = NULL; + int i; + + if (node->type != json_object) + return XT_ABORT; - // Walk over the nodes children. - for (child = node->children; child; child = child->next) { - if (g_strcasecmp("text", child->name) == 0) { - txs->text = g_memdup(child->text, child->text_len + 1); - } else if (g_strcasecmp("retweeted_status", child->name) == 0) { - rt = child; - } else if (g_strcasecmp("created_at", child->name) == 0) { + for (i = 0; i < node->u.object.length; i ++) { + const char *k = node->u.object.values[i].name; + const json_value *v = node->u.object.values[i].value; + + if (strcmp("text", k) == 0 && v->type == json_string) { + txs->text = g_memdup(v->u.string.ptr, v->u.string.length); + } else if (strcmp("retweeted_status", k) == 0 && v->type == json_object) { + rt = v; + } else if (strcmp("created_at", k) == 0 && v->type == json_string) { struct tm parsed; /* Very sensitive to changes to the formatting of this field. :-( Also assumes the timezone used is UTC since C time handling functions suck. */ - if (strptime(child->text, TWITTER_TIME_FORMAT, &parsed) != NULL) + if (strptime(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) txs->created_at = mktime_utc(&parsed); - } else if (g_strcasecmp("user", child->name) == 0) { - txs->user = g_new0(struct twitter_xml_user, 1); -// twitter_xt_get_user(child, txs->user); - } else if (g_strcasecmp("id", child->name) == 0) { - txs->id = g_ascii_strtoull(child->text, NULL, 10); - } else if (g_strcasecmp("in_reply_to_status_id", child->name) == 0) { - txs->reply_to = g_ascii_strtoull(child->text, NULL, 10); + } else if (strcmp("user", k) == 0 && v->type == json_object) { + txs->user = twitter_xt_get_user(v); + } else if (strcmp("id", k) == 0 && v->type == json_integer) { + txs->id = v->u.integer; + } else if (strcmp("in_reply_to_status_id", k) == 0 && v->type == json_integer) { + txs->reply_to = v->u.integer; + } else if (strcmp("entities", k) == 0 && v->type == json_object) { + txs->reply_to = v->u.integer; } } @@ -484,29 +501,29 @@ static xt_status twitter_xt_get_status(struct xt_node *node, struct twitter_xml_ g_free(txs->text); txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text); txs_free(rtxs); - } else { - struct xt_node *urls, *url; - - urls = xt_find_path(node, "entities"); - if (urls != NULL) - urls = urls->children; - for (; urls; urls = urls->next) { - if (strcmp(urls->name, "urls") != 0 && strcmp(urls->name, "media") != 0) + } else if (entities && NULL) { + JSON_O_FOREACH (entities, k, v) { + int i; + + if (v->type != json_array) + continue; + if (strcmp(k, "urls") != 0 && strcmp(k, "media") != 0) continue; - for (url = urls ? urls->children : NULL; url; url = url->next) { - /* "short" is a reserved word. :-P */ - struct xt_node *kort = xt_find_node(url->children, "url"); - struct xt_node *disp = xt_find_node(url->children, "display_url"); + for (i = 0; i < v->u.array.length; i ++) { + if (v->u.array.values[i]->type != json_object) + continue; + + const char *kort = json_o_str(v->u.array.values[i], "url"); + const char *disp = json_o_str(v->u.array.values[i], "display_url"); char *pos, *new; - if (!kort || !kort->text || !disp || !disp->text || - !(pos = strstr(txs->text, kort->text))) + if (!kort || !disp || !(pos = strstr(txs->text, kort))) continue; *pos = '\0'; - new = g_strdup_printf("%s%s <%s>%s", txs->text, kort->text, - disp->text, pos + strlen(kort->text)); + new = g_strdup_printf("%s%s <%s>%s", txs->text, kort, + disp, pos + strlen(kort)); g_free(txs->text); txs->text = new; @@ -523,36 +540,36 @@ static xt_status twitter_xt_get_status(struct xt_node *node, struct twitter_xml_ * - all es within the element and * - the next_cursor. */ -static xt_status twitter_xt_get_status_list(struct im_connection *ic, struct xt_node *node, +static xt_status twitter_xt_get_status_list(struct im_connection *ic, const json_value *node, struct twitter_xml_list *txl) { struct twitter_xml_status *txs; - struct xt_node *child; + json_value *c; bee_user_t *bu; + int i; // Set the type of the list. txl->type = TXL_STATUS; + + if (node->type != json_array) + return XT_ABORT; // The root node should hold the list of statuses // Walk over the nodes children. - for (child = node->children; child; child = child->next) { - if (g_strcasecmp("status", child->name) == 0) { - txs = g_new0(struct twitter_xml_status, 1); - twitter_xt_get_status(child, txs); - // Put the item in the front of the list. - txl->list = g_slist_prepend(txl->list, txs); - - if (txs->user && txs->user->screen_name && - (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) { - struct twitter_user_data *tud = bu->data; - - if (txs->id > tud->last_id) { - tud->last_id = txs->id; - tud->last_time = txs->created_at; - } + for (i = 0; i < node->u.array.length; i ++) { + txs = g_new0(struct twitter_xml_status, 1); + twitter_xt_get_status(node->u.array.values[i], txs); + // Put the item in the front of the list. + txl->list = g_slist_prepend(txl->list, txs); + + if (txs->user && txs->user->screen_name && + (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) { + struct twitter_user_data *tud = bu->data; + + if (txs->id > tud->last_id) { + tud->last_id = txs->id; + tud->last_time = txs->created_at; } - } else if (g_strcasecmp("next_cursor", child->name) == 0) { -// twitter_xt_next_cursor(child, txl); } } @@ -878,7 +895,7 @@ static void twitter_http_get_home_timeline(struct http_request *req) { struct im_connection *ic = req->data; struct twitter_data *td; - struct xt_node *parsed; + json_value *parsed; struct twitter_xml_list *txl; // Check if the connection is still active. @@ -911,7 +928,7 @@ static void twitter_http_get_mentions(struct http_request *req) { struct im_connection *ic = req->data; struct twitter_data *td; - struct xt_node *parsed; + json_value *parsed; struct twitter_xml_list *txl; // Check if the connection is still active. -- cgit v1.2.3 From fb351ce6b6cdc714019f49f693182e32055fd78b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 9 Nov 2012 00:07:23 +0000 Subject: Getting better. Corruption fixed, fetching of mentions fixed, error handling "fixed". --- protocols/twitter/twitter_lib.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 47264db0..6231cf69 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -173,6 +173,8 @@ char *twitter_parse_error(struct http_request *req) g_free(ret); ret = NULL; + /* EX: + {"errors":[{"message":"Rate limit exceeded","code":88}]} */ if (req->body_size > 0) { root = xt_from_string(req->reply_body, req->body_size); @@ -188,7 +190,8 @@ char *twitter_parse_error(struct http_request *req) return ret ? ret : req->status_string; } -/* TODO: NULL means failure, but not always that the connection is dead. This sucks for callers. */ +/* WATCH OUT: This function might or might not destroy your connection. + Sub-optimal indeed, but just be careful when this returns NULL! */ static json_value *twitter_parse_response(struct im_connection *ic, struct http_request *req) { gboolean logging_in = !(ic->flags & OPT_LOGGED_IN); @@ -456,7 +459,7 @@ static xt_status twitter_xt_get_users(json_value *node, struct twitter_xml_list */ static xt_status twitter_xt_get_status(const json_value *node, struct twitter_xml_status *txs) { - const json_value *c, *rt = NULL, *entities = NULL; + const json_value *rt = NULL, *entities = NULL; int i; if (node->type != json_object) @@ -467,7 +470,7 @@ static xt_status twitter_xt_get_status(const json_value *node, struct twitter_xm const json_value *v = node->u.object.values[i].value; if (strcmp("text", k) == 0 && v->type == json_string) { - txs->text = g_memdup(v->u.string.ptr, v->u.string.length); + txs->text = g_memdup(v->u.string.ptr, v->u.string.length + 1); } else if (strcmp("retweeted_status", k) == 0 && v->type == json_object) { rt = v; } else if (strcmp("created_at", k) == 0 && v->type == json_string) { @@ -485,7 +488,7 @@ static xt_status twitter_xt_get_status(const json_value *node, struct twitter_xm } else if (strcmp("in_reply_to_status_id", k) == 0 && v->type == json_integer) { txs->reply_to = v->u.integer; } else if (strcmp("entities", k) == 0 && v->type == json_object) { - txs->reply_to = v->u.integer; + entities = v; } } @@ -501,7 +504,7 @@ static xt_status twitter_xt_get_status(const json_value *node, struct twitter_xm g_free(txs->text); txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text); txs_free(rtxs); - } else if (entities && NULL) { + } else if (entities) { JSON_O_FOREACH (entities, k, v) { int i; @@ -544,7 +547,6 @@ static xt_status twitter_xt_get_status_list(struct im_connection *ic, const json struct twitter_xml_list *txl) { struct twitter_xml_status *txs; - json_value *c; bee_user_t *bu; int i; @@ -916,6 +918,9 @@ static void twitter_http_get_home_timeline(struct http_request *req) td->home_timeline_obj = txl; end: + if (!g_slist_find(twitter_connections, ic)) + return; + td->flags |= TWITTER_GOT_TIMELINE; twitter_flush_timeline(ic); @@ -949,6 +954,9 @@ static void twitter_http_get_mentions(struct http_request *req) td->mentions_obj = txl; end: + if (!g_slist_find(twitter_connections, ic)) + return; + td->flags |= TWITTER_GOT_MENTIONS; twitter_flush_timeline(ic); -- cgit v1.2.3 From 5246133a607561abe8096c471de02bddeb6be67c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 9 Nov 2012 00:23:44 +0000 Subject: Updated error response parsing. Also, use this for 401 responses so for example the auth errors caused by NTP desync are clearer: twitter - Login error: Authentication failure (401 Unauthorized (Timestamp out of bounds)) --- protocols/twitter/twitter_lib.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 6231cf69..c72c317b 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -168,23 +168,21 @@ static void twitter_add_buddy(struct im_connection *ic, char *name, const char * char *twitter_parse_error(struct http_request *req) { static char *ret = NULL; - struct xt_node *root, *node, *err; + json_value *root, *err; g_free(ret); ret = NULL; - /* EX: - {"errors":[{"message":"Rate limit exceeded","code":88}]} */ if (req->body_size > 0) { - root = xt_from_string(req->reply_body, req->body_size); - - for (node = root; node; node = node->next) - if ((err = xt_find_node(node->children, "error")) && err->text_len > 0) { - ret = g_strdup_printf("%s (%s)", req->status_string, err->text); - break; - } - - xt_free_node(root); + root = json_parse(req->reply_body); + err = json_o_get(root, "errors"); + if (err->type == json_array && (err = err->u.array.values[0]) && + err->type == json_object) { + const char *msg = json_o_str(err, "message"); + if (msg) + ret = g_strdup_printf("%s (%s)", req->status_string, msg); + } + json_value_free(root); } return ret ? ret : req->status_string; @@ -215,7 +213,8 @@ static json_value *twitter_parse_response(struct im_connection *ic, struct http_ /* IIRC Twitter once had an outage where they were randomly throwing 401s so I'll keep treating this one as fatal only during login. */ - imcb_error(ic, "Authentication failure"); + imcb_error(ic, "Authentication failure (%s)", + twitter_parse_error(req)); imc_logout(ic, FALSE); return NULL; } else if (req->status_code != 200) { -- cgit v1.2.3 From 24132ec9af8b275fb4b94e347c6ccce0017c27d0 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 9 Nov 2012 23:53:45 +0000 Subject: Fixed the last parser (generic handler which just remembers status IDs). xmltree.h include can almost be removed, it's just used for return values now. --- protocols/twitter/twitter_lib.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index c72c317b..cfa2cf06 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -969,7 +969,7 @@ static void twitter_http_post(struct http_request *req) { struct im_connection *ic = req->data; struct twitter_data *td; - struct xt_node *parsed, *node; + json_value *parsed, *id; // Check if the connection is still active. if (!g_slist_find(twitter_connections, ic)) @@ -981,9 +981,8 @@ static void twitter_http_post(struct http_request *req) if (!(parsed = twitter_parse_response(ic, req))) return; - if ((node = xt_find_node(parsed, "status")) && - (node = xt_find_node(node->children, "id")) && node->text) - td->last_status_id = g_ascii_strtoull(node->text, NULL, 10); + if ((id = json_o_get(parsed, "id")) && id->type == json_integer) + td->last_status_id = id->u.integer; } /** -- cgit v1.2.3 From 9e8c945b0c0e794fdc33413ad87feb4606a07981 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 10 Nov 2012 12:31:33 +0000 Subject: Removed xmltree dependency entirely. --- protocols/twitter/twitter_lib.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index cfa2cf06..654cf467 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -34,7 +34,6 @@ #include "url.h" #include "misc.h" #include "base64.h" -#include "xmltree.h" #include "twitter_lib.h" #include "json_util.h" #include @@ -256,7 +255,7 @@ void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor) /** * Fill a list of ids. */ -static xt_status twitter_xt_get_friends_id_list(json_value *node, struct twitter_xml_list *txl) +static gboolean twitter_xt_get_friends_id_list(json_value *node, struct twitter_xml_list *txl) { json_value *c; int i; @@ -266,7 +265,7 @@ static xt_status twitter_xt_get_friends_id_list(json_value *node, struct twitter c = json_o_get(node, "ids"); if (!c || c->type != json_array) - return XT_ABORT; + return FALSE; for (i = 0; i < c->u.array.length; i ++) { if (c->u.array.values[i]->type != json_integer) @@ -283,7 +282,7 @@ static xt_status twitter_xt_get_friends_id_list(json_value *node, struct twitter else txl->next_cursor = -1; - return XT_HANDLED; + return TRUE; } static void twitter_get_users_lookup(struct im_connection *ic); @@ -333,7 +332,7 @@ static void twitter_http_get_friends_ids(struct http_request *req) txl_free(txl); } -static xt_status twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl); +static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl); static void twitter_http_get_users_lookup(struct http_request *req); static void twitter_get_users_lookup(struct im_connection *ic) @@ -420,7 +419,7 @@ struct twitter_xml_user *twitter_xt_get_user(const json_value *node) * It sets: * - all s from the element. */ -static xt_status twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl) +static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl) { struct twitter_xml_user *txu; int i; @@ -429,7 +428,7 @@ static xt_status twitter_xt_get_users(json_value *node, struct twitter_xml_list txl->type = TXL_USER; if (!node || node->type != json_array) - return XT_ABORT; + return FALSE; // The root node should hold the list of users // Walk over the nodes children. @@ -439,7 +438,7 @@ static xt_status twitter_xt_get_users(json_value *node, struct twitter_xml_list txl->list = g_slist_prepend(txl->list, txu); } - return XT_HANDLED; + return TRUE; } #ifdef __GLIBC__ @@ -456,18 +455,14 @@ static xt_status twitter_xt_get_users(json_value *node, struct twitter_xml_list * - the status id and * - the user in a twitter_xml_user struct. */ -static xt_status twitter_xt_get_status(const json_value *node, struct twitter_xml_status *txs) +static gboolean twitter_xt_get_status(const json_value *node, struct twitter_xml_status *txs) { const json_value *rt = NULL, *entities = NULL; - int i; if (node->type != json_object) - return XT_ABORT; + return FALSE; - for (i = 0; i < node->u.object.length; i ++) { - const char *k = node->u.object.values[i].name; - const json_value *v = node->u.object.values[i].value; - + JSON_O_FOREACH (node, k, v) { if (strcmp("text", k) == 0 && v->type == json_string) { txs->text = g_memdup(v->u.string.ptr, v->u.string.length + 1); } else if (strcmp("retweeted_status", k) == 0 && v->type == json_object) { @@ -495,9 +490,9 @@ static xt_status twitter_xt_get_status(const json_value *node, struct twitter_xm wasn't truncated because it may be lying. */ if (rt) { struct twitter_xml_status *rtxs = g_new0(struct twitter_xml_status, 1); - if (twitter_xt_get_status(rt, rtxs) != XT_HANDLED) { + if (!twitter_xt_get_status(rt, rtxs)) { txs_free(rtxs); - return XT_HANDLED; + return TRUE; } g_free(txs->text); @@ -533,7 +528,7 @@ static xt_status twitter_xt_get_status(const json_value *node, struct twitter_xm } } - return XT_HANDLED; + return TRUE; } /** @@ -542,8 +537,8 @@ static xt_status twitter_xt_get_status(const json_value *node, struct twitter_xm * - all es within the element and * - the next_cursor. */ -static xt_status twitter_xt_get_status_list(struct im_connection *ic, const json_value *node, - struct twitter_xml_list *txl) +static gboolean twitter_xt_get_status_list(struct im_connection *ic, const json_value *node, + struct twitter_xml_list *txl) { struct twitter_xml_status *txs; bee_user_t *bu; @@ -553,7 +548,7 @@ static xt_status twitter_xt_get_status_list(struct im_connection *ic, const json txl->type = TXL_STATUS; if (node->type != json_array) - return XT_ABORT; + return FALSE; // The root node should hold the list of statuses // Walk over the nodes children. @@ -574,7 +569,7 @@ static xt_status twitter_xt_get_status_list(struct im_connection *ic, const json } } - return XT_HANDLED; + return TRUE; } static char *twitter_msg_add_id(struct im_connection *ic, -- cgit v1.2.3 From ddc2de54664ec25b95bbce997fbbb6a7104f1203 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 10 Nov 2012 23:52:21 +0000 Subject: Very immature code for reading from the streaming API. It reads from a fixed URL and tried to parse individual JSON objects. Not doing anything useful with it. --- protocols/twitter/twitter_lib.c | 68 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 654cf467..8d10a406 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -726,8 +726,48 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList * list } } -static void twitter_http_get_home_timeline(struct http_request *req); -static void twitter_http_get_mentions(struct http_request *req); +static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor); +static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor); + +static void twitter_http_stream(struct http_request *req) +{ + int len; + int i; + char c; + json_value *j; + + printf( "%d bytes in stream\n", req->body_size ); + + /* m/^[\d\s]*/ /* why is there a second commend here? :-) */ + for (i = 0; i < req->body_size; i ++) { + if (!isspace(req->reply_body[i]) && !isdigit(req->reply_body[i])) + break; + } + + /* Nothing but numbers and whitespace in there. Try again later. */ + if (i == req->body_size) + return; + + /* Get length. */ + if (sscanf(req->reply_body, "%d", &len) != 1) + return; + + if (req->body_size < i + len) { + printf("Not enough bytes in buffer yet\n"); + return; + } + + http_flush_bytes(req, i); + c = req->reply_body[len]; + req->reply_body[len] = '\0'; + + printf("JSON: %s\n", req->reply_body); + printf("parsed: %p\n", (j = json_parse(req->reply_body))); + json_value_free(j); + req->reply_body[len] = c; + + http_flush_bytes(req, len); +} /** * Get the timeline with optionally mentions @@ -751,6 +791,23 @@ void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor) if (include_mentions) { twitter_get_mentions(ic, next_cursor); } + + static int bla = 0; + + if (bla) + return; + bla = 1; + + char *args[4]; + args[0] = "with"; + args[1] = "followings"; + args[2] = "delimited"; + args[3] = "length"; + + if ((td->stream = twitter_http(ic, "https://userstream.twitter.com/1.1/user.json", + twitter_http_stream, ic, 0, args, 4))) { + td->stream->flags |= HTTPC_STREAMING; + } } /** @@ -809,10 +866,13 @@ void twitter_flush_timeline(struct im_connection *ic) td->home_timeline_obj = td->mentions_obj = NULL; } +static void twitter_http_get_home_timeline(struct http_request *req); +static void twitter_http_get_mentions(struct http_request *req); + /** * Get the timeline. */ -void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor) +static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor) { struct twitter_data *td = ic->proto_data; @@ -848,7 +908,7 @@ void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor) /** * Get mentions. */ -void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor) +static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor) { struct twitter_data *td = ic->proto_data; -- cgit v1.2.3 From dff0e0bdf3acea7d301242421b4fa906ff46278d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Nov 2012 14:42:20 +0000 Subject: Showing tweets now, and leaking less memory. Still lots of cleanup left to do. --- protocols/twitter/twitter_lib.c | 80 +++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 26 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 8d10a406..031d824e 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -175,7 +175,7 @@ char *twitter_parse_error(struct http_request *req) if (req->body_size > 0) { root = json_parse(req->reply_body); err = json_o_get(root, "errors"); - if (err->type == json_array && (err = err->u.array.values[0]) && + if (err && err->type == json_array && (err = err->u.array.values[0]) && err->type == json_object) { const char *msg = json_o_str(err, "message"); if (msg) @@ -528,7 +528,7 @@ static gboolean twitter_xt_get_status(const json_value *node, struct twitter_xml } } - return TRUE; + return txs->text && txs->user && txs->id; } /** @@ -726,15 +726,19 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList * list } } -static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor); -static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor); +static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o); static void twitter_http_stream(struct http_request *req) { - int len; - int i; + struct im_connection *ic = req->data; + //struct twitter_data *td = ic->proto_data; + json_value *parsed; + int len, i; char c; - json_value *j; + + if (!g_slist_find(twitter_connections, ic)) + return; + //td = ic->proto_data;; printf( "%d bytes in stream\n", req->body_size ); @@ -762,13 +766,54 @@ static void twitter_http_stream(struct http_request *req) req->reply_body[len] = '\0'; printf("JSON: %s\n", req->reply_body); - printf("parsed: %p\n", (j = json_parse(req->reply_body))); - json_value_free(j); + printf("parsed: %p\n", (parsed = json_parse(req->reply_body))); + if (parsed) { + twitter_stream_handle_object(ic, parsed); + } + json_value_free(parsed); req->reply_body[len] = c; http_flush_bytes(req, len); + + /* One notification might bring multiple events! */ + if (req->body_size > 0) + twitter_http_stream(req); +} + +static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o) +{ + struct twitter_xml_status *txs = g_new0(struct twitter_xml_status, 1); + + if (twitter_xt_get_status(o, txs)) { + GSList *output = g_slist_append(NULL, txs); + twitter_groupchat(ic, output); + txs_free(txs); + g_slist_free(output); + return TRUE; + } + txs_free(txs); + return FALSE; } +gboolean twitter_open_stream(struct im_connection *ic) +{ + struct twitter_data *td = ic->proto_data; + char *args[4] = {"with", "followings", "delimited", "length"}; + + if ((td->stream = twitter_http(ic, TWITTER_USER_STREAM_URL, + twitter_http_stream, ic, 0, args, 4))) { + /* This flag must be enabled or we'll get no data until EOF + (which err, kind of, defeats the purpose of a streaming API). */ + td->stream->flags |= HTTPC_STREAMING; + return TRUE; + } + + return FALSE; +} + +static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor); +static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor); + /** * Get the timeline with optionally mentions */ @@ -791,23 +836,6 @@ void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor) if (include_mentions) { twitter_get_mentions(ic, next_cursor); } - - static int bla = 0; - - if (bla) - return; - bla = 1; - - char *args[4]; - args[0] = "with"; - args[1] = "followings"; - args[2] = "delimited"; - args[3] = "length"; - - if ((td->stream = twitter_http(ic, "https://userstream.twitter.com/1.1/user.json", - twitter_http_stream, ic, 0, args, 4))) { - td->stream->flags |= HTTPC_STREAMING; - } } /** -- cgit v1.2.3 From 62f6b45d2056b90f57b908cd9bc9b6a995365c43 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Nov 2012 16:53:56 +0000 Subject: Realised I don't need delimited=length at all, objects are guaranteed to be CRLF-terminated. --- protocols/twitter/twitter_lib.c | 54 +++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 32 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 031d824e..3e5da854 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -731,49 +731,39 @@ static gboolean twitter_stream_handle_object(struct im_connection *ic, json_valu static void twitter_http_stream(struct http_request *req) { struct im_connection *ic = req->data; - //struct twitter_data *td = ic->proto_data; json_value *parsed; - int len, i; - char c; + int len = 0; + char c, *nl; if (!g_slist_find(twitter_connections, ic)) return; - //td = ic->proto_data;; printf( "%d bytes in stream\n", req->body_size ); - /* m/^[\d\s]*/ /* why is there a second commend here? :-) */ - for (i = 0; i < req->body_size; i ++) { - if (!isspace(req->reply_body[i]) && !isdigit(req->reply_body[i])) - break; - } - - /* Nothing but numbers and whitespace in there. Try again later. */ - if (i == req->body_size) - return; - - /* Get length. */ - if (sscanf(req->reply_body, "%d", &len) != 1) - return; + /* MUST search for CRLF, not just LF: + https://dev.twitter.com/docs/streaming-apis/processing#Parsing_responses */ + nl = strstr(req->reply_body, "\r\n"); - if (req->body_size < i + len) { - printf("Not enough bytes in buffer yet\n"); + if (!nl) { + printf("Incomplete data\n"); return; } - http_flush_bytes(req, i); - c = req->reply_body[len]; - req->reply_body[len] = '\0'; - - printf("JSON: %s\n", req->reply_body); - printf("parsed: %p\n", (parsed = json_parse(req->reply_body))); - if (parsed) { - twitter_stream_handle_object(ic, parsed); + len = nl - req->reply_body; + if (len > 0) { + c = req->reply_body[len]; + req->reply_body[len] = '\0'; + + printf("JSON: %s\n", req->reply_body); + printf("parsed: %p\n", (parsed = json_parse(req->reply_body))); + if (parsed) { + twitter_stream_handle_object(ic, parsed); + } + json_value_free(parsed); + req->reply_body[len] = c; } - json_value_free(parsed); - req->reply_body[len] = c; - http_flush_bytes(req, len); + http_flush_bytes(req, len + 2); /* One notification might bring multiple events! */ if (req->body_size > 0) @@ -798,10 +788,10 @@ static gboolean twitter_stream_handle_object(struct im_connection *ic, json_valu gboolean twitter_open_stream(struct im_connection *ic) { struct twitter_data *td = ic->proto_data; - char *args[4] = {"with", "followings", "delimited", "length"}; + char *args[2] = {"with", "followings"}; if ((td->stream = twitter_http(ic, TWITTER_USER_STREAM_URL, - twitter_http_stream, ic, 0, args, 4))) { + twitter_http_stream, ic, 0, args, 2))) { /* This flag must be enabled or we'll get no data until EOF (which err, kind of, defeats the purpose of a streaming API). */ td->stream->flags |= HTTPC_STREAMING; -- cgit v1.2.3 From 2fb1262a8200ec05d1b3334103fb7182dc2b2fa7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Nov 2012 18:22:39 +0000 Subject: Tiny cleanup. Fixing some memory leaks (why did I not notice so far that those free()s were commented out?). --- protocols/twitter/twitter_lib.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 3e5da854..ee19786a 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -957,9 +957,7 @@ static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor) } g_free(args[1]); - if (td->timeline_id) { - g_free(args[5]); - } + g_free(args[5]); } /** @@ -985,7 +983,7 @@ static void twitter_http_get_home_timeline(struct http_request *req) if (!(parsed = twitter_parse_response(ic, req))) goto end; twitter_xt_get_status_list(ic, parsed, txl); -// json_value_free(parsed); + json_value_free(parsed); td->home_timeline_obj = txl; @@ -1021,7 +1019,7 @@ static void twitter_http_get_mentions(struct http_request *req) if (!(parsed = twitter_parse_response(ic, req))) goto end; twitter_xt_get_status_list(ic, parsed, txl); -// json_value_free(parsed); + json_value_free(parsed); td->mentions_obj = txl; -- cgit v1.2.3 From dd672e2c4d0dcf73a30be3d8f7fc2ec38cb6450e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Nov 2012 21:52:26 +0000 Subject: Detect and handle streaming connection loss. --- protocols/twitter/twitter_lib.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index ee19786a..d7e54392 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -731,6 +731,7 @@ static gboolean twitter_stream_handle_object(struct im_connection *ic, json_valu static void twitter_http_stream(struct http_request *req) { struct im_connection *ic = req->data; + struct twitter_data *td; json_value *parsed; int len = 0; char c, *nl; @@ -738,6 +739,15 @@ static void twitter_http_stream(struct http_request *req) if (!g_slist_find(twitter_connections, ic)) return; + td = ic->proto_data; + + if ((req->flags & HTTPC_EOF) || !req->reply_body) { + td->stream = NULL; + imcb_error(ic, "Stream closed (%s)", req->status_string); + imc_logout(ic, TRUE); + return; + } + printf( "%d bytes in stream\n", req->body_size ); /* MUST search for CRLF, not just LF: -- cgit v1.2.3 From e132b60e77f395463cf95dc4ee09e96e9658ae35 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Nov 2012 23:32:47 +0000 Subject: Extend keepalive code to time out connections when pings don't get acknowledged, using this for Twitter streams and MSN so far. --- protocols/twitter/twitter_lib.c | 1 + 1 file changed, 1 insertion(+) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index d7e54392..bf5d76ab 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -739,6 +739,7 @@ static void twitter_http_stream(struct http_request *req) if (!g_slist_find(twitter_connections, ic)) return; + ic->flags |= OPT_PONGED; td = ic->proto_data; if ((req->flags & HTTPC_EOF) || !req->reply_body) { -- cgit v1.2.3 From d1356cb8b0f964ddf7de50e1ba52eecc271e470a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Nov 2012 23:57:43 +0000 Subject: Decode incoming DMs. --- protocols/twitter/twitter_lib.c | 101 ++++++++++++++++++++++++++++++---------- 1 file changed, 76 insertions(+), 25 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index bf5d76ab..57c84f24 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -447,6 +447,8 @@ static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list * #define TWITTER_TIME_FORMAT "%a %b %d %H:%M:%S +0000 %Y" #endif +static char* expand_entities(char* text, const json_value *entities); + /** * Function to fill a twitter_xml_status struct. * It sets: @@ -499,36 +501,77 @@ static gboolean twitter_xt_get_status(const json_value *node, struct twitter_xml txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text); txs_free(rtxs); } else if (entities) { - JSON_O_FOREACH (entities, k, v) { - int i; - - if (v->type != json_array) + txs->text = expand_entities(txs->text, entities); + } + + return txs->text && txs->user && txs->id; +} + +/** + * Function to fill a twitter_xml_status struct (DM variant). + */ +static gboolean twitter_xt_get_dm(const json_value *node, struct twitter_xml_status *txs) +{ + const json_value *entities = NULL; + + if (node->type != json_object) + return FALSE; + + JSON_O_FOREACH (node, k, v) { + if (strcmp("text", k) == 0 && v->type == json_string) { + txs->text = g_memdup(v->u.string.ptr, v->u.string.length + 1); + } else if (strcmp("created_at", k) == 0 && v->type == json_string) { + struct tm parsed; + + /* Very sensitive to changes to the formatting of + this field. :-( Also assumes the timezone used + is UTC since C time handling functions suck. */ + if (strptime(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) + txs->created_at = mktime_utc(&parsed); + } else if (strcmp("sender", k) == 0 && v->type == json_object) { + txs->user = twitter_xt_get_user(v); + } else if (strcmp("id", k) == 0 && v->type == json_integer) { + txs->id = v->u.integer; + } + } + + if (entities) { + txs->text = expand_entities(txs->text, entities); + } + + return txs->text && txs->user && txs->id; +} + +static char* expand_entities(char* text, const json_value *entities) { + JSON_O_FOREACH (entities, k, v) { + int i; + + if (v->type != json_array) + continue; + if (strcmp(k, "urls") != 0 && strcmp(k, "media") != 0) + continue; + + for (i = 0; i < v->u.array.length; i ++) { + if (v->u.array.values[i]->type != json_object) continue; - if (strcmp(k, "urls") != 0 && strcmp(k, "media") != 0) + + const char *kort = json_o_str(v->u.array.values[i], "url"); + const char *disp = json_o_str(v->u.array.values[i], "display_url"); + char *pos, *new; + + if (!kort || !disp || !(pos = strstr(text, kort))) continue; - for (i = 0; i < v->u.array.length; i ++) { - if (v->u.array.values[i]->type != json_object) - continue; - - const char *kort = json_o_str(v->u.array.values[i], "url"); - const char *disp = json_o_str(v->u.array.values[i], "display_url"); - char *pos, *new; - - if (!kort || !disp || !(pos = strstr(txs->text, kort))) - continue; - - *pos = '\0'; - new = g_strdup_printf("%s%s <%s>%s", txs->text, kort, - disp, pos + strlen(kort)); - - g_free(txs->text); - txs->text = new; - } + *pos = '\0'; + new = g_strdup_printf("%s%s <%s>%s", text, kort, + disp, pos + strlen(kort)); + + g_free(text); + text = new; } } - - return txs->text && txs->user && txs->id; + + return text; } /** @@ -784,6 +827,7 @@ static void twitter_http_stream(struct http_request *req) static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o) { struct twitter_xml_status *txs = g_new0(struct twitter_xml_status, 1); + json_value *c; if (twitter_xt_get_status(o, txs)) { GSList *output = g_slist_append(NULL, txs); @@ -791,6 +835,13 @@ static gboolean twitter_stream_handle_object(struct im_connection *ic, json_valu txs_free(txs); g_slist_free(output); return TRUE; + } else if ((c = json_o_get(o, "direct_message")) && + twitter_xt_get_dm(c, txs)) { + GSList *output = g_slist_append(NULL, txs); + twitter_private_message_chat(ic, output); + txs_free(txs); + g_slist_free(output); + return TRUE; } txs_free(txs); return FALSE; -- cgit v1.2.3 From 5aa96fc81a66224d9218e54d70944b1ef3d26e2d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 20 Nov 2012 00:20:10 +0000 Subject: Some code for handling disconnect/event messages. --- protocols/twitter/twitter_lib.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 57c84f24..ff1d9f22 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -824,8 +824,11 @@ static void twitter_http_stream(struct http_request *req) twitter_http_stream(req); } +static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value *o); + static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o) { + struct twitter_data *td = ic->proto_data; struct twitter_xml_status *txs = g_new0(struct twitter_xml_status, 1); json_value *c; @@ -842,11 +845,49 @@ static gboolean twitter_stream_handle_object(struct im_connection *ic, json_valu txs_free(txs); g_slist_free(output); return TRUE; + } else if ((c = json_o_get(o, "event")) && c->type == json_string) { + twitter_stream_handle_event(ic, o); + return TRUE; + } else if ((c = json_o_get(o, "disconnect")) && c->type == json_object) { + /* HACK: Because we're inside an event handler, we can't just + disconnect here. Instead, just change the HTTP status string + into a Twitter status string. */ + char *reason = json_o_strdup(c, "reason"); + if (reason) { + g_free(td->stream->status_string); + td->stream->status_string = reason; + } + return TRUE; } txs_free(txs); return FALSE; } +static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value *o) +{ + struct twitter_data *td = ic->proto_data; + json_value *source = json_o_get(o, "source"); + json_value *target = json_o_get(o, "target"); + const char *type = json_o_str(o, "event"); + + if (!type || !source || source->type != json_object + || !target || target->type != json_object) { + return FALSE; + } + + if (strcmp(type, "follow") == 0) { + struct twitter_xml_user *us = twitter_xt_get_user(source); + struct twitter_xml_user *ut = twitter_xt_get_user(target); + if (strcmp(us->screen_name, td->user) == 0) { + twitter_add_buddy(ic, ut->screen_name, ut->name); + } + txu_free(us); + txu_free(ut); + } + + return TRUE; +} + gboolean twitter_open_stream(struct im_connection *ic) { struct twitter_data *td = ic->proto_data; -- cgit v1.2.3 From c751e512afd4dc200c04a70cc6d0edb94e63e8b3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Nov 2012 00:15:17 +0000 Subject: Fixing one obvious memory leak. --- protocols/twitter/twitter_lib.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index ff1d9f22..36efc18a 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -1157,6 +1157,8 @@ static void twitter_http_post(struct http_request *req) if ((id = json_o_get(parsed, "id")) && id->type == json_integer) td->last_status_id = id->u.integer; + + json_value_free(parsed); } /** -- cgit v1.2.3 From c9b5817211cfcbf66a44c6a347245d5913806f91 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Nov 2012 00:55:47 +0000 Subject: Minor rework: Always fill td->log now and use it not just for show_ids, but also for stream deduplication. Also, drop tweets from unknown people unless fetch_mentions is set. The stream will feed us that spam either way but not everyone wants to see it. Last, fixing a bug where in streaming mode, per-user last tweet times were no longer getting tracked. --- protocols/twitter/twitter_lib.c | 131 ++++++++++++++++++++++++++-------------- 1 file changed, 84 insertions(+), 47 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 36efc18a..7048e6ae 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -457,12 +457,14 @@ static char* expand_entities(char* text, const json_value *entities); * - the status id and * - the user in a twitter_xml_user struct. */ -static gboolean twitter_xt_get_status(const json_value *node, struct twitter_xml_status *txs) +static struct twitter_xml_status *twitter_xt_get_status(const json_value *node) { + struct twitter_xml_status *txs; const json_value *rt = NULL, *entities = NULL; if (node->type != json_object) return FALSE; + txs = g_new0(struct twitter_xml_status, 1); JSON_O_FOREACH (node, k, v) { if (strcmp("text", k) == 0 && v->type == json_string) { @@ -491,20 +493,22 @@ static gboolean twitter_xt_get_status(const json_value *node, struct twitter_xml /* If it's a (truncated) retweet, get the original. Even if the API claims it wasn't truncated because it may be lying. */ if (rt) { - struct twitter_xml_status *rtxs = g_new0(struct twitter_xml_status, 1); - if (!twitter_xt_get_status(rt, rtxs)) { + struct twitter_xml_status *rtxs = twitter_xt_get_status(rt); + if (rtxs) { + g_free(txs->text); + txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text); + txs->id = rtxs->id; txs_free(rtxs); - return TRUE; } - - g_free(txs->text); - txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text); - txs_free(rtxs); } else if (entities) { txs->text = expand_entities(txs->text, entities); } - return txs->text && txs->user && txs->id; + if (txs->text && txs->user && txs->id) + return txs; + + txs_free(txs); + return NULL; } /** @@ -584,7 +588,6 @@ static gboolean twitter_xt_get_status_list(struct im_connection *ic, const json_ struct twitter_xml_list *txl) { struct twitter_xml_status *txs; - bee_user_t *bu; int i; // Set the type of the list. @@ -596,54 +599,61 @@ static gboolean twitter_xt_get_status_list(struct im_connection *ic, const json_ // The root node should hold the list of statuses // Walk over the nodes children. for (i = 0; i < node->u.array.length; i ++) { - txs = g_new0(struct twitter_xml_status, 1); - twitter_xt_get_status(node->u.array.values[i], txs); - // Put the item in the front of the list. + txs = twitter_xt_get_status(node->u.array.values[i]); + if (!txs) + continue; + txl->list = g_slist_prepend(txl->list, txs); - - if (txs->user && txs->user->screen_name && - (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) { - struct twitter_user_data *tud = bu->data; - - if (txs->id > tud->last_id) { - tud->last_id = txs->id; - tud->last_time = txs->created_at; - } - } } return TRUE; } +/* Will log messages either way. Need to keep track of IDs for stream deduping. + Plus, show_ids is on by default and I don't see why anyone would disable it. */ static char *twitter_msg_add_id(struct im_connection *ic, struct twitter_xml_status *txs, const char *prefix) { struct twitter_data *td = ic->proto_data; - char *ret = NULL; - - if (!set_getbool(&ic->acc->set, "show_ids")) { - if (*prefix) - return g_strconcat(prefix, txs->text, NULL); - else - return NULL; - } + int reply_to = -1; + bee_user_t *bu; - td->log[td->log_id].id = txs->id; - td->log[td->log_id].bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name); if (txs->reply_to) { int i; for (i = 0; i < TWITTER_LOG_LENGTH; i++) if (td->log[i].id == txs->reply_to) { - ret = g_strdup_printf("\002[\002%02d->%02d\002]\002 %s%s", - td->log_id, i, prefix, txs->text); + reply_to = i; break; } } - if (ret == NULL) - ret = g_strdup_printf("\002[\002%02d\002]\002 %s%s", td->log_id, prefix, txs->text); - td->log_id = (td->log_id + 1) % TWITTER_LOG_LENGTH; - return ret; + if (txs->user && txs->user->screen_name && + (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) { + struct twitter_user_data *tud = bu->data; + + if (txs->id > tud->last_id) { + tud->last_id = txs->id; + tud->last_time = txs->created_at; + } + } + + td->log_id = (td->log_id + 1) % TWITTER_LOG_LENGTH; + td->log[td->log_id].id = txs->id; + td->log[td->log_id].bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name); + + if (set_getbool(&ic->acc->set, "show_ids")) { + if (reply_to != -1) + return g_strdup_printf("\002[\002%02d->%02d\002]\002 %s%s", + td->log_id, reply_to, prefix, txs->text); + else + return g_strdup_printf("\002[\002%02d\002]\002 %s%s", + td->log_id, prefix, txs->text); + } else { + if (*prefix) + return g_strconcat(prefix, txs->text, NULL); + else + return NULL; + } } static void twitter_groupchat_init(struct im_connection *ic) @@ -825,19 +835,16 @@ static void twitter_http_stream(struct http_request *req) } static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value *o); +static gboolean twitter_stream_handle_status(struct im_connection *ic, struct twitter_xml_status *txs); static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o) { struct twitter_data *td = ic->proto_data; - struct twitter_xml_status *txs = g_new0(struct twitter_xml_status, 1); + struct twitter_xml_status *txs; json_value *c; - if (twitter_xt_get_status(o, txs)) { - GSList *output = g_slist_append(NULL, txs); - twitter_groupchat(ic, output); - txs_free(txs); - g_slist_free(output); - return TRUE; + if ((txs = twitter_xt_get_status(o))) { + return twitter_stream_handle_status(ic, txs); } else if ((c = json_o_get(o, "direct_message")) && twitter_xt_get_dm(c, txs)) { GSList *output = g_slist_append(NULL, txs); @@ -859,10 +866,40 @@ static gboolean twitter_stream_handle_object(struct im_connection *ic, json_valu } return TRUE; } - txs_free(txs); return FALSE; } +static gboolean twitter_stream_handle_status(struct im_connection *ic, struct twitter_xml_status *txs) +{ + struct twitter_data *td = ic->proto_data; + int i; + + for (i = 0; i < TWITTER_LOG_LENGTH; i++) { + if (td->log[i].id == txs->id) { + /* Got a duplicate (RT, surely). Drop it. */ + txs_free(txs); + return TRUE; + } + } + + if (!(set_getbool(&ic->acc->set, "fetch_mentions") || + bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) { + /* Tweet is from an unknown person and the user does not want + to see @mentions, so drop it. twitter_stream_handle_event() + picks up new follows so this simple filter should be safe. */ + /* TODO: The streaming API seems to do poor @mention matching. + I.e. I'm getting mentions for @WilmerSomething, not just for + @Wilmer. But meh. You want spam, you get spam. */ + return TRUE; + } + + GSList *output = g_slist_append(NULL, txs); + twitter_groupchat(ic, output); + txs_free(txs); + g_slist_free(output); + return TRUE; +} + static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value *o) { struct twitter_data *td = ic->proto_data; -- cgit v1.2.3 From 2cd8540c1076c3d0423abb1927bd47fdcbfbd382 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Nov 2012 12:05:48 +0000 Subject: Show DMs the right way. With mode=many and show_ids=off they'll look just like tweets but that's what you get with bad settings.. Also, don't show DMs from ourselves. --- protocols/twitter/twitter_lib.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 7048e6ae..676d804d 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -514,12 +514,14 @@ static struct twitter_xml_status *twitter_xt_get_status(const json_value *node) /** * Function to fill a twitter_xml_status struct (DM variant). */ -static gboolean twitter_xt_get_dm(const json_value *node, struct twitter_xml_status *txs) +static struct twitter_xml_status *twitter_xt_get_dm(const json_value *node) { + struct twitter_xml_status *txs; const json_value *entities = NULL; if (node->type != json_object) return FALSE; + txs = g_new0(struct twitter_xml_status, 1); JSON_O_FOREACH (node, k, v) { if (strcmp("text", k) == 0 && v->type == json_string) { @@ -543,7 +545,11 @@ static gboolean twitter_xt_get_dm(const json_value *node, struct twitter_xml_sta txs->text = expand_entities(txs->text, entities); } - return txs->text && txs->user && txs->id; + if (txs->text && txs->user && txs->id) + return txs; + + txs_free(txs); + return NULL; } static char* expand_entities(char* text, const json_value *entities) { @@ -846,11 +852,11 @@ static gboolean twitter_stream_handle_object(struct im_connection *ic, json_valu if ((txs = twitter_xt_get_status(o))) { return twitter_stream_handle_status(ic, txs); } else if ((c = json_o_get(o, "direct_message")) && - twitter_xt_get_dm(c, txs)) { - GSList *output = g_slist_append(NULL, txs); - twitter_private_message_chat(ic, output); + (txs = twitter_xt_get_dm(c))) { + if (strcmp(txs->user->screen_name, td->user) != 0) + imcb_buddy_msg(ic, txs->user->screen_name, + txs->text, 0, txs->created_at); txs_free(txs); - g_slist_free(output); return TRUE; } else if ((c = json_o_get(o, "event")) && c->type == json_string) { twitter_stream_handle_event(ic, o); -- cgit v1.2.3 From 96dd574444f2c99bb0a82b2c354804f03e306f23 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Nov 2012 13:11:19 +0000 Subject: s/twitter_msg/twitter_log/ and use it in a few more places. --- protocols/twitter/twitter_lib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 676d804d..f358b307 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -219,8 +219,8 @@ static json_value *twitter_parse_response(struct im_connection *ic, struct http_ } else if (req->status_code != 200) { // It didn't go well, output the error and return. if (!periodic || logging_in || ++td->http_fails >= 5) - imcb_error(ic, "Could not retrieve %s: %s", - path, twitter_parse_error(req)); + twitter_log(ic, "Error: Could not retrieve %s: %s", + path, twitter_parse_error(req)); if (logging_in) imc_logout(ic, TRUE); -- cgit v1.2.3 From 631ec80ccd9d3deeb590ae9537ad4260d804363d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Nov 2012 14:26:23 +0000 Subject: Changed mode/room management a little bit. --- protocols/twitter/twitter_lib.c | 56 ++++++++--------------------------------- 1 file changed, 10 insertions(+), 46 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index f358b307..ad349c6f 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -69,8 +69,6 @@ struct twitter_xml_status { guint64 id, reply_to; }; -static void twitter_groupchat_init(struct im_connection *ic); - /** * Frees a twitter_xml_user struct. */ @@ -147,17 +145,15 @@ static void twitter_add_buddy(struct im_connection *ic, char *name, const char * // Check if the buddy is already in the buddy list. if (!bee_user_by_handle(ic->bee, ic, name)) { - char *mode = set_getstr(&ic->acc->set, "mode"); - // The buddy is not in the list, add the buddy and set the status to logged in. imcb_add_buddy(ic, name, NULL); imcb_rename_buddy(ic, name, fullname); - if (g_strcasecmp(mode, "chat") == 0) { + if (td->flags & TWITTER_MODE_CHAT) { /* Necessary so that nicks always get translated to the exact Twitter username. */ imcb_buddy_nick_hint(ic, name, name); imcb_chat_add_buddy(td->timeline_gc, name); - } else if (g_strcasecmp(mode, "many") == 0) + } else if (td->flags & TWITTER_MODE_MANY) imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL); } } @@ -305,10 +301,6 @@ static void twitter_http_get_friends_ids(struct http_request *req) td = ic->proto_data; - /* Create the room now that we "logged in". */ - if (!td->timeline_gc && g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) - twitter_groupchat_init(ic); - txl = g_new0(struct twitter_xml_list, 1); txl->list = td->follow_ids; @@ -662,26 +654,6 @@ static char *twitter_msg_add_id(struct im_connection *ic, } } -static void twitter_groupchat_init(struct im_connection *ic) -{ - char *name_hint; - struct groupchat *gc; - struct twitter_data *td = ic->proto_data; - GSList *l; - - td->timeline_gc = gc = imcb_chat_new(ic, "twitter/timeline"); - - name_hint = g_strdup_printf("%s_%s", td->prefix, ic->acc->user); - imcb_chat_name_hint(gc, name_hint); - g_free(name_hint); - - for (l = ic->bee->users; l; l = l->next) { - bee_user_t *bu = l->data; - if (bu->ic == ic) - imcb_chat_add_buddy(td->timeline_gc, bu->handle); - } -} - /** * Function that is called to see the statuses in a groupchat window. */ @@ -694,12 +666,7 @@ static void twitter_groupchat(struct im_connection *ic, GSList * list) guint64 last_id = 0; // Create a new groupchat if it does not exsist. - if (!td->timeline_gc) - twitter_groupchat_init(ic); - - gc = td->timeline_gc; - if (!gc->joined) - imcb_chat_add_buddy(gc, ic->acc->user); + gc = twitter_groupchat_init(ic); for (l = list; l; l = g_slist_next(l)) { char *msg; @@ -743,13 +710,10 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList * list struct twitter_data *td = ic->proto_data; GSList *l = NULL; struct twitter_xml_status *status; - char from[MAX_STRING]; - gboolean mode_one; + char from[MAX_STRING] = ""; guint64 last_id = 0; - mode_one = g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "one") == 0; - - if (mode_one) { + if (td->flags & TWITTER_MODE_ONE) { g_snprintf(from, sizeof(from) - 1, "%s_%s", td->prefix, ic->acc->user); from[MAX_STRING - 1] = '\0'; } @@ -764,17 +728,17 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList * list last_id = status->id; strip_html(status->text); - if (mode_one) + if (td->flags & TWITTER_MODE_ONE) prefix = g_strdup_printf("\002<\002%s\002>\002 ", - status->user->screen_name); + status->user->screen_name); else twitter_add_buddy(ic, status->user->screen_name, status->user->name); text = twitter_msg_add_id(ic, status, prefix ? prefix : ""); imcb_buddy_msg(ic, - mode_one ? from : status->user->screen_name, - text ? text : status->text, 0, status->created_at); + *from ? from : status->user->screen_name, + text ? text : status->text, 0, status->created_at); // Update the timeline_id to hold the highest id, so that by the next request // we won't pick up the updates already in the list. @@ -1016,7 +980,7 @@ void twitter_flush_timeline(struct im_connection *ic) imcb_connected(ic); // See if the user wants to see the messages in a groupchat window or as private messages. - if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) + if (td->flags & TWITTER_MODE_CHAT) twitter_groupchat(ic, output); else twitter_private_message_chat(ic, output); -- cgit v1.2.3 From 7f557d53d986c5f688c5781fea04a46464e5b491 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Nov 2012 14:58:29 +0000 Subject: Fixing two oopses from my last commit. --- protocols/twitter/twitter_lib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index ad349c6f..1b7f382a 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -152,7 +152,8 @@ static void twitter_add_buddy(struct im_connection *ic, char *name, const char * /* Necessary so that nicks always get translated to the exact Twitter username. */ imcb_buddy_nick_hint(ic, name, name); - imcb_chat_add_buddy(td->timeline_gc, name); + if (td->timeline_gc) + imcb_chat_add_buddy(td->timeline_gc, name); } else if (td->flags & TWITTER_MODE_MANY) imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL); } -- cgit v1.2.3 From 29f72b7a4b4fa960b6fccb45243fa3f5d87a99bf Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Nov 2012 17:43:13 +0000 Subject: Finally cleaned up the show-tweet functions. --- protocols/twitter/twitter_lib.c | 145 +++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 77 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 1b7f382a..b888a730 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -462,6 +462,7 @@ static struct twitter_xml_status *twitter_xt_get_status(const json_value *node) JSON_O_FOREACH (node, k, v) { if (strcmp("text", k) == 0 && v->type == json_string) { txs->text = g_memdup(v->u.string.ptr, v->u.string.length + 1); + strip_html(txs->text); } else if (strcmp("retweeted_status", k) == 0 && v->type == json_object) { rt = v; } else if (strcmp("created_at", k) == 0 && v->type == json_string) { @@ -519,6 +520,7 @@ static struct twitter_xml_status *twitter_xt_get_dm(const json_value *node) JSON_O_FOREACH (node, k, v) { if (strcmp("text", k) == 0 && v->type == json_string) { txs->text = g_memdup(v->u.string.ptr, v->u.string.length + 1); + strip_html(txs->text); } else if (strcmp("created_at", k) == 0 && v->type == json_string) { struct tm parsed; @@ -566,7 +568,7 @@ static char* expand_entities(char* text, const json_value *entities) { continue; *pos = '\0'; - new = g_strdup_printf("%s%s <%s>%s", text, kort, + new = g_strdup_printf("%s%s <%s>%s", text, kort, disp, pos + strlen(kort)); g_free(text); @@ -658,96 +660,85 @@ static char *twitter_msg_add_id(struct im_connection *ic, /** * Function that is called to see the statuses in a groupchat window. */ -static void twitter_groupchat(struct im_connection *ic, GSList * list) +static void twitter_status_show_chat(struct im_connection *ic, struct twitter_xml_status *status) { struct twitter_data *td = ic->proto_data; - GSList *l = NULL; - struct twitter_xml_status *status; struct groupchat *gc; - guint64 last_id = 0; + gboolean me = g_strcasecmp(td->user, status->user->screen_name) == 0; + char *msg; // Create a new groupchat if it does not exsist. gc = twitter_groupchat_init(ic); - for (l = list; l; l = g_slist_next(l)) { - char *msg; - - status = l->data; - if (status->user == NULL || status->text == NULL || last_id == status->id) - continue; - - last_id = status->id; - - strip_html(status->text); - - if (set_getbool(&ic->acc->set, "strip_newlines")) - strip_newlines(status->text); - - msg = twitter_msg_add_id(ic, status, ""); - - // Say it! - if (g_strcasecmp(td->user, status->user->screen_name) == 0) { - imcb_chat_log(gc, "You: %s", msg ? msg : status->text); - } else { - twitter_add_buddy(ic, status->user->screen_name, status->user->name); - - imcb_chat_msg(gc, status->user->screen_name, - msg ? msg : status->text, 0, status->created_at); - } - - g_free(msg); - - // Update the timeline_id to hold the highest id, so that by the next request - // we won't pick up the updates already in the list. - td->timeline_id = MAX(td->timeline_id, status->id); + if (!me) + /* MUST be done before twitter_msg_add_id() to avoid #872. */ + twitter_add_buddy(ic, status->user->screen_name, status->user->name); + msg = twitter_msg_add_id(ic, status, ""); + + // Say it! + if (me) { + imcb_chat_log(gc, "You: %s", msg ? msg : status->text); + } else { + imcb_chat_msg(gc, status->user->screen_name, + msg ? msg : status->text, 0, status->created_at); } + + g_free(msg); } /** * Function that is called to see statuses as private messages. */ -static void twitter_private_message_chat(struct im_connection *ic, GSList * list) +static void twitter_status_show_msg(struct im_connection *ic, struct twitter_xml_status *status) { struct twitter_data *td = ic->proto_data; - GSList *l = NULL; - struct twitter_xml_status *status; char from[MAX_STRING] = ""; - guint64 last_id = 0; + char *prefix = NULL, *text = NULL; + gboolean me = g_strcasecmp(td->user, status->user->screen_name) == 0; if (td->flags & TWITTER_MODE_ONE) { g_snprintf(from, sizeof(from) - 1, "%s_%s", td->prefix, ic->acc->user); from[MAX_STRING - 1] = '\0'; } - for (l = list; l; l = g_slist_next(l)) { - char *prefix = NULL, *text = NULL; - - status = l->data; - if (status->user == NULL || status->text == NULL || last_id == status->id) - continue; - - last_id = status->id; + if (td->flags & TWITTER_MODE_ONE) + prefix = g_strdup_printf("\002<\002%s\002>\002 ", + status->user->screen_name); + else if (!me) + twitter_add_buddy(ic, status->user->screen_name, status->user->name); + else + prefix = g_strdup("You: "); - strip_html(status->text); - if (td->flags & TWITTER_MODE_ONE) - prefix = g_strdup_printf("\002<\002%s\002>\002 ", - status->user->screen_name); - else - twitter_add_buddy(ic, status->user->screen_name, status->user->name); + text = twitter_msg_add_id(ic, status, prefix ? prefix : ""); - text = twitter_msg_add_id(ic, status, prefix ? prefix : ""); + imcb_buddy_msg(ic, + *from ? from : status->user->screen_name, + text ? text : status->text, 0, status->created_at); - imcb_buddy_msg(ic, - *from ? from : status->user->screen_name, - text ? text : status->text, 0, status->created_at); + g_free(text); + g_free(prefix); +} - // Update the timeline_id to hold the highest id, so that by the next request - // we won't pick up the updates already in the list. - td->timeline_id = MAX(td->timeline_id, status->id); +static void twitter_status_show(struct im_connection *ic, struct twitter_xml_status *status) +{ + struct twitter_data *td = ic->proto_data; + + if (status->user == NULL || status->text == NULL) + return; + + /* Grrrr. Would like to do this during parsing, but can't access + settings from there. */ + if (set_getbool(&ic->acc->set, "strip_newlines")) + strip_newlines(status->text); + + if (td->flags & TWITTER_MODE_CHAT) + twitter_status_show_chat(ic, status); + else + twitter_status_show_msg(ic, status); - g_free(text); - g_free(prefix); - } + // Update the timeline_id to hold the highest id, so that by the next request + // we won't pick up the updates already in the list. + td->timeline_id = MAX(td->timeline_id, status->id); } static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o); @@ -847,7 +838,7 @@ static gboolean twitter_stream_handle_status(struct im_connection *ic, struct tw for (i = 0; i < TWITTER_LOG_LENGTH; i++) { if (td->log[i].id == txs->id) { - /* Got a duplicate (RT, surely). Drop it. */ + /* Got a duplicate (RT, probably). Drop it. */ txs_free(txs); return TRUE; } @@ -864,10 +855,9 @@ static gboolean twitter_stream_handle_status(struct im_connection *ic, struct tw return TRUE; } - GSList *output = g_slist_append(NULL, txs); - twitter_groupchat(ic, output); + twitter_status_show(ic, txs); txs_free(txs); - g_slist_free(output); + return TRUE; } @@ -950,9 +940,12 @@ void twitter_flush_timeline(struct im_connection *ic) int show_old_mentions = set_getint(&ic->acc->set, "show_old_mentions"); struct twitter_xml_list *home_timeline = td->home_timeline_obj; struct twitter_xml_list *mentions = td->mentions_obj; + guint64 last_id = 0; GSList *output = NULL; GSList *l; + imcb_connected(ic); + if (!(td->flags & TWITTER_GOT_TIMELINE)) { return; } @@ -976,17 +969,15 @@ void twitter_flush_timeline(struct im_connection *ic) output = g_slist_insert_sorted(output, l->data, twitter_compare_elements); } } - - if (!(ic->flags & OPT_LOGGED_IN)) - imcb_connected(ic); // See if the user wants to see the messages in a groupchat window or as private messages. - if (td->flags & TWITTER_MODE_CHAT) - twitter_groupchat(ic, output); - else - twitter_private_message_chat(ic, output); - - g_slist_free(output); + while (output) { + struct twitter_xml_status *txs = output->data; + if (txs->id != last_id) + twitter_status_show(ic, txs); + last_id = txs->id; + output = g_slist_remove(output, txs); + } txl_free(home_timeline); txl_free(mentions); -- cgit v1.2.3 From 9ed00818d7351ca5bcb4ba16b5dfe900d45831d5 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Nov 2012 18:39:11 +0000 Subject: Fixed one potential memory leak, and re-remembered that for GLib + valgrind you REALLY need to set G_SLICE=always-malloc. Argh! --- protocols/twitter/twitter_lib.c | 1 - 1 file changed, 1 deletion(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index b888a730..fe276133 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -270,7 +270,6 @@ static gboolean twitter_xt_get_friends_id_list(json_value *node, struct twitter_ txl->list = g_slist_prepend(txl->list, g_strdup_printf("%lld", c->u.array.values[i]->u.integer)); - } c = json_o_get(node, "next_cursor"); -- cgit v1.2.3 From f97b8e9637acba704e976dff79436a83c0f9c63a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Nov 2012 20:43:52 +0000 Subject: Use hex for show_ids, but stick to the 2-char maximum. a 256-message backlog really should be enough. --- protocols/twitter/twitter_lib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index fe276133..862104f5 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -643,10 +643,10 @@ static char *twitter_msg_add_id(struct im_connection *ic, if (set_getbool(&ic->acc->set, "show_ids")) { if (reply_to != -1) - return g_strdup_printf("\002[\002%02d->%02d\002]\002 %s%s", + return g_strdup_printf("\002[\002%02x->%02x\002]\002 %s%s", td->log_id, reply_to, prefix, txs->text); else - return g_strdup_printf("\002[\002%02d\002]\002 %s%s", + return g_strdup_printf("\002[\002%02x\002]\002 %s%s", td->log_id, prefix, txs->text); } else { if (*prefix) -- cgit v1.2.3 From 67f68282bb20ad3af6dfa6017b89b89ab0a1767f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Nov 2012 22:09:41 +0000 Subject: Only a few commands use twitter_message_id_from_command_arg(), others were still using a decimal scanf format string. Messy code duplication. :-( --- protocols/twitter/twitter_lib.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 862104f5..f4c81e0c 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -66,7 +66,8 @@ struct twitter_xml_status { time_t created_at; char *text; struct twitter_xml_user *user; - guint64 id, reply_to; + guint64 id, rt_id; /* Usually equal, with RTs id == *original* id */ + guint64 reply_to; }; /** @@ -490,6 +491,7 @@ static struct twitter_xml_status *twitter_xt_get_status(const json_value *node) if (rtxs) { g_free(txs->text); txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text); + txs->rt_id = txs->id; txs->id = rtxs->id; txs_free(rtxs); } @@ -641,6 +643,12 @@ static char *twitter_msg_add_id(struct im_connection *ic, td->log[td->log_id].id = txs->id; td->log[td->log_id].bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name); + /* This is all getting hairy. :-( If we RT'ed something ourselves, + remember OUR id instead so undo will work. In other cases, the + original tweet's id should be remembered for deduplicating. */ + if (txs->rt_id && strcmp(txs->user->screen_name, td->user) == 0) + td->log[td->log_id].id = txs->rt_id; + if (set_getbool(&ic->acc->set, "show_ids")) { if (reply_to != -1) return g_strdup_printf("\002[\002%02x->%02x\002]\002 %s%s", @@ -1153,8 +1161,9 @@ static void twitter_http_post(struct http_request *req) if (!(parsed = twitter_parse_response(ic, req))) return; - if ((id = json_o_get(parsed, "id")) && id->type == json_integer) + if ((id = json_o_get(parsed, "id")) && id->type == json_integer) { td->last_status_id = id->u.integer; + } json_value_free(parsed); } -- cgit v1.2.3 From b235228768645c11d3ef138ddba839c7dd529567 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Nov 2012 22:28:38 +0000 Subject: Have root confirm some commands that so far gave no feedback at all, since "no news is good news" can be a little confusing. --- protocols/twitter/twitter_lib.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index f4c81e0c..c7041a68 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -1166,6 +1166,9 @@ static void twitter_http_post(struct http_request *req) } json_value_free(parsed); + + if (req->flags & TWITTER_HTTP_USER_ACK) + twitter_log(ic, "Command processed successfully"); } /** @@ -1212,7 +1215,8 @@ void twitter_status_destroy(struct im_connection *ic, guint64 id) char *url; url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_DESTROY_URL, (unsigned long long) id, ".json"); - twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); + twitter_http_f(ic, url, twitter_http_post, ic, 1, NULL, 0, + TWITTER_HTTP_USER_ACK); g_free(url); } @@ -1221,7 +1225,8 @@ void twitter_status_retweet(struct im_connection *ic, guint64 id) char *url; url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_RETWEET_URL, (unsigned long long) id, ".json"); - twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); + twitter_http_f(ic, url, twitter_http_post, ic, 1, NULL, 0, + TWITTER_HTTP_USER_ACK); g_free(url); } @@ -1235,8 +1240,8 @@ void twitter_report_spam(struct im_connection *ic, char *screen_name) NULL, }; args[1] = screen_name; - twitter_http(ic, TWITTER_REPORT_SPAM_URL, twitter_http_post, - ic, 1, args, 2); + twitter_http_f(ic, TWITTER_REPORT_SPAM_URL, twitter_http_post, + ic, 1, args, 2, TWITTER_HTTP_USER_ACK); } /** @@ -1247,6 +1252,7 @@ void twitter_favourite_tweet(struct im_connection *ic, guint64 id) char *url; url = g_strdup_printf("%s%llu%s", TWITTER_FAVORITE_CREATE_URL, (unsigned long long) id, ".json"); - twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); + twitter_http_f(ic, url, twitter_http_post, ic, 1, NULL, 0, + TWITTER_HTTP_USER_ACK); g_free(url); } -- cgit v1.2.3 From 2dcde94625f2ae1af1aef674c2aa1e2bc714d144 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 3 Dec 2012 23:30:33 +0000 Subject: Don't hide own tweets in streaming mode with fetch_mentions off, and move txs_free() to the right place - fixes a memory leak. --- protocols/twitter/twitter_lib.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index c7041a68..a8b203bb 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -813,7 +813,9 @@ static gboolean twitter_stream_handle_object(struct im_connection *ic, json_valu json_value *c; if ((txs = twitter_xt_get_status(o))) { - return twitter_stream_handle_status(ic, txs); + gboolean ret = twitter_stream_handle_status(ic, txs); + txs_free(txs); + return ret; } else if ((c = json_o_get(o, "direct_message")) && (txs = twitter_xt_get_dm(c))) { if (strcmp(txs->user->screen_name, td->user) != 0) @@ -846,12 +848,12 @@ static gboolean twitter_stream_handle_status(struct im_connection *ic, struct tw for (i = 0; i < TWITTER_LOG_LENGTH; i++) { if (td->log[i].id == txs->id) { /* Got a duplicate (RT, probably). Drop it. */ - txs_free(txs); return TRUE; } } - if (!(set_getbool(&ic->acc->set, "fetch_mentions") || + if (!(strcmp(txs->user->screen_name, td->user) == 0 || + set_getbool(&ic->acc->set, "fetch_mentions") || bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) { /* Tweet is from an unknown person and the user does not want to see @mentions, so drop it. twitter_stream_handle_event() @@ -863,7 +865,6 @@ static gboolean twitter_stream_handle_status(struct im_connection *ic, struct tw } twitter_status_show(ic, txs); - txs_free(txs); return TRUE; } -- cgit v1.2.3 From 573e274c58bf7d154b35ab5cd9d0b711f7ede715 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 21 Dec 2012 22:50:05 +0100 Subject: For finding since_id, don't use RT original IDs of course. :-/ This fixes duplicate-tweet issues in non-streaming mode. --- protocols/twitter/twitter_lib.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'protocols/twitter/twitter_lib.c') diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index a8b203bb..3d368d2e 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -476,7 +476,7 @@ static struct twitter_xml_status *twitter_xt_get_status(const json_value *node) } else if (strcmp("user", k) == 0 && v->type == json_object) { txs->user = twitter_xt_get_user(v); } else if (strcmp("id", k) == 0 && v->type == json_integer) { - txs->id = v->u.integer; + txs->rt_id = txs->id = v->u.integer; } else if (strcmp("in_reply_to_status_id", k) == 0 && v->type == json_integer) { txs->reply_to = v->u.integer; } else if (strcmp("entities", k) == 0 && v->type == json_object) { @@ -491,7 +491,6 @@ static struct twitter_xml_status *twitter_xt_get_status(const json_value *node) if (rtxs) { g_free(txs->text); txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text); - txs->rt_id = txs->id; txs->id = rtxs->id; txs_free(rtxs); } @@ -646,7 +645,7 @@ static char *twitter_msg_add_id(struct im_connection *ic, /* This is all getting hairy. :-( If we RT'ed something ourselves, remember OUR id instead so undo will work. In other cases, the original tweet's id should be remembered for deduplicating. */ - if (txs->rt_id && strcmp(txs->user->screen_name, td->user) == 0) + if (strcmp(txs->user->screen_name, td->user) == 0) td->log[td->log_id].id = txs->rt_id; if (set_getbool(&ic->acc->set, "show_ids")) { @@ -745,7 +744,7 @@ static void twitter_status_show(struct im_connection *ic, struct twitter_xml_sta // Update the timeline_id to hold the highest id, so that by the next request // we won't pick up the updates already in the list. - td->timeline_id = MAX(td->timeline_id, status->id); + td->timeline_id = MAX(td->timeline_id, status->rt_id); } static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o); -- cgit v1.2.3