facebook: implemented handling of message attachments facebook

Tue, 04 Aug 2015 20:45:13 -0400

author
James Geboski <jgeboski@gmail.com>
date
Tue, 04 Aug 2015 20:45:13 -0400
branch
facebook
changeset 37315
7a7771557143
parent 37314
1fedb5e19577
child 37316
9e3520d6ec49

facebook: implemented handling of message attachments

libpurple/protocols/facebook/api.c file | annotate | diff | comparison | revisions
libpurple/protocols/facebook/api.h file | annotate | diff | comparison | revisions
libpurple/protocols/facebook/http.c file | annotate | diff | comparison | revisions
libpurple/protocols/facebook/http.h file | annotate | diff | comparison | revisions
libpurple/protocols/facebook/json.c file | annotate | diff | comparison | revisions
libpurple/protocols/facebook/json.h file | annotate | diff | comparison | revisions
--- a/libpurple/protocols/facebook/api.c	Mon Aug 03 22:34:20 2015 -0400
+++ b/libpurple/protocols/facebook/api.c	Tue Aug 04 20:45:13 2015 -0400
@@ -306,7 +306,7 @@
 }
 
 static gboolean
-fb_api_json_chk(FbApi *api, gconstpointer data, gsize size, JsonNode **node)
+fb_api_json_chk(FbApi *api, gconstpointer data, gssize size, JsonNode **node)
 {
 	const gchar *str;
 	FbApiError errc = FB_API_ERROR_GENERAL;
@@ -458,7 +458,6 @@
 	gchar *val;
 	GList *keys;
 	GList *l;
-	gsize size;
 	GString *gstr;
 	PurpleHttpConnection *ret;
 	PurpleHttpRequest *req;
@@ -505,8 +504,8 @@
 		g_free(data);
 	}
 
-	data = fb_http_params_close(params, &size);
-	purple_http_request_set_contents(req, data, size);
+	data = fb_http_params_close(params, NULL);
+	purple_http_request_set_contents(req, data, -1);
 	ret = purple_http_request(priv->gc, req, info->callback, api);
 	purple_http_request_unref(req);
 
@@ -632,14 +631,41 @@
 	bldr = fb_json_bldr_new(JSON_NODE_OBJECT);
 	fb_json_bldr_add_int(bldr, "delta_batch_size", 125);
 	fb_json_bldr_add_int(bldr, "max_deltas_able_to_process", 1250);
-	fb_json_bldr_add_int(bldr, "sync_api_version", 2);
+	fb_json_bldr_add_int(bldr, "sync_api_version", 3);
 	fb_json_bldr_add_str(bldr, "encoding", "JSON");
 
 	if (priv->stoken == NULL) {
 		fb_json_bldr_add_int(bldr, "initial_titan_sequence_id",
 		                     priv->sid);
 		fb_json_bldr_add_str(bldr, "device_id", priv->did);
+		fb_json_bldr_add_int(bldr, "entity_fbid", priv->uid);
+
 		fb_json_bldr_obj_begin(bldr, "device_params");
+		fb_json_bldr_add_str(bldr, "animated_image_format", "GIF");
+
+		fb_json_bldr_obj_begin(bldr, "animated_image_sizes");
+		fb_json_bldr_add_str(bldr, "0", "9001x9001");
+		fb_json_bldr_obj_end(bldr);
+
+		fb_json_bldr_obj_begin(bldr, "image_sizes");
+		fb_json_bldr_add_str(bldr, "0", "9001x9001");
+		fb_json_bldr_obj_end(bldr);
+		fb_json_bldr_obj_end(bldr);
+
+		fb_json_bldr_obj_begin(bldr, "queue_params");
+		fb_json_bldr_add_str(bldr, "buzz_on_deltas_enabled", "false");
+
+		fb_json_bldr_obj_begin(bldr, "graphql_query_hashes");
+		fb_json_bldr_add_str(bldr, "xma_query_id", FB_API_QRYID_XMA);
+		fb_json_bldr_obj_end(bldr);
+
+		fb_json_bldr_obj_begin(bldr, "graphql_query_params");
+		fb_json_bldr_obj_begin(bldr, FB_API_QRYID_XMA);
+		fb_json_bldr_add_str(bldr, "xma_id", "<ID>");
+		fb_json_bldr_add_str(bldr, "small_preview_size", "9001");
+		fb_json_bldr_add_str(bldr, "large_preview_size", "9001");
+		fb_json_bldr_obj_end(bldr);
+		fb_json_bldr_obj_end(bldr);
 		fb_json_bldr_obj_end(bldr);
 
 		json = fb_json_bldr_close(bldr, JSON_NODE_OBJECT, NULL);
@@ -771,34 +797,160 @@
 	json_node_free(root);
 }
 
