diff options
Diffstat (limited to 'facebook')
| -rw-r--r-- | facebook/facebook-api.c | 187 | ||||
| -rw-r--r-- | facebook/facebook-api.h | 24 | ||||
| -rw-r--r-- | facebook/facebook.c | 82 | ||||
| -rw-r--r-- | facebook/marshaller.list | 1 | 
4 files changed, 242 insertions, 52 deletions
| diff --git a/facebook/facebook-api.c b/facebook/facebook-api.c index 184cb89..1237861 100644 --- a/facebook/facebook-api.c +++ b/facebook/facebook-api.c @@ -61,6 +61,7 @@ struct _FbApiPrivate      gboolean invisible;      guint unread;      FbId lastmid; +    gchar *contacts_delta;  };  struct _FbApiData @@ -81,6 +82,9 @@ fb_api_message_send(FbApi *api, FbApiMessage *msg);  static void  fb_api_sticker(FbApi *api, FbId sid, FbApiMessage *msg); +void +fb_api_contacts_delta(FbApi *api, const gchar *delta_cursor); +  G_DEFINE_TYPE(FbApi, fb_api, G_TYPE_OBJECT);  static void @@ -174,6 +178,7 @@ fb_api_dispose(GObject *obj)      g_free(priv->did);      g_free(priv->stoken);      g_free(priv->token); +    g_free(priv->contacts_delta);  }  static void @@ -336,6 +341,23 @@ fb_api_class_init(FbApiClass *klass)                   2, G_TYPE_POINTER, G_TYPE_BOOLEAN);      /** +     * FbApi::contacts-delta: +     * @api: The #FbApi. +     * @added: The #GSList of added #FbApiUser's. +     * @removed: The #GSList of strings with removed user ids. +     * +     * Like 'contacts', but only the deltas. +     */ +    g_signal_new("contacts-delta", +                 G_TYPE_FROM_CLASS(klass), +                 G_SIGNAL_ACTION, +                 0, +                 NULL, NULL, +                 fb_marshal_VOID__POINTER_POINTER, +                 G_TYPE_NONE, +                 2, G_TYPE_POINTER, G_TYPE_POINTER); + +    /**       * FbApi::error:       * @api: The #FbApi.       * @error: The #GError. @@ -762,6 +784,9 @@ fb_api_http_query(FbApi *api, gint64 query, JsonBuilder *builder,      case FB_API_QUERY_CONTACTS_AFTER:          name = "FetchContactsFullWithAfterQuery";          break; +    case FB_API_QUERY_CONTACTS_DELTA: +        name = "FetchContactsDeltaQuery"; +        break;      case FB_API_QUERY_STICKER:          name = "FetchStickersWithPreviewsQuery";          break; @@ -2001,24 +2026,15 @@ fb_api_contact(FbApi *api, FbId uid)      fb_api_http_query(api, FB_API_QUERY_CONTACT, bldr, fb_api_cb_contact);  } -static void -fb_api_cb_contacts(FbHttpRequest *req, gpointer data) +static GSList * +fb_api_cb_contacts_nodes(FbApi *api, JsonNode *root, GSList *users)  { -    const gchar *cursor;      const gchar *str; -    FbApi *api = data;      FbApiPrivate *priv = api->priv;      FbApiUser *user;      FbId uid;      FbJsonValues *values; -    gboolean complete;      GError *err = NULL; -    GSList *users = NULL; -    JsonNode *root; - -    if (!fb_api_http_chk(api, req, &root)) { -        return; -    }      values = fb_json_values_new(root);      fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE, @@ -2029,8 +2045,10 @@ fb_api_cb_contacts(FbHttpRequest *req, gpointer data)                         "$.structured_name.text");      fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE,                         "$.hugePictureUrl.uri"); -    fb_json_values_set_array(values, FALSE, "$.viewer.messenger_contacts" -                                             ".nodes"); + +    if (JSON_NODE_TYPE(root) == JSON_NODE_ARRAY) { +        fb_json_values_set_array(values, FALSE, "$"); +    }      while (fb_json_values_update(values, &err)) {          str = fb_json_values_next_str(values, "0"); @@ -2051,20 +2069,126 @@ fb_api_cb_contacts(FbHttpRequest *req, gpointer data)          user->csum = fb_api_user_icon_checksum(user->icon);          users = g_slist_prepend(users, user); + +        if (JSON_NODE_TYPE(root) != JSON_NODE_ARRAY) { +            break; +        }      }      g_object_unref(values); -    values = fb_json_values_new(root); +    return users; +} + +/* base64(contact:<our id>:<their id>:<whatever>) */ +static GSList * +fb_api_cb_contacts_parse_removed(FbApi *api, JsonNode *node, GSList *users) +{ +    gsize len; +    char **split; +    guchar *decoded = g_base64_decode(json_node_get_string(node), &len); + +    g_return_val_if_fail(decoded[len] == '\0', users); +    g_return_val_if_fail(len == strlen(decoded), users); +    g_return_val_if_fail(g_str_has_prefix(decoded, "contact:"), users); + +    split = g_strsplit_set(decoded, ":", 4); + +    g_return_val_if_fail(g_strv_length(split) == 4, users); + +    users = g_slist_prepend(users, g_strdup(split[2])); + +    g_strfreev(split); +    g_free(decoded); + +    return users; +} + +static void +fb_api_cb_contacts(FbHttpRequest *req, gpointer data) +{ +    const gchar *cursor; +    const gchar *delta_cursor; +    const gchar *str; +    FbApi *api = data; +    FbApiPrivate *priv = api->priv; +    FbApiUser *user; +    FbId uid; +    FbJsonValues *values; +    gboolean complete; +    gboolean is_delta; +    GError *err = NULL; +    GList *l; +    GSList *users = NULL; +    JsonNode *root; +    JsonNode *croot; +    JsonNode *node; + +    if (!fb_api_http_chk(api, req, &root)) { +        return; +    } + +    croot = fb_json_node_get(root, "$.viewer.messenger_contacts.deltas", NULL); +    is_delta = (croot != NULL); + +    if (!is_delta) { +        croot = fb_json_node_get(root, "$.viewer.messenger_contacts", NULL); +        node = fb_json_node_get(croot, "$.nodes", NULL); +        users = fb_api_cb_contacts_nodes(api, node, users); +        json_node_free(node); + +    } else { +        GSList *added = NULL; +        GSList *removed = NULL; +        JsonArray *arr = fb_json_node_get_arr(croot, "$.nodes", NULL); +        GList *elms = json_array_get_elements(arr); + +        for (l = elms; l != NULL; l = l->next) { +            if (node = fb_json_node_get(l->data, "$.added", NULL)) { +                added = fb_api_cb_contacts_nodes(api, node, added); +                json_node_free(node); +            } + +            if (node = fb_json_node_get(l->data, "$.removed", NULL)) { +                removed = fb_api_cb_contacts_parse_removed(api, node, removed); +                json_node_free(node); +            } +        } + +        g_signal_emit_by_name(api, "contacts-delta", added, removed); + +        g_slist_free_full(added, (GDestroyNotify) fb_api_user_free); +        g_slist_free_full(removed, (GDestroyNotify) g_free); + +        g_list_free(elms); +        json_array_unref(arr); +    } + +    values = fb_json_values_new(croot); +    fb_json_values_add(values, FB_JSON_TYPE_BOOL, FALSE, +                       "$.page_info.has_next_page");      fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE, -                       "$.viewer.messenger_contacts.page_info.end_cursor"); +                       "$.page_info.delta_cursor"); +    fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE, +                       "$.page_info.end_cursor");      fb_json_values_update(values, NULL); +    complete = !fb_json_values_next_bool(values, FALSE); + +    delta_cursor = fb_json_values_next_str(values, NULL); +      cursor = fb_json_values_next_str(values, NULL);      if (G_UNLIKELY(err == NULL)) { -        complete = (cursor == NULL); -        g_signal_emit_by_name(api, "contacts", users, complete); + +        if (is_delta || complete) { +            g_free(priv->contacts_delta); +            priv->contacts_delta = g_strdup(is_delta ? cursor : delta_cursor); +        } + +        if (users) { +            g_signal_emit_by_name(api, "contacts", users, complete); +        }          if (!complete) {              fb_api_contacts_after(api, cursor); @@ -2075,14 +2199,25 @@ fb_api_cb_contacts(FbHttpRequest *req, gpointer data)      g_slist_free_full(users, (GDestroyNotify) fb_api_user_free);      g_object_unref(values); + +    json_node_free(croot);      json_node_free(root);  }  void  fb_api_contacts(FbApi *api)  { +    FbApiPrivate *priv;      JsonBuilder *bldr; +    g_return_if_fail(FB_IS_API(api)); +    priv = api->priv; + +    if (priv->contacts_delta) { +        fb_api_contacts_delta(api, priv->contacts_delta); +        return; +    } +      bldr = fb_json_bldr_new(JSON_NODE_OBJECT);      fb_json_bldr_arr_begin(bldr, "0");      fb_json_bldr_add_str(bldr, NULL, "user"); @@ -2110,6 +2245,24 @@ fb_api_contacts_after(FbApi *api, const gchar *cursor)  }  void +fb_api_contacts_delta(FbApi *api, const gchar *delta_cursor) +{ +    JsonBuilder *bldr; + +    bldr = fb_json_bldr_new(JSON_NODE_OBJECT); + +    fb_json_bldr_add_str(bldr, "0", delta_cursor); + +    fb_json_bldr_arr_begin(bldr, "1"); +    fb_json_bldr_add_str(bldr, NULL, "user"); +    fb_json_bldr_arr_end(bldr); + +    fb_json_bldr_add_str(bldr, "2", G_STRINGIFY(FB_API_CONTACTS_COUNT)); +    fb_api_http_query(api, FB_API_QUERY_CONTACTS_DELTA, bldr, +                      fb_api_cb_contacts); +} + +void  fb_api_connect(FbApi *api, gboolean invisible)  {      FbApiPrivate *priv; diff --git a/facebook/facebook-api.h b/facebook/facebook-api.h index ac4cc59..cb7467f 100644 --- a/facebook/facebook-api.h +++ b/facebook/facebook-api.h @@ -163,10 +163,8 @@   *   2: big_img_size   *   3: huge_img_size   *   4: small_img_size - *   5: low_res_cover_size - *   6: media_type   */ -#define FB_API_QUERY_CONTACTS  10153856456271729 +#define FB_API_QUERY_CONTACTS  10154444360806729  /**   * FB_API_QUERY_CONTACTS_AFTER: @@ -180,10 +178,24 @@   *   3: big_img_size   *   4: huge_img_size   *   5: small_img_size - *   6: low_res_cover_size - *   7: media_type   */ -#define FB_API_QUERY_CONTACTS_AFTER  10153856456281729 +#define FB_API_QUERY_CONTACTS_AFTER  10154444360816729 + + +/** + * FB_API_QUERY_CONTACTS_DELTA: + * + * The query hash for the `FetchContactsDeltaQuery`. + * + * Key mapping: + *   0: after + *   1: profile_types + *   2: limit + *   3: big_img_size + *   4: huge_img_size + *   5: small_img_size + */ +#define FB_API_QUERY_CONTACTS_DELTA  10154444360801729  /**   * FB_API_QUERY_STICKER: diff --git a/facebook/facebook.c b/facebook/facebook.c index 15dd6c6..fb7e2b6 100644 --- a/facebook/facebook.c +++ b/facebook/facebook.c @@ -202,16 +202,32 @@ fb_cb_sync_contacts(gpointer data, gint fd, b_input_condition cond)  }  static void +fb_sync_contacts_add_timeout(FbData *fata) +{ +    gint sync; +    struct im_connection *ic = fb_data_get_connection(fata); +    account_t *acct = ic->acc; + +    sync = set_getint(&acct->set, "sync_interval"); + +    if (sync < 1) { +        set_setint(&acct->set, "sync_interval", 1); +        sync = 1; +    } + +    sync *= 60 * 1000; +    fb_data_add_timeout(fata, "sync-contacts", sync, fb_cb_sync_contacts, +                        fata); +} + +static void  fb_cb_api_contacts(FbApi *api, GSList *users, gboolean complete, gpointer data)  { -    account_t *acct; -    bee_user_t *bu;      FbApiUser *user;      FbData *fata = data;      FbId muid;      FbBuddyData *fbd;      gchar uid[FB_ID_STRMAX]; -    gint sync;      GSList *l;      GValue val = G_VALUE_INIT;      struct im_connection *ic; @@ -244,40 +260,44 @@ fb_cb_api_contacts(FbApi *api, GSList *users, gboolean complete, gpointer data)          return;      } -    l = ic->bee->users; +    if (!(ic->flags & OPT_LOGGED_IN)) { +        imcb_log(ic, "Connecting"); +        fb_api_connect(api, FALSE); +    } -    while (l != NULL) { -        bu = l->data; -        l = l->next; +    fb_sync_contacts_add_timeout(fata); +} -        if (bu->ic != ic) { -            continue; -        } +static void +fb_cb_api_contacts_delta(FbApi *api, GSList *added, GSList *removed, gpointer data) +{ +    bee_user_t *bu; +    FbApiUser *user; +    FbData *fata = data; +    gchar uid[FB_ID_STRMAX]; +    GSList *l; +    struct im_connection *ic; -        fbd = bu->data; -        if (fbd->flags.new_buddy) { -            fbd->flags.new_buddy = FALSE; -        } else { -            imcb_remove_buddy(ic, bu->handle, NULL); -        } -    } +    ic = fb_data_get_connection(fata); -    if (!(ic->flags & OPT_LOGGED_IN)) { -        imcb_log(ic, "Connecting"); -        fb_api_connect(api, FALSE); +    for (l = added; l != NULL; l = l->next) { +        user = l->data; +        FB_ID_TO_STR(user->uid, uid); + +        imcb_add_buddy(ic, uid, NULL); +        imcb_buddy_nick_hint(ic, uid, user->name); +        imcb_rename_buddy(ic, uid, user->name);      } -    acct = ic->acc; -    sync = set_getint(&acct->set, "sync_interval"); +    for (l = removed; l != NULL; l = l->next) { +        bu = imcb_buddy_by_handle(ic, l->data); -    if (sync < 5) { -        set_setint(&acct->set, "sync_interval", 5); -        sync = 5; +        if (bu) { +            imcb_remove_buddy(ic, bu->handle, NULL); +        }      } -    sync *= 60 * 1000; -    fb_data_add_timeout(fata, "sync-contacts", sync, fb_cb_sync_contacts, -                        fata); +    fb_sync_contacts_add_timeout(fata);  }  static void @@ -745,7 +765,7 @@ fb_init(account_t *acct)      set_add(&acct->set, "mark_read", "false", fb_eval_mark_read, acct);      set_add(&acct->set, "mark_read_reply", "false", set_eval_bool, acct);      set_add(&acct->set, "show_unread", "false", set_eval_bool, acct); -    set_add(&acct->set, "sync_interval", "30", set_eval_int, acct); +    set_add(&acct->set, "sync_interval", "5", set_eval_int, acct);  }  static void @@ -777,6 +797,10 @@ fb_login(account_t *acc)                       G_CALLBACK(fb_cb_api_contacts),                       fata);      g_signal_connect(api, +                     "contacts-delta", +                     G_CALLBACK(fb_cb_api_contacts_delta), +                     fata); +    g_signal_connect(api,                       "error",                       G_CALLBACK(fb_cb_api_error),                       fata); diff --git a/facebook/marshaller.list b/facebook/marshaller.list index ab96190..ef5d4c9 100644 --- a/facebook/marshaller.list +++ b/facebook/marshaller.list @@ -4,3 +4,4 @@ VOID:POINTER  VOID:POINTER,BOOLEAN  VOID:STRING,BOXED  VOID:VOID +VOID:POINTER,POINTER | 
