aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjgeboski <jgeboski@gmail.com>2015-01-03 18:51:42 -0500
committerjgeboski <jgeboski@gmail.com>2015-01-14 16:06:05 -0500
commit20cd31cc5e1320e7dde8a30380d67185c5b289c8 (patch)
tree4e428a88daa25093bf156766405e8d932ab8844d
parentcefcc09b3a91f882a4511f675750d1798494969b (diff)
downloadbitlbee-facebook-20cd31cc5e1320e7dde8a30380d67185c5b289c8.tar.gz
bitlbee-facebook-20cd31cc5e1320e7dde8a30380d67185c5b289c8.tar.bz2
bitlbee-facebook-20cd31cc5e1320e7dde8a30380d67185c5b289c8.tar.xz
Implemented contacts list lookup
-rw-r--r--facebook/facebook-api.c131
-rw-r--r--facebook/facebook-api.h33
-rw-r--r--facebook/facebook.c47
3 files changed, 206 insertions, 5 deletions
diff --git a/facebook/facebook-api.c b/facebook/facebook-api.c
index a5b601c..144304a 100644
--- a/facebook/facebook-api.c
+++ b/facebook/facebook-api.c
@@ -448,6 +448,102 @@ void fb_api_auth(fb_api_t *api, const gchar *user, const gchar *pass)
}
/**
+ * Implemented #fb_http_func for #fb_api_contacts().
+ *
+ * @param req The #fb_http_req.
+ * @param data The user-defined data, which is #fb_api.
+ **/
+static void fb_api_cb_contacts(fb_http_req_t *req, gpointer data)
+{
+ fb_api_t *api = data;
+ GSList *users;
+ fb_api_user_t *user;
+ json_value *json;
+ json_value *jv;
+ json_value *jx;
+ json_value *jy;
+ json_value *jz;
+ const gchar *str;
+ const gchar *uid;
+ const gchar *name;
+ guint i;
+
+ json = fb_api_json_new(api, req->body, req->body_size);
+ users = NULL;
+
+ if (json == NULL)
+ return;
+
+ if (!fb_json_val_chk(json, "viewer", json_object, &jv) ||
+ !fb_json_val_chk(jv, "messenger_contacts", json_object, &jv) ||
+ !fb_json_val_chk(jv, "nodes", json_array, &jv))
+ {
+ fb_api_error(api, FB_API_ERROR_GENERAL, "Failed to parse contacts");
+ goto finish;
+ }
+
+ for (i = 0; i < jv->u.array.length; i++) {
+ jx = jv->u.array.values[i];
+
+ /* Scattered values lead to a gnarly conditional... */
+ if (fb_json_val_chk(jx, "represented_profile", json_object, &jy) &&
+
+ /* Check the contact type is "user" */
+ fb_json_val_chk(jy, "__type__", json_object, &jz) &&
+ fb_json_str_chk(jz, "name", &str) &&
+ (g_ascii_strcasecmp(str, "user") == 0) &&
+
+ /* Check the contact is a friend */
+ fb_json_str_chk(jy, "friendship_status", &str) &&
+ (g_ascii_strcasecmp(str, "ARE_FRIENDS") == 0) &&
+
+ /* Obtain the contact user identifier */
+ fb_json_str_chk(jy, "id", &uid) &&
+ (g_strcmp0(uid, api->uid) != 0) &&
+
+ /* Obtain the name of the user */
+ fb_json_val_chk(jx, "structured_name", json_object, &jy) &&
+ fb_json_str_chk(jy, "text", &name))
+ {
+ user = fb_api_user_new(uid, name);
+ users = g_slist_prepend(users, user);
+ }
+ }
+
+ FB_API_FUNC(api, contacts, users);
+
+finish:
+ g_slist_free_full(users, (GDestroyNotify) fb_api_user_free);
+ json_value_free(json);
+}
+
+/**
+ * Sends a contacts request.
+ *
+ * @param api The #fb_api.
+ **/
+void fb_api_contacts(fb_api_t *api)
+{
+ fb_http_req_t *req;
+
+ g_return_if_fail(api != NULL);
+
+ req = fb_api_req_new(api, FB_API_GHOST, FB_API_PATH_GQL,
+ fb_api_cb_contacts,
+ "com.facebook.contacts.service.d",
+ "FetchContactsFullQuery",
+ "get");
+
+ fb_http_req_params_set(req,
+ FB_HTTP_PAIR("query_id", FB_API_QRYID_CONTACTS),
+ FB_HTTP_PAIR("query_params", "{}"),
+ NULL
+ );
+
+ fb_api_req_send(api, req);
+}
+
+/**
* Connects the #fb_api to the remote services. This is mainly for
* connecting and setting up the internal #fb_mqtt.
*
@@ -473,3 +569,38 @@ void fb_api_disconnect(fb_api_t *api)
fb_mqtt_disconnect(api->mqtt);
}
+
+/**
+ * Creates a new #fb_api_user. The returned #fb_api_user should be
+ * freed with #fb_api_user_free() when no longer needed.
+ *
+ * @param uid The user identifier.
+ * @param name The name of the user.
+ *
+ * @return The #fb_api_user or NULL on error.
+ **/
+fb_api_user_t *fb_api_user_new(const gchar *uid, const gchar *name)
+{
+ fb_api_user_t *user;
+
+ user = g_new0(fb_api_user_t, 1);
+ user->uid = g_strdup(uid);
+ user->name = g_strdup(name);
+
+ return user;
+}
+
+/**
+ * Frees all memory used by a #fb_api_user.
+ *
+ * @param user The #fb_api_user.
+ **/
+void fb_api_user_free(fb_api_user_t *user)
+{
+ if (G_UNLIKELY(user == NULL))
+ return;
+
+ g_free(user->name);
+ g_free(user->uid);
+ g_free(user);
+}
diff --git a/facebook/facebook-api.h b/facebook/facebook-api.h
index 7d61c8e..b6bd8bd 100644
--- a/facebook/facebook-api.h
+++ b/facebook/facebook-api.h
@@ -28,11 +28,15 @@
#define FB_API_HOST "api.facebook.com"
#define FB_API_BHOST "b-api.facebook.com"
+#define FB_API_GHOST "graph.facebook.com"
#define FB_API_AGENT "Facebook App / " PACKAGE " / " PACKAGE_VERSION
#define FB_API_KEY "256002347743983"
#define FB_API_SECRET "374e60f8b9bb6b8cbb30f78030438895"
#define FB_API_PATH_AUTH "/method/auth.login"
+#define FB_API_PATH_GQL "/graphql"
+
+#define FB_API_QRYID_CONTACTS "10153122424521729"
/**
@@ -59,6 +63,9 @@ typedef struct fb_api fb_api_t;
/** The main structure for #fb_api callback functions. **/
typedef struct fb_api_funcs fb_api_funcs_t;
+/** The structure for representing an #fb_api user. **/
+typedef struct fb_api_user fb_api_user_t;
+
/**
* The #GError codes of #fb_api.
@@ -102,6 +109,17 @@ struct fb_api_funcs
* @param data The user-defined data or NULL.
**/
void (*connect) (fb_api_t *api, gpointer data);
+
+ /**
+ * The contacts function. This is called whenever the #fb_api has
+ * retrieved a set contacts. This is called as a result of
+ * #fb_api_contacts().
+ *
+ * @param api The #fb_api.
+ * @param users The #GSList of #fb_api_user.
+ * @param data The user-defined data or NULL.
+ **/
+ void (*contacts) (fb_api_t *api, const GSList *users, gpointer data);
};
/**
@@ -124,6 +142,15 @@ struct fb_api
gchar *sid; /** The sync identifier. **/
};
+/**
+ * The structure for representing an #fb_api user.
+ **/
+struct fb_api_user
+{
+ gchar *uid; /** The user identifier. **/
+ gchar *name; /** The name of the user. **/
+};
+
#define FB_API_ERROR fb_api_error_quark()
@@ -139,8 +166,14 @@ void fb_api_error(fb_api_t *api, fb_api_error_t err, const gchar *fmt, ...);
void fb_api_auth(fb_api_t *api, const gchar *user, const gchar *pass);
+void fb_api_contacts(fb_api_t *api);
+
void fb_api_connect(fb_api_t *api);
void fb_api_disconnect(fb_api_t *api);
+fb_api_user_t *fb_api_user_new(const gchar *uid, const gchar *name);
+
+void fb_api_user_free(fb_api_user_t *user);
+
#endif /* _FACEBOOK_API_H */
diff --git a/facebook/facebook.c b/facebook/facebook.c
index ca2d687..aed3893 100644
--- a/facebook/facebook.c
+++ b/facebook/facebook.c
@@ -61,8 +61,43 @@ static void fb_cb_api_auth(fb_api_t *api, gpointer data)
**/
static void fb_cb_api_connect(fb_api_t *api, gpointer data)
{
- fb_data_t *fata = data;
+ fb_data_t *fata = data;
+ GSList *l;
+ bee_user_t *bu;
+
imcb_connected(fata->ic);
+
+ for (l = fata->ic->bee->users; l != NULL; l = l->next) {
+ bu = l->data;
+
+ /* For now, all users are online */
+ imcb_buddy_status(fata->ic, bu->handle, OPT_LOGGED_IN, NULL, NULL);
+ }
+}
+
+/**
+ * Implemented #fb_api_funcs->contacts().
+ *
+ * @param api The #fb_api.
+ * @param users The #GSList of #fb_api_user.
+ * @param data The user defined data, which is #fb_data.
+ **/
+static void fb_cb_api_contacts(fb_api_t *api, const GSList *users,
+ gpointer data)
+{
+ fb_data_t *fata = data;
+ fb_api_user_t *user;
+ const GSList *l;
+
+ for (l = users; l != NULL; l = l->next) {
+ user = l->data;
+ imcb_add_buddy(fata->ic, user->uid, NULL);
+ imcb_buddy_nick_hint(fata->ic, user->uid, user->name);
+ imcb_rename_buddy(fata->ic, user->uid, user->name);
+ }
+
+ imcb_log(fata->ic, "Establishing connection");
+ fb_api_connect(fata->api);
}
/**
@@ -78,9 +113,10 @@ fb_data_t *fb_data_new(account_t *acc)
fb_data_t *fata;
static const fb_api_funcs_t funcs = {
- .error = fb_cb_api_error,
- .auth = fb_cb_api_auth,
- .connect = fb_cb_api_connect
+ .error = fb_cb_api_error,
+ .auth = fb_cb_api_auth,
+ .connect = fb_cb_api_connect,
+ .contacts = fb_cb_api_contacts
};
g_return_val_if_fail(acc != NULL, NULL);
@@ -163,7 +199,8 @@ static void fb_login(account_t *acc)
return;
}
- fb_api_connect(fata->api);
+ imcb_log(fata->ic, "Fetching contacts");
+ fb_api_contacts(fata->api);
}
/**