diff options
-rw-r--r-- | facebook/facebook-api.c | 230 | ||||
-rw-r--r-- | facebook/facebook-api.h | 28 | ||||
-rw-r--r-- | facebook/facebook.c | 12 |
3 files changed, 228 insertions, 42 deletions
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 7102a29..3079d0c 100644 --- a/facebook/facebook.c +++ b/facebook/facebook.c @@ -357,15 +357,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; } } |