summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2017-03-29 13:14:42 -0700
committerSean Whitton <spwhitton@spwhitton.name>2017-03-29 13:14:42 -0700
commit549d34be231d1c91904343b5e4060981ba0503e6 (patch)
tree8587b5b8500d86dbf9f11c637818f0632a684538
parent737f53e36addbd6679584a898b65eabc3529c08f (diff)
parent797b620055bbc7f5c901a1255861a905684449f4 (diff)
downloadbitlbee-facebook-549d34be231d1c91904343b5e4060981ba0503e6.tar.gz
Merge tag 'v1.1.0'
-rw-r--r--ChangeLog27
-rw-r--r--configure.ac2
-rw-r--r--facebook/facebook-api.c230
-rw-r--r--facebook/facebook-api.h28
-rw-r--r--facebook/facebook.c12
5 files changed, 256 insertions, 43 deletions
diff --git a/ChangeLog b/ChangeLog
index 6df0dc3..8f25fda 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,2 +1,29 @@
+bitlbee-facebook-1.1.0 (2017-01-29):
+ - Fix connection errors after facebook discontinued support for old versions
+ of facebook messenger for android. While most of the protocol implementation
+ was already above that version, there was a subtle change that broke fetching
+ of sync_sequence_id, and the previously empty MQTT user agent string is now
+ considered an old version too (#138)
+ - Fix receiving topic/groupchat membership events (#119)
+ - Fix creating channels, inviting, kicking members (#120)
+ - Fix setting channel topics (#121)
+ - Fix contacts pagination for accounts with more than 500 friends (#74)
+ - Fix chats with only two members including self, like the Marketplace ones
+ - Fix crash on some kinds of SSL connection failure (#82)
+ - Fix crash when glib is compiled with G_ENABLE_DEBUG
+ - Deduplicate echoes of sent messages (#76)
+ - Set a non-empty HTTP user agent for api/graphql queries, since the empty
+ user agent often gets flagged for suspicious activity (#108)
+ - Prevent disconnections due to contacts with incomplete profiles (#89)
+ - Prevent disconnections on invalid attachment errors (#73)
+ - Prevent disconnections when trying to join a chat after being kicked (#244)
+ - Use FetchContactsDeltaQuery to incrementally sync contacts. Uses less
+ traffic, can be done more often and fixes issues with non-friends (#116)
+ - Parse URL attachments generated by the android share feature (#97)
+ - Add `mark_read_reply` setting to only mark messages as read on reply (#69)
+ - Add support for the bitlbee 3.5 plugin information API (bitlbee 3.4 is
+ still supported by this version, but will be dropped in the next one)
+ - Improve url comparison to avoid showing urls twice on attachments
+
bitlbee-facebook-1.0.0 (2016-01-16):
- Initial release
diff --git a/configure.ac b/configure.ac
index 031ec7e..e27bb58 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,7 +15,7 @@
AC_INIT(
[bitlbee-facebook],
- [1.0.0],
+ [1.1.0],
[https://github.com/bitlbee/bitlbee-facebook/issues],
[bitlbee-facebook],
[https://github.com/bitlbee/bitlbee-facebook],
diff --git a/facebook/facebook-api.c b/facebook/facebook-api.c
index d9d4486..b318c62 100644
--- a/facebook/facebook-api.c
+++ b/facebook/facebook-api.c
@@ -370,9 +370,9 @@ fb_api_class_init(FbApiClass *klass)
G_SIGNAL_ACTION,
0,
NULL, NULL,
- fb_marshal_VOID__OBJECT,
+ fb_marshal_VOID__POINTER,
G_TYPE_NONE,
- 1, G_TYPE_ERROR);
+ 1, G_TYPE_POINTER);
/**
* FbApi::events:
@@ -793,6 +793,7 @@ fb_api_http_query(FbApi *api, gint64 query, JsonBuilder *builder,
case FB_API_QUERY_THREAD:
name = "ThreadQuery";
break;
+ case FB_API_QUERY_SEQ_ID:
case FB_API_QUERY_THREADS:
name = "ThreadListQuery";
break;
@@ -877,7 +878,7 @@ fb_api_cb_mqtt_open(FbMqtt *mqtt, gpointer data)
/* Write the information string */
fb_thrift_write_field(thft, FB_THRIFT_TYPE_STRING, 2, 1);
- fb_thrift_write_str(thft, "");
+ fb_thrift_write_str(thft, FB_API_MQTT_AGENT);
/* Write the UNKNOWN ("cp"?) */
fb_thrift_write_field(thft, FB_THRIFT_TYPE_I64, 3, 2);
@@ -1078,7 +1079,7 @@ fb_api_cb_mqtt_connect(FbMqtt *mqtt, gpointer data)
if (priv->sid == 0) {
bldr = fb_json_bldr_new(JSON_NODE_OBJECT);
fb_json_bldr_add_str(bldr, "1", "0");
- fb_api_http_query(api, FB_API_QUERY_THREADS, bldr, fb_api_cb_seqid);
+ fb_api_http_query(api, FB_API_QUERY_SEQ_ID, bldr, fb_api_cb_seqid);
} else {
fb_api_connect_queue(api);
}
@@ -1390,7 +1391,7 @@ fb_api_message_parse_attach(FbApi *api, const gchar *mid, FbApiMessage *msg,
values = fb_json_values_new(root);
fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE, "$.xmaGraphQL");
fb_json_values_add(values, FB_JSON_TYPE_INT, FALSE, "$.fbid");
- fb_json_values_set_array(values, FALSE, "$.deltaNewMessage.attachments");
+ fb_json_values_set_array(values, FALSE, "$.attachments");
while (fb_json_values_update(values, &err)) {
str = fb_json_values_next_str(values, NULL);
@@ -1432,25 +1433,39 @@ fb_api_message_parse_attach(FbApi *api, const gchar *mid, FbApiMessage *msg,
return msgs;
}
+static GSList *
+fb_api_cb_publish_ms_new_message(FbApi *api, JsonNode *root, GSList *msgs, GError **error);
+
+static GSList *
+fb_api_cb_publish_ms_event(FbApi *api, JsonNode *root, GSList *events, FbApiEventType type, GError **error);
+
static void
fb_api_cb_publish_ms(FbApi *api, GByteArray *pload)
{
- const gchar *body;
const gchar *data;
- const gchar *str;
- FbApiMessage *dmsg;
- FbApiMessage msg;
FbApiPrivate *priv = api->priv;
- FbId id;
- FbId oid;
FbJsonValues *values;
FbThrift *thft;
gchar *stoken;
GError *err = NULL;
+ GList *elms, *l;
GSList *msgs = NULL;
+ GSList *events = NULL;
guint size;
JsonNode *root;
JsonNode *node;
+ JsonArray *arr;
+
+ static const struct {
+ const gchar *member;
+ FbApiEventType type;
+ gboolean is_message;
+ } event_types[] = {
+ {"deltaNewMessage", 0, 1},
+ {"deltaThreadName", FB_API_EVENT_TYPE_THREAD_TOPIC, 0},
+ {"deltaParticipantsAddedToGroupThread", FB_API_EVENT_TYPE_THREAD_USER_ADDED, 0},
+ {"deltaParticipantLeftGroupThread", FB_API_EVENT_TYPE_THREAD_USER_REMOVED, 0},
+ };
/* Read identifier string (for Facebook employees) */
thft = fb_thrift_new(pload, 0);
@@ -1489,39 +1504,101 @@ fb_api_cb_publish_ms(FbApi *api, GByteArray *pload)
return;
}
+ arr = fb_json_node_get_arr(root, "$.deltas", NULL);
+ elms = json_array_get_elements(arr);
+
+ for (l = elms; l != NULL; l = l->next) {
+ guint i = 0;
+ JsonObject *o = json_node_get_object(l->data);
+
+ for (i = 0; i < G_N_ELEMENTS(event_types); i++) {
+ if ((node = json_object_get_member(o, event_types[i].member))) {
+ if (event_types[i].is_message) {
+ msgs = fb_api_cb_publish_ms_new_message(
+ api, node, msgs, &err
+ );
+ } else {
+ events = fb_api_cb_publish_ms_event(
+ api, node, events, event_types[i].type, &err
+ );
+ }
+ }
+ }
+
+ if (G_UNLIKELY(err != NULL)) {
+ break;
+ }
+ }
+
+ g_list_free(elms);
+ json_array_unref(arr);
+
+ if (G_LIKELY(err == NULL)) {
+ if (msgs) {
+ msgs = g_slist_reverse(msgs);
+ g_signal_emit_by_name(api, "messages", msgs);
+ }
+
+ if (events) {
+ events = g_slist_reverse(events);
+ g_signal_emit_by_name(api, "events", events);
+ }
+ } else {
+ fb_api_error_emit(api, err);
+ }
+
+ g_slist_free_full(msgs, (GDestroyNotify) fb_api_message_free);
+ g_slist_free_full(events, (GDestroyNotify) fb_api_event_free);
+ json_node_free(root);
+}
+
+
+static GSList *
+fb_api_cb_publish_ms_new_message(FbApi *api, JsonNode *root, GSList *msgs, GError **error)
+{
+ const gchar *body;
+ const gchar *str;
+ GError *err = NULL;
+ FbApiPrivate *priv = api->priv;
+ FbApiMessage *dmsg;
+ FbApiMessage msg;
+ FbId id;
+ FbId oid;
+ FbJsonValues *values;
+ JsonNode *node;
+
values = fb_json_values_new(root);
fb_json_values_add(values, FB_JSON_TYPE_INT, FALSE,
- "$.deltaNewMessage.messageMetadata.offlineThreadingId");
+ "$.messageMetadata.offlineThreadingId");
fb_json_values_add(values, FB_JSON_TYPE_INT, FALSE,
- "$.deltaNewMessage.messageMetadata.actorFbId");
+ "$.messageMetadata.actorFbId");
fb_json_values_add(values, FB_JSON_TYPE_INT, FALSE,
- "$.deltaNewMessage.messageMetadata"
+ "$.messageMetadata"
".threadKey.otherUserFbId");
fb_json_values_add(values, FB_JSON_TYPE_INT, FALSE,
- "$.deltaNewMessage.messageMetadata"
+ "$.messageMetadata"
".threadKey.threadFbId");
fb_json_values_add(values, FB_JSON_TYPE_INT, FALSE,
- "$.deltaNewMessage.messageMetadata.timestamp");
+ "$.messageMetadata.timestamp");
fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE,
- "$.deltaNewMessage.body");
+ "$.body");
fb_json_values_add(values, FB_JSON_TYPE_INT, FALSE,
- "$.deltaNewMessage.stickerId");
+ "$.stickerId");
fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE,
- "$.deltaNewMessage.messageMetadata.messageId");
- fb_json_values_set_array(values, TRUE, "$.deltas");
+ "$.messageMetadata.messageId");
- while (fb_json_values_update(values, &err)) {
+ if (fb_json_values_update(values, &err)) {
id = fb_json_values_next_int(values, 0);
/* Ignore everything but new messages */
if (id == 0) {
- continue;
+ goto beach;
}
/* Ignore sequential duplicates */
if (id == priv->lastmid) {
fb_util_debug_info("Ignoring duplicate %" FB_ID_FORMAT, id);
- continue;
+ goto beach;
}
priv->lastmid = id;
@@ -1557,7 +1634,7 @@ fb_api_cb_publish_ms(FbApi *api, GByteArray *pload)
str = fb_json_values_next_str(values, NULL);
if (str == NULL) {
- continue;
+ goto beach;
}
node = fb_json_values_get_root(values);
@@ -1565,20 +1642,97 @@ fb_api_cb_publish_ms(FbApi *api, GByteArray *pload)
&err);
if (G_UNLIKELY(err != NULL)) {
- break;
+ g_propagate_error(error, err);
+ goto beach;
}
}
- if (G_LIKELY(err == NULL)) {
- msgs = g_slist_reverse(msgs);
- g_signal_emit_by_name(api, "messages", msgs);
- } else {
- fb_api_error_emit(api, err);
+beach:
+ g_object_unref(values);
+ return msgs;
+}
+
+static GSList *
+fb_api_cb_publish_ms_event(FbApi *api, JsonNode *root, GSList *events, FbApiEventType type, GError **error)
+{
+ FbApiEvent *event;
+ FbJsonValues *values = NULL;
+ FbJsonValues *values_inner = NULL;
+ GError *err = NULL;
+
+ values = fb_json_values_new(root);
+ fb_json_values_add(values, FB_JSON_TYPE_INT, FALSE,
+ "$.messageMetadata.threadKey.threadFbId");
+ fb_json_values_add(values, FB_JSON_TYPE_INT, FALSE,
+ "$.messageMetadata.actorFbId");
+
+ switch (type) {
+ case FB_API_EVENT_TYPE_THREAD_TOPIC:
+ fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE,
+ "$.name");
+ break;
+
+ case FB_API_EVENT_TYPE_THREAD_USER_ADDED:
+ values_inner = fb_json_values_new(root);
+
+ fb_json_values_add(values_inner, FB_JSON_TYPE_INT, FALSE,
+ "$.userFbId");
+
+ /* use the text field for the full name */
+ fb_json_values_add(values_inner, FB_JSON_TYPE_STR, FALSE,
+ "$.fullName");
+
+ fb_json_values_set_array(values_inner, FALSE,
+ "$.addedParticipants");
+ break;
+
+ case FB_API_EVENT_TYPE_THREAD_USER_REMOVED:
+ fb_json_values_add(values, FB_JSON_TYPE_INT, FALSE,
+ "$.leftParticipantFbId");
+
+ /* use the text field for the kick message */
+ fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE,
+ "$.messageMetadata.adminText");
+ break;
+ }
+
+ fb_json_values_update(values, &err);
+
+ event = fb_api_event_dup(NULL, FALSE);
+ event->type = type;
+ event->tid = fb_json_values_next_int(values, 0);
+ event->uid = fb_json_values_next_int(values, 0);
+
+ if (type == FB_API_EVENT_TYPE_THREAD_TOPIC) {
+ event->text = fb_json_values_next_str_dup(values, NULL);
+ } else if (type == FB_API_EVENT_TYPE_THREAD_USER_REMOVED) {
+ /* overwrite actor with subject */
+ event->uid = fb_json_values_next_int(values, 0);
+ event->text = fb_json_values_next_str_dup(values, NULL);
+ } else if (type == FB_API_EVENT_TYPE_THREAD_USER_ADDED) {
+
+ while (fb_json_values_update(values_inner, &err)) {
+ FbApiEvent *devent = fb_api_event_dup(event, FALSE);
+
+ devent->uid = fb_json_values_next_int(values_inner, 0);
+ devent->text = fb_json_values_next_str_dup(values_inner, NULL);
+
+ events = g_slist_prepend(events, devent);
+ }
+ fb_api_event_free(event);
+ event = NULL;
+ g_object_unref(values_inner);
}
- g_slist_free_full(msgs, (GDestroyNotify) fb_api_message_free);
g_object_unref(values);
- json_node_free(root);
+
+ if (G_UNLIKELY(err != NULL)) {
+ g_propagate_error(error, err);
+ } else if (event) {
+ events = g_slist_prepend(events, event);
+ }
+
+ return events;
}
static void
@@ -2850,7 +3004,7 @@ fb_api_cb_thread_create(FbHttpRequest *req, gpointer data)
}
values = fb_json_values_new(root);
- fb_json_values_add(values, FB_JSON_TYPE_STR, TRUE, "$.thread_fbid");
+ fb_json_values_add(values, FB_JSON_TYPE_STR, TRUE, "$.id");
fb_json_values_update(values, &err);
FB_API_ERROR_EMIT(api, err,
@@ -2897,8 +3051,8 @@ fb_api_thread_create(FbApi *api, GSList *uids)
json = fb_json_bldr_close(bldr, JSON_NODE_ARRAY, NULL);
prms = fb_http_values_new();
- fb_http_values_set_str(prms, "to", json);
- fb_api_http_req(api, FB_API_URL_THREADS, "createThread", "POST", prms,
+ fb_http_values_set_str(prms, "recipients", json);
+ fb_api_http_req(api, FB_API_URL_THREADS, "createGroup", "POST", prms,
fb_api_cb_thread_create);
g_free(json);
}
@@ -2919,7 +3073,7 @@ fb_api_thread_invite(FbApi *api, FbId tid, FbId uid)
prms = fb_http_values_new();
fb_http_values_set_str(prms, "to", json);
- fb_http_values_set_strf(prms, "id", "t_id.%" FB_ID_FORMAT, tid);
+ fb_http_values_set_strf(prms, "id", "t_%" FB_ID_FORMAT, tid);
fb_api_http_req(api, FB_API_URL_PARTS, "addMembers", "POST", prms,
fb_api_cb_http_bool);
g_free(json);
@@ -2937,7 +3091,7 @@ fb_api_thread_remove(FbApi *api, FbId tid, FbId uid)
priv = api->priv;
prms = fb_http_values_new();
- fb_http_values_set_strf(prms, "id", "t_id.%" FB_ID_FORMAT, tid);
+ fb_http_values_set_strf(prms, "id", "t_%" FB_ID_FORMAT, tid);
if (uid == 0) {
uid = priv->uid;
@@ -2962,7 +3116,7 @@ fb_api_thread_topic(FbApi *api, FbId tid, const gchar *topic)
prms = fb_http_values_new();
fb_http_values_set_str(prms, "name", topic);
- fb_http_values_set_strf(prms, "tid", "t_id.%" FB_ID_FORMAT, tid);
+ fb_http_values_set_int(prms, "tid", tid);
fb_api_http_req(api, FB_API_URL_TOPIC, "setThreadName",
"messaging.setthreadname", prms,
fb_api_cb_http_bool);
diff --git a/facebook/facebook-api.h b/facebook/facebook-api.h
index b6a2812..fa38d3d 100644
--- a/facebook/facebook-api.h
+++ b/facebook/facebook-api.h
@@ -96,6 +96,22 @@
#define FB_API_AGENT "Facebook plugin / BitlBee / " PACKAGE_VERSION
/**
+ * FB_API_MQTT_AGENT
+ *
+ * The client information string sent in the MQTT CONNECT message
+ *
+ * We announce ourselves as compatible with Orca-Android 38.0 since that's the
+ * closest version to the last major protocol update. Some parts use older
+ * features, some parts use newer ones.
+ *
+ * Fun fact: this version sends old-style MQIsdp CONNECT messages for the first
+ * connection, with JSON payloads instead of compressed thrift.
+ */
+
+#define FB_API_MQTT_AGENT FB_API_AGENT " [FBAN/Orca-Android;FBAV/38.0.0.22.155;FBBV/14477681]"
+
+
+/**
* FB_API_URL_ATTACH:
*
* The URL for attachment URL requests.
@@ -136,7 +152,7 @@
*
* The URL for thread management requests.
*/
-#define FB_API_URL_THREADS FB_API_GHOST "/me/threads"
+#define FB_API_URL_THREADS FB_API_GHOST "/me/group_threads"
/**
* FB_API_URL_TOPIC:
@@ -273,6 +289,16 @@
#define FB_API_QUERY_THREADS 10153919752026729
/**
+ * FB_API_QUERY_SEQ_ID:
+ *
+ * A variant of ThreadListQuery with sequence ID
+ *
+ * TODO: parameters.
+ */
+
+#define FB_API_QUERY_SEQ_ID 10155268192741729
+
+/**
* FB_API_QUERY_XMA:
*
* The query hash for the `XMAQuery`.
diff --git a/facebook/facebook.c b/facebook/facebook.c
index 29a67e9..995ad59 100644
--- a/facebook/facebook.c
+++ b/facebook/facebook.c
@@ -353,15 +353,21 @@ fb_cb_api_events(FbApi *api, GSList *events, gpointer data)
case FB_API_EVENT_TYPE_THREAD_USER_ADDED:
if (bee_user_by_handle(ic->bee, ic, uid) == NULL) {
- g_hash_table_insert(fetch, &event->tid, event);
- break;
+ if (event->text) {
+ bee_user_new(ic->bee, ic, uid, BEE_USER_LOCAL);
+ imcb_buddy_nick_hint(ic, uid, event->text);
+ imcb_rename_buddy(ic, uid, event->text);
+ } else {
+ g_hash_table_insert(fetch, &event->tid, event);
+ break;
+ }
}
imcb_chat_add_buddy(gc, uid);
break;
case FB_API_EVENT_TYPE_THREAD_USER_REMOVED:
- imcb_chat_remove_buddy(gc, uid, NULL);
+ imcb_chat_remove_buddy(gc, uid, event->text);
break;
}
}