+static gchar *
+fb_api_message_parse_xma(FbApi *api, const gchar *json)
+{
+	const gchar *str;
+	FbHttpParams *params;
+	FbJsonValues *values;
+	gchar *ret;
+	GError *err = NULL;
+	JsonNode *node;
+	JsonNode *root;
+
+	if (!fb_api_json_chk(api, json, -1, &root)) {
+		return NULL;
+	}
+
+	node = fb_json_node_get_nth(root, 0);
+	values = fb_json_values_new(node);
+	fb_json_values_add(values, TRUE, "$.story_attachment.target"
+	                                  ".__type__.name");
+	fb_json_values_add(values, TRUE, "$.story_attachment.url");
+	fb_json_values_update(values, &err);
+
+	FB_API_ERROR_CHK(api, err,
+		fb_json_values_free(values);
+		json_node_free(root);
+		return NULL;
+	);
+
+	str = fb_json_values_next_str(values, NULL);
+
+	if (!purple_strequal(str, "ExternalUrl")) {
+		fb_util_debug_warning("Unknown XMA type %s", str);
+		fb_json_values_free(values);
+		json_node_free(root);
+		return NULL;
+	}
+
+	str = fb_json_values_next_str(values, NULL);
+	params = fb_http_params_new_parse(str, TRUE);
+	ret = fb_http_params_dup_str(params, "u", NULL);
+	fb_http_params_free(params);
+
+	fb_json_values_free(values);
+	json_node_free(root);
+	return ret;
+}
+
+static GSList *
+fb_api_message_parse_attach(FbApi *api, FbApiMessage *msg, GSList *msgs,
+                            JsonNode *root)
+{
+	const gchar *body = msg->text;
+	const gchar *str;
+	FbJsonValues *values;
+	gpointer mptr;
+
+	values = fb_json_values_new(root);
+	fb_json_values_add(values, FALSE, "$.imageMetadata.imageURIMap.0");
+	fb_json_values_add(values, FALSE, "$.xmaGraphQL");
+	fb_json_values_add(values, FALSE, "$.filename");
+	fb_json_values_set_array(values, "$.deltaNewMessage.attachments",
+	                         NULL);
+
+	while (fb_json_values_update(values, NULL)) {
+		if (!fb_json_values_successful(values)) {
+			continue;
+		}
+
+		msg->text = fb_json_values_next_str_dup(values, NULL);
+
+		if (msg->text != NULL) {
+			mptr = fb_api_message_dup(msg, FALSE);
+			msgs = g_slist_prepend(msgs, mptr);
+			continue;
+		}
+
+		str = fb_json_values_next_str(values, NULL);
+
+		if (str != NULL) {
+			msg->text = fb_api_message_parse_xma(api, str);
+
+			if (msg->text == NULL) {
+				continue;
+			}
+
+			if (purple_strequal(msg->text, body)) {
+				g_free(msg->text);
+				continue;
+			}
+
+			mptr = fb_api_message_dup(msg, FALSE);
+			msgs = g_slist_prepend(msgs, mptr);
+			continue;
+		}
+
+		str = fb_json_values_next_str(values, NULL);
+
+		if (G_UNLIKELY(str == NULL)) {
+			str = _("unknown attachment");
+		}
+
+		msg->text = g_strdup_printf("%s/%" FB_ID_FORMAT " [%s]",
+					    FB_API_URL_MESSAGES,
+					    msg->uid, str);
+		mptr = fb_api_message_dup(msg, FALSE);
+		msgs = g_slist_prepend(msgs, mptr);
+	}
+
+	fb_json_values_free(values);
+	return msgs;
+}
+
 static void
 fb_api_cb_publish_ms(FbApi *api, const GByteArray *pload)
 {
-	const gchar *str;
+	const gchar *data;
 	FbApiMessage msg;
 	FbApiPrivate *priv = api->priv;
+	FbHttpParams *params;
 	FbJsonValues *values;
 	FbThrift *thft;
+	gchar *json;
 	gchar *stoken;
 	GError *err = NULL;
+	gint64 id;
 	gpointer mptr;
+	GRegex *regex;
 	GSList *msgs = NULL;
-	guint i;
-	JsonArray *arr;
+	guint size;
 	JsonNode *root;
 	JsonNode *node;
 
 	thft = fb_thrift_new((GByteArray*) pload, 0, TRUE);
 	fb_thrift_read_str(thft, NULL);
-	i = fb_thrift_get_pos(thft);
+	size = fb_thrift_get_pos(thft);
 	g_object_unref(thft);
 
-	g_return_if_fail(i < pload->len);
+	g_return_if_fail(size < pload->len);
+
+	data = (gchar *) pload->data + size;
+	size = pload->len - size;
 
-	if (!fb_api_json_chk(api, pload->data + i, pload->len - i, &root)) {
+	/* Ugly hack to fix broken JSON from Facebook */
+	regex = g_regex_new("(\\d+)(:\")", 0, 0, &err);
+	json = g_regex_replace(regex, data, size, 0, "\"\\1\"\\2", 0, &err);
+	g_regex_unref(regex);
+	FB_API_ERROR_CHK(api, err, return);
+
+	if (!fb_api_json_chk(api, json, -1, &root)) {
+		g_free(json);
 		return;
 	}
 
+	g_free(json);
 	values = fb_json_values_new(root);
 	fb_json_values_add(values, FALSE, "$.lastIssuedSeqId");
 	fb_json_values_add(values, FALSE, "$.syncToken");
@@ -828,6 +980,7 @@
 	fb_json_values_add(values, FALSE, "$.deltaNewMessage.messageMetadata"
 	                                   ".threadKey.threadFbId");
 	fb_json_values_add(values, FALSE, "$.deltaNewMessage.body");
+	fb_json_values_add(values, FALSE, "$.deltaNewMessage.stickerId");
 	fb_json_values_set_array(values, "$.deltas", &err);
 
 	FB_API_ERROR_CHK(api, err,
@@ -856,22 +1009,20 @@
 			msgs = g_slist_prepend(msgs, mptr);
 		}
 
-		node = fb_json_values_get_root(values);
-		arr = fb_json_node_get_arr(node, "$.deltaNewMessage"
-		                                  ".attachments", NULL);
+		id = fb_json_values_next_int(values, 0);
 
-		if (G_UNLIKELY(arr == NULL)) {
+		if (id != 0) {
+			params = fb_http_params_new();
+			fb_http_params_set_int(params, "sticker_id", id);
+			msg.text = fb_http_params_close(params,
+			                                FB_API_URL_STICKER);
+			mptr = fb_api_message_dup(&msg, FALSE);
+			msgs = g_slist_prepend(msgs, mptr);
 			continue;
 		}
 
-		if (json_array_get_length(arr) > 0) {
-			str = _("* Non-Displayable Attachments *");
-			msg.text = g_strdup(str);
-			mptr = fb_api_message_dup(&msg, FALSE);
-			msgs = g_slist_prepend(msgs, mptr);
-		}
-
-		json_array_unref(arr);
+		node = fb_json_values_get_root(values);
+		msgs = fb_api_message_parse_attach(api, &msg, msgs, node);
 	}
 
 	msgs = g_slist_reverse(msgs);
@@ -1528,19 +1679,14 @@
 {
 	FbApi *api = data;
 	FbApiThread thrd;
-	GList *vals;
-	JsonNode *node = NULL;
+	JsonNode *node;
 	JsonNode *root;
-	JsonObject *obj;
 
 	if (!fb_api_http_chk(api, con, res, &root)) {
 		return;
 	}
 
-	obj = json_node_get_object(root);
-	vals = json_object_get_values(obj);
-	node = vals->data;
-	g_list_free(vals);
+	node = fb_json_node_get_nth(root, 0);
 
 	if (node == NULL) {
 		fb_api_error(api, FB_API_ERROR_GENERAL,
--- a/libpurple/protocols/facebook/api.h	Mon Aug 03 22:34:20 2015 -0400
+++ b/libpurple/protocols/facebook/api.h	Tue Aug 04 20:45:13 2015 -0400
@@ -32,25 +32,29 @@
 #include "id.h"
 #include "mqtt.h"
 
-#define FB_API_HOST    "https://api.facebook.com"
+#define FB_API_AHOST   "https://api.facebook.com"
 #define FB_API_BHOST   "https://b-api.facebook.com"
 #define FB_API_GHOST   "https://graph.facebook.com"
+#define FB_API_WHOST   "https://www.facebook.com"
 #define FB_API_AGENT   "Facebook App / " PACKAGE " / " VERSION
 #define FB_API_KEY     "256002347743983"
 #define FB_API_SECRET  "374e60f8b9bb6b8cbb30f78030438895"
 
 #define FB_API_CONTACTS_COUNT  "200"
 
-#define FB_API_URL_AUTH   FB_API_BHOST "/method/auth.login"
-#define FB_API_URL_GQL    FB_API_GHOST "/graphql"
-#define FB_API_URL_PARTS  FB_API_GHOST "/participants"
-#define FB_API_URL_THRDS  FB_API_GHOST "/me/threads"
-#define FB_API_URL_TOPIC  FB_API_HOST  "/method/messaging.setthreadname"
+#define FB_API_URL_AUTH     FB_API_BHOST "/method/auth.login"
+#define FB_API_URL_GQL      FB_API_GHOST "/graphql"
+#define FB_API_URL_MESSAGES FB_API_WHOST "/messages"
+#define FB_API_URL_PARTS    FB_API_GHOST "/participants"
+#define FB_API_URL_STICKER  FB_API_WHOST "/stickers/asset/"
+#define FB_API_URL_THRDS    FB_API_GHOST "/me/threads"
+#define FB_API_URL_TOPIC    FB_API_AHOST "/method/messaging.setthreadname"
 
 #define FB_API_QRYID_CONTACTS        "10153746900696729"
 #define FB_API_QRYID_CONTACTS_AFTER  "10153746900731729"
 #define FB_API_QRYID_THREAD_INFO     "10153813976871729"
 #define FB_API_QRYID_THREAD_LIST     "10153813976891729"
+#define FB_API_QRYID_XMA             "10153896863741729"
 
 #define FB_TYPE_API             (fb_api_get_type())
 #define FB_API(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), FB_TYPE_API, FbApi))
--- a/libpurple/protocols/facebook/http.c	Mon Aug 03 22:34:20 2015 -0400
+++ b/libpurple/protocols/facebook/http.c	Tue Aug 04 20:45:13 2015 -0400
@@ -122,7 +122,7 @@
 }
 
 gchar *
-fb_http_params_close(FbHttpParams *params, gsize *size)
+fb_http_params_close(FbHttpParams *params, const gchar *url)
 {
 	GHashTableIter iter;
 	gpointer key;
@@ -147,8 +147,9 @@
 		g_string_append_uri_escaped(ret, val, NULL, TRUE);
 	}
 
-	if (size != NULL) {
-		*size = ret->len;
+	if (url != NULL) {
+		g_string_prepend_c(ret, '?');
+		g_string_prepend(ret, url);
 	}
 
 	fb_http_params_free(params);
@@ -223,6 +224,16 @@
 	return fb_http_params_get(params, name, error);
 }
 
+gchar *
+fb_http_params_dup_str(FbHttpParams *params, const gchar *name,
+                       GError **error)
+{
+	const gchar *str;
+
+	str = fb_http_params_get(params, name, error);
+	return g_strdup(str);
+}
+
 static void
 fb_http_params_set(FbHttpParams *params, const gchar *name, gchar *value)
 {
--- a/libpurple/protocols/facebook/http.h	Mon Aug 03 22:34:20 2015 -0400
+++ b/libpurple/protocols/facebook/http.h	Tue Aug 04 20:45:13 2015 -0400
@@ -53,7 +53,7 @@
 fb_http_params_free(FbHttpParams *params);
 
 gchar *
-fb_http_params_close(FbHttpParams *params, gsize *size);
+fb_http_params_close(FbHttpParams *params, const gchar *url);
 
 gboolean
 fb_http_params_get_bool(FbHttpParams *params, const gchar *name,
@@ -71,6 +71,10 @@
 fb_http_params_get_str(FbHttpParams *params, const gchar *name,
                        GError **error);
 
+gchar *
+fb_http_params_dup_str(FbHttpParams *params, const gchar *name,
+                       GError **error);
+
 void
 fb_http_params_set_bool(FbHttpParams *params, const gchar *name,
 		        gboolean value);
--- a/libpurple/protocols/facebook/json.c	Mon Aug 03 22:34:20 2015 -0400
+++ b/libpurple/protocols/facebook/json.c	Tue Aug 04 20:45:13 2015 -0400
@@ -248,6 +248,21 @@
 	return ret;
 }
 
+JsonNode *
+fb_json_node_get_nth(JsonNode *root, guint n)
+{
+	GList *vals;
+	JsonNode *ret;
+	JsonObject *obj;
+
+	obj = json_node_get_object(root);
+	vals = json_object_get_values(obj);
+	ret = g_list_nth_data(vals, n);
+
+	g_list_free(vals);
+	return ret;
+}
+
 JsonArray *
 fb_json_node_get_arr(JsonNode *root, const gchar *expr, GError **error)
 {
@@ -466,6 +481,10 @@
 		value = l->data;
 		node = fb_json_node_get(root, value->expr, &err);
 
+		if (G_IS_VALUE(&value->value)) {
+			g_value_unset(&value->value);
+		}
+
 		if (err != NULL) {
 			if (value->required) {
 				fb_util_debug_error("%s", err->message);
@@ -476,10 +495,6 @@
 			continue;
 		}
 
-		if (G_IS_VALUE(&value->value)) {
-			g_value_unset(&value->value);
-		}
-
 		json_node_get_value(node, &value->value);
 		json_node_free(node);
 	}
--- a/libpurple/protocols/facebook/json.h	Mon Aug 03 22:34:20 2015 -0400
+++ b/libpurple/protocols/facebook/json.h	Tue Aug 04 20:45:13 2015 -0400
@@ -101,6 +101,9 @@
 JsonNode *
 fb_json_node_get(JsonNode *root, const gchar *expr, GError **error);
 
+JsonNode *
+fb_json_node_get_nth(JsonNode *root, guint n);
+
 JsonArray *
 fb_json_node_get_arr(JsonNode *root, const gchar *expr, GError **error);
 

mercurial