diff options
Diffstat (limited to 'protocols/twitter/twitter_lib.c')
-rw-r--r-- | protocols/twitter/twitter_lib.c | 456 |
1 files changed, 293 insertions, 163 deletions
diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 4a09cbb1..57c84f24 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -34,8 +34,8 @@ #include "url.h" #include "misc.h" #include "base64.h" -#include "xmltree.h" #include "twitter_lib.h" +#include "json_util.h" #include <ctype.h> #include <errno.h> @@ -167,32 +167,34 @@ 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; 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 && 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; } -static struct xt_node *twitter_parse_response(struct im_connection *ic, struct http_request *req) +/* 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); 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, ' '))) { @@ -210,7 +212,8 @@ static struct xt_node *twitter_parse_response(struct im_connection *ic, struct h /* 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) { @@ -226,7 +229,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"); } @@ -250,45 +253,36 @@ void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor) } /** - * 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 gboolean 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 <statuses> node should hold the list of statuses <status> - // 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 FALSE; - return XT_HANDLED; + 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("%lld", 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 TRUE; } static void twitter_get_users_lookup(struct im_connection *ic); @@ -299,7 +293,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 +315,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 +332,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 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) @@ -378,7 +373,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 +389,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,25 +403,15 @@ 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 twitter_xml_user *twitter_xt_get_user(const json_value *node) { - 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; + 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; } /** @@ -434,26 +419,26 @@ static xt_status twitter_xt_get_user(struct xt_node *node, struct twitter_xml_us * It sets: * - all <user>s from the <users> element. */ -static xt_status twitter_xt_get_users(struct xt_node *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; - struct xt_node *child; + int i; // Set the type of the list. txl->type = TXL_USER; + if (!node || node->type != json_array) + return FALSE; + // The root <users> node should hold the list of users <user> // 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. + for (i = 0; i < node->u.array.length; i ++) { + txu = twitter_xt_get_user(node->u.array.values[i]); + if (txu) txl->list = g_slist_prepend(txl->list, txu); - } } - return XT_HANDLED; + return TRUE; } #ifdef __GLIBC__ @@ -462,6 +447,8 @@ static xt_status twitter_xt_get_users(struct xt_node *node, struct twitter_xml_l #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: @@ -470,31 +457,34 @@ static xt_status twitter_xt_get_users(struct xt_node *node, struct twitter_xml_l * - 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 gboolean twitter_xt_get_status(const json_value *node, struct twitter_xml_status *txs) { - struct xt_node *child, *rt = NULL; - - // 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) { + const json_value *rt = NULL, *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("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) { + entities = v; } } @@ -502,45 +492,86 @@ static xt_status twitter_xt_get_status(struct xt_node *node, struct twitter_xml_ 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); txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text); txs_free(rtxs); - } else { - struct xt_node *urls, *url; + } else if (entities) { + 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; - 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) + 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; - 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"); - char *pos, *new; - - if (!kort || !kort->text || !disp || !disp->text || - !(pos = strstr(txs->text, kort->text))) - continue; - - *pos = '\0'; - new = g_strdup_printf("%s%s <%s>%s", txs->text, kort->text, - disp->text, pos + strlen(kort->text)); - - g_free(txs->text); - txs->text = new; - } + 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; + + *pos = '\0'; + new = g_strdup_printf("%s%s <%s>%s", text, kort, + disp, pos + strlen(kort)); + + g_free(text); + text = new; } } - - return XT_HANDLED; + + return text; } /** @@ -549,40 +580,39 @@ static xt_status twitter_xt_get_status(struct xt_node *node, struct twitter_xml_ * - all <status>es within the <status> element and * - the next_cursor. */ -static xt_status twitter_xt_get_status_list(struct im_connection *ic, struct xt_node *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; - struct xt_node *child; bee_user_t *bu; + int i; // Set the type of the list. txl->type = TXL_STATUS; + + if (node->type != json_array) + return FALSE; // The root <statuses> node should hold the list of statuses <status> // 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); } } - return XT_HANDLED; + return TRUE; } static char *twitter_msg_add_id(struct im_connection *ic, @@ -739,8 +769,102 @@ 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 gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o); + +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; + + if (!g_slist_find(twitter_connections, ic)) + return; + + ic->flags |= OPT_PONGED; + 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: + https://dev.twitter.com/docs/streaming-apis/processing#Parsing_responses */ + nl = strstr(req->reply_body, "\r\n"); + + if (!nl) { + printf("Incomplete data\n"); + return; + } + + 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; + } + + http_flush_bytes(req, len + 2); + + /* 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); + 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; + } 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; +} + +gboolean twitter_open_stream(struct im_connection *ic) +{ + struct twitter_data *td = ic->proto_data; + char *args[2] = {"with", "followings"}; + + if ((td->stream = twitter_http(ic, TWITTER_USER_STREAM_URL, + 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; + 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 @@ -822,10 +946,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; @@ -861,7 +988,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; @@ -892,9 +1019,7 @@ 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]); } /** @@ -904,7 +1029,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. @@ -920,11 +1045,14 @@ 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; end: + if (!g_slist_find(twitter_connections, ic)) + return; + td->flags |= TWITTER_GOT_TIMELINE; twitter_flush_timeline(ic); @@ -937,7 +1065,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. @@ -953,11 +1081,14 @@ 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; end: + if (!g_slist_find(twitter_connections, ic)) + return; + td->flags |= TWITTER_GOT_MENTIONS; twitter_flush_timeline(ic); @@ -971,7 +1102,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)) @@ -983,9 +1114,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; } /** @@ -1031,7 +1161,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 +1170,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 +1196,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); } |