guibuilder_pel7x64builder0
6 years ago
3 changed files with 952 additions and 28 deletions
@ -0,0 +1,845 @@
@@ -0,0 +1,845 @@
|
||||
Index: purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/api.c |
||||
=================================================================== |
||||
--- purple-facebook-0.9.6.orig/pidgin/libpurple/protocols/facebook/api.c |
||||
+++ purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/api.c |
||||
@@ -33,6 +33,7 @@ |
||||
#include "util.h" |
||||
|
||||
typedef struct _FbApiData FbApiData; |
||||
+typedef struct _FbApiPreloginData FbApiPreloginData; |
||||
|
||||
enum |
||||
{ |
||||
@@ -44,6 +45,8 @@ enum |
||||
PROP_STOKEN, |
||||
PROP_TOKEN, |
||||
PROP_UID, |
||||
+ PROP_TWEAK, |
||||
+ PROP_WORK, |
||||
|
||||
PROP_N |
||||
}; |
||||
@@ -69,6 +72,11 @@ struct _FbApiPrivate |
||||
guint unread; |
||||
FbId lastmid; |
||||
gchar *contacts_delta; |
||||
+ int tweak; |
||||
+ gboolean is_work; |
||||
+ gboolean need_work_switch; |
||||
+ gchar *sso_verifier; |
||||
+ FbId work_community_id; |
||||
}; |
||||
|
||||
struct _FbApiData |
||||
@@ -77,6 +85,13 @@ struct _FbApiData |
||||
GDestroyNotify func; |
||||
}; |
||||
|
||||
+struct _FbApiPreloginData |
||||
+{ |
||||
+ FbApi *api; |
||||
+ const gchar *user; |
||||
+ const gchar *pass; |
||||
+}; |
||||
+ |
||||
static void |
||||
fb_api_attach(FbApi *api, FbId aid, const gchar *msgid, FbApiMessage *msg); |
||||
|
||||
@@ -94,6 +109,24 @@ fb_api_contacts_delta(FbApi *api, const |
||||
|
||||
G_DEFINE_TYPE_WITH_CODE(FbApi, fb_api, G_TYPE_OBJECT, G_ADD_PRIVATE(FbApi)); |
||||
|
||||
+static const gchar *agents[] = { |
||||
+ FB_API_AGENT, |
||||
+ NULL, |
||||
+}; |
||||
+ |
||||
+static const gchar * |
||||
+fb_api_get_agent_string(int tweak, gboolean mqtt) |
||||
+{ |
||||
+ gboolean http_only = tweak & 4; |
||||
+ gboolean mqtt_only = tweak & 8; |
||||
+ |
||||
+ if (tweak <= 0 || tweak > 15 || (http_only && mqtt) || (mqtt_only && !mqtt)) { |
||||
+ return agents[0]; |
||||
+ } |
||||
+ |
||||
+ return agents[tweak & 3]; |
||||
+} |
||||
+ |
||||
static void |
||||
fb_api_set_property(GObject *obj, guint prop, const GValue *val, |
||||
GParamSpec *pspec) |
||||
@@ -123,6 +156,12 @@ fb_api_set_property(GObject *obj, guint |
||||
case PROP_UID: |
||||
priv->uid = g_value_get_int64(val); |
||||
break; |
||||
+ case PROP_TWEAK: |
||||
+ priv->tweak = g_value_get_int(val); |
||||
+ break; |
||||
+ case PROP_WORK: |
||||
+ priv->is_work = g_value_get_boolean(val); |
||||
+ break; |
||||
|
||||
default: |
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop, pspec); |
||||
@@ -154,6 +193,12 @@ fb_api_get_property(GObject *obj, guint |
||||
case PROP_UID: |
||||
g_value_set_int64(val, priv->uid); |
||||
break; |
||||
+ case PROP_TWEAK: |
||||
+ g_value_set_int(val, priv->tweak); |
||||
+ break; |
||||
+ case PROP_WORK: |
||||
+ g_value_set_boolean(val, priv->is_work); |
||||
+ break; |
||||
|
||||
default: |
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop, pspec); |
||||
@@ -190,6 +235,7 @@ fb_api_dispose(GObject *obj) |
||||
g_free(priv->stoken); |
||||
g_free(priv->token); |
||||
g_free(priv->contacts_delta); |
||||
+ g_free(priv->sso_verifier); |
||||
} |
||||
|
||||
static void |
||||
@@ -279,6 +325,26 @@ fb_api_class_init(FbApiClass *klass) |
||||
"User identifier", |
||||
0, G_MAXINT64, 0, |
||||
G_PARAM_READWRITE); |
||||
+ |
||||
+ /** |
||||
+ * FbApi:tweak: |
||||
+ */ |
||||
+ props[PROP_TWEAK] = g_param_spec_int( |
||||
+ "tweak", |
||||
+ "Tweak", |
||||
+ "", |
||||
+ 0, G_MAXINT, 0, |
||||
+ G_PARAM_READWRITE); |
||||
+ |
||||
+ /** |
||||
+ * FbApi:work: |
||||
+ */ |
||||
+ props[PROP_WORK] = g_param_spec_boolean( |
||||
+ "work", |
||||
+ "Work", |
||||
+ "", |
||||
+ FALSE, |
||||
+ G_PARAM_READWRITE); |
||||
g_object_class_install_properties(gklass, PROP_N, props); |
||||
|
||||
/** |
||||
@@ -517,6 +583,22 @@ fb_api_class_init(FbApiClass *klass) |
||||
fb_marshal_VOID__POINTER, |
||||
G_TYPE_NONE, |
||||
1, G_TYPE_POINTER); |
||||
+ |
||||
+ /** |
||||
+ * FbApi::work-sso-login: |
||||
+ * @api: The #FbApi. |
||||
+ * |
||||
+ * Emitted when user interaction is required to continue SAML SSO login |
||||
+ */ |
||||
+ |
||||
+ g_signal_new("work-sso-login", |
||||
+ G_TYPE_FROM_CLASS(klass), |
||||
+ G_SIGNAL_ACTION, |
||||
+ 0, |
||||
+ NULL, NULL, |
||||
+ fb_marshal_VOID__VOID, |
||||
+ G_TYPE_NONE, |
||||
+ 0); |
||||
} |
||||
|
||||
static void |
||||
@@ -760,7 +842,8 @@ fb_api_http_req(FbApi *api, const gchar |
||||
PurpleHttpConnection *ret; |
||||
PurpleHttpRequest *req; |
||||
|
||||
- fb_http_params_set_str(params, "api_key", FB_API_KEY); |
||||
+ fb_http_params_set_str(params, "api_key", |
||||
+ priv->is_work ? FB_WORK_API_KEY : FB_API_KEY); |
||||
fb_http_params_set_str(params, "device_id", priv->did); |
||||
fb_http_params_set_str(params, "fb_api_req_friendly_name", name); |
||||
fb_http_params_set_str(params, "format", "json"); |
||||
@@ -787,7 +870,7 @@ fb_api_http_req(FbApi *api, const gchar |
||||
g_string_append_printf(gstr, "%s=%s", key, val); |
||||
} |
||||
|
||||
- g_string_append(gstr, FB_API_SECRET); |
||||
+ g_string_append(gstr, priv->is_work ? FB_WORK_API_SECRET : FB_API_SECRET); |
||||
data = g_compute_checksum_for_string(G_CHECKSUM_MD5, gstr->str, |
||||
gstr->len); |
||||
fb_http_params_set_str(params, "sig", data); |
||||
@@ -929,7 +1012,9 @@ fb_api_cb_mqtt_open(FbMqtt *mqtt, gpoint |
||||
|
||||
/* Write the information string */ |
||||
fb_thrift_write_field(thft, FB_THRIFT_TYPE_STRING, 2, 1); |
||||
- fb_thrift_write_str(thft, FB_API_MQTT_AGENT); |
||||
+ fb_thrift_write_str(thft, (priv->tweak != 0) |
||||
+ ? fb_api_get_agent_string(priv->tweak, 1) |
||||
+ : FB_API_MQTT_AGENT); |
||||
|
||||
/* Write the UNKNOWN ("cp"?) */ |
||||
fb_thrift_write_field(thft, FB_THRIFT_TYPE_I64, 3, 2); |
||||
@@ -2128,6 +2213,54 @@ fb_api_attach(FbApi *api, FbId aid, cons |
||||
} |
||||
|
||||
static void |
||||
+fb_api_cb_work_peek(PurpleHttpConnection *con, PurpleHttpResponse *res, gpointer data) |
||||
+{ |
||||
+ FbApi *api = data; |
||||
+ FbApiPrivate *priv = api->priv; |
||||
+ GError *err = NULL; |
||||
+ JsonNode *root; |
||||
+ gchar *community = NULL; |
||||
+ |
||||
+ if (!fb_api_http_chk(api, con, res, &root)) { |
||||
+ return; |
||||
+ } |
||||
+ |
||||
+ /* The work_users[0] explicitly only handles the first user. |
||||
+ * If more than one user is ever needed, this is what you want to change, |
||||
+ * but as far as I know this feature (linked work accounts) is deprecated |
||||
+ * and most users can detach their work accounts from their personal |
||||
+ * accounts by assigning a password to the work account. */ |
||||
+ community = fb_json_node_get_str(root, |
||||
+ "$.data.viewer.work_users[0].community.login_identifier", &err); |
||||
+ |
||||
+ FB_API_ERROR_EMIT(api, err, |
||||
+ g_free(community); |
||||
+ json_node_free(root); |
||||
+ return; |
||||
+ ); |
||||
+ |
||||
+ priv->work_community_id = FB_ID_FROM_STR(community); |
||||
+ |
||||
+ fb_api_auth(api, "X", "X", "personal_to_work_switch"); |
||||
+ |
||||
+ g_free(community); |
||||
+ json_node_free(root); |
||||
+} |
||||
+ |
||||
+static PurpleHttpConnection * |
||||
+fb_api_work_peek(FbApi *api) |
||||
+{ |
||||
+ FbHttpParams *prms; |
||||
+ |
||||
+ prms = fb_http_params_new(); |
||||
+ fb_http_params_set_int(prms, "doc_id", FB_API_WORK_COMMUNITY_PEEK); |
||||
+ |
||||
+ return fb_api_http_req(api, FB_API_URL_GQL, "WorkCommunityPeekQuery", |
||||
+ "post", prms, fb_api_cb_work_peek); |
||||
+} |
||||
+ |
||||
+ |
||||
+static void |
||||
fb_api_cb_auth(PurpleHttpConnection *con, PurpleHttpResponse *res, |
||||
gpointer data) |
||||
{ |
||||
@@ -2143,7 +2276,14 @@ fb_api_cb_auth(PurpleHttpConnection *con |
||||
|
||||
values = fb_json_values_new(root); |
||||
fb_json_values_add(values, FB_JSON_TYPE_STR, TRUE, "$.access_token"); |
||||
- fb_json_values_add(values, FB_JSON_TYPE_INT, TRUE, "$.uid"); |
||||
+ |
||||
+ /* extremely silly difference */ |
||||
+ if (priv->is_work) { |
||||
+ fb_json_values_add(values, FB_JSON_TYPE_STR, TRUE, "$.uid"); |
||||
+ } else { |
||||
+ fb_json_values_add(values, FB_JSON_TYPE_INT, TRUE, "$.uid"); |
||||
+ } |
||||
+ |
||||
fb_json_values_update(values, &err); |
||||
|
||||
FB_API_ERROR_EMIT(api, err, |
||||
@@ -2154,25 +2294,218 @@ fb_api_cb_auth(PurpleHttpConnection *con |
||||
|
||||
g_free(priv->token); |
||||
priv->token = fb_json_values_next_str_dup(values, NULL); |
||||
- priv->uid = fb_json_values_next_int(values, 0); |
||||
|
||||
- g_signal_emit_by_name(api, "auth"); |
||||
+ if (priv->is_work) { |
||||
+ priv->uid = FB_ID_FROM_STR(fb_json_values_next_str(values, "0")); |
||||
+ } else { |
||||
+ priv->uid = fb_json_values_next_int(values, 0); |
||||
+ } |
||||
+ |
||||
+ if (priv->need_work_switch) { |
||||
+ fb_api_work_peek(api); |
||||
+ priv->need_work_switch = FALSE; |
||||
+ } else { |
||||
+ g_signal_emit_by_name(api, "auth"); |
||||
+ } |
||||
+ |
||||
g_object_unref(values); |
||||
json_node_free(root); |
||||
} |
||||
|
||||
void |
||||
-fb_api_auth(FbApi *api, const gchar *user, const gchar *pass) |
||||
+fb_api_auth(FbApi *api, const gchar *user, const gchar *pass, const gchar *credentials_type) |
||||
{ |
||||
+ FbApiPrivate *priv = api->priv; |
||||
FbHttpParams *prms; |
||||
|
||||
prms = fb_http_params_new(); |
||||
fb_http_params_set_str(prms, "email", user); |
||||
fb_http_params_set_str(prms, "password", pass); |
||||
+ |
||||
+ if (credentials_type) { |
||||
+ fb_http_params_set_str(prms, "credentials_type", credentials_type); |
||||
+ } |
||||
+ |
||||
+ if (priv->sso_verifier) { |
||||
+ fb_http_params_set_str(prms, "code_verifier", priv->sso_verifier); |
||||
+ g_free(priv->sso_verifier); |
||||
+ priv->sso_verifier = NULL; |
||||
+ } |
||||
+ |
||||
+ if (priv->work_community_id) { |
||||
+ fb_http_params_set_int(prms, "community_id", priv->work_community_id); |
||||
+ } |
||||
+ |
||||
+ if (priv->is_work && priv->token) { |
||||
+ fb_http_params_set_str(prms, "access_token", priv->token); |
||||
+ } |
||||
+ |
||||
fb_api_http_req(api, FB_API_URL_AUTH, "authenticate", "auth.login", |
||||
prms, fb_api_cb_auth); |
||||
} |
||||
|
||||
+static void |
||||
+fb_api_cb_work_prelogin(PurpleHttpConnection *con, PurpleHttpResponse *res, gpointer data) |
||||
+{ |
||||
+ FbApiPreloginData *pata = data; |
||||
+ FbApi *api = pata->api; |
||||
+ FbApiPrivate *priv = api->priv; |
||||
+ GError *err = NULL; |
||||
+ JsonNode *root; |
||||
+ gchar *status; |
||||
+ const gchar *user = pata->user; |
||||
+ const gchar *pass = pata->pass; |
||||
+ |
||||
+ g_free(pata); |
||||
+ |
||||
+ if (!fb_api_http_chk(api, con, res, &root)) { |
||||
+ return; |
||||
+ } |
||||
+ |
||||
+ status = fb_json_node_get_str(root, "$.status", &err); |
||||
+ |
||||
+ FB_API_ERROR_EMIT(api, err, |
||||
+ json_node_free(root); |
||||
+ return; |
||||
+ ); |
||||
+ |
||||
+ if (g_strcmp0(status, "can_login_password") == 0) { |
||||
+ fb_api_auth(api, user, pass, "work_account_password"); |
||||
+ |
||||
+ } else if (g_strcmp0(status, "can_login_via_linked_account") == 0) { |
||||
+ fb_api_auth(api, user, pass, "personal_account_password_with_work_username"); |
||||
+ priv->need_work_switch = TRUE; |
||||
+ |
||||
+ } else if (g_strcmp0(status, "can_login_sso") == 0) { |
||||
+ g_signal_emit_by_name(api, "work-sso-login"); |
||||
+ |
||||
+ } else if (g_strcmp0(status, "cannot_login") == 0) { |
||||
+ char *reason = fb_json_node_get_str(root, "$.cannot_login_reason", NULL); |
||||
+ |
||||
+ if (g_strcmp0(reason, "non_business_email") == 0) { |
||||
+ fb_api_error(api, FB_API_ERROR_AUTH, |
||||
+ "Cannot login with non-business email. " |
||||
+ "Change the 'username' setting or disable 'work'"); |
||||
+ } else { |
||||
+ char *title = fb_json_node_get_str(root, "$.error_title", NULL); |
||||
+ char *body = fb_json_node_get_str(root, "$.error_body", NULL); |
||||
+ |
||||
+ fb_api_error(api, FB_API_ERROR_AUTH, |
||||
+ "Work prelogin failed (%s - %s)", title, body); |
||||
+ |
||||
+ g_free(title); |
||||
+ g_free(body); |
||||
+ } |
||||
+ |
||||
+ g_free(reason); |
||||
+ |
||||
+ } else if (g_strcmp0(status, "can_self_invite") == 0) { |
||||
+ fb_api_error(api, FB_API_ERROR_AUTH, "Unknown email. " |
||||
+ "Change the 'username' setting or disable 'work'"); |
||||
+ } |
||||
+ |
||||
+ g_free(status); |
||||
+ json_node_free(root); |
||||
+} |
||||
+ |
||||
+void |
||||
+fb_api_work_login(FbApi *api, const gchar *user, const gchar *pass) |
||||
+{ |
||||
+ FbApiPrivate *priv = api->priv; |
||||
+ FbApiPreloginData *pata = g_new0(FbApiPreloginData, 1); |
||||
+ FbHttpParams *prms; |
||||
+ gchar *data; |
||||
+ PurpleHttpConnection *ret; |
||||
+ PurpleHttpRequest *req; |
||||
+ const char *url = FB_API_URL_WORK_PRELOGIN; |
||||
+ |
||||
+ pata->api = api; |
||||
+ pata->user = user; |
||||
+ pata->pass = pass; |
||||
+ |
||||
+ priv->is_work = TRUE; |
||||
+ |
||||
+ // see mark k |
||||
+ req = purple_http_request_new(url); |
||||
+ purple_http_request_set_max_len(req, -1); |
||||
+ purple_http_request_set_method(req, "POST"); |
||||
+ |
||||
+ purple_http_request_header_set(req, "Authorization", "OAuth null"); |
||||
+ purple_http_request_header_set(req, "User-Agent", FB_API_AGENT); |
||||
+ purple_http_request_header_set(req, "Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); |
||||
+ |
||||
+ prms = fb_http_params_new(); |
||||
+ fb_http_params_set_str(prms, "email", user); |
||||
+ fb_http_params_set_str(prms, "access_token", |
||||
+ FB_WORK_API_KEY "|" FB_WORK_API_SECRET); |
||||
+ |
||||
+ data = fb_http_params_close(prms, NULL); |
||||
+ purple_http_request_set_contents(req, data, -1); |
||||
+ ret = purple_http_request(priv->gc, req, fb_api_cb_work_prelogin, pata); |
||||
+ fb_http_conns_add(priv->cons, ret); |
||||
+ purple_http_request_unref(req); |
||||
+ |
||||
+ fb_util_debug(FB_UTIL_DEBUG_INFO, "HTTP Request (%p):", ret); |
||||
+ fb_util_debug(FB_UTIL_DEBUG_INFO, " Request URL: %s", url); |
||||
+ fb_util_debug(FB_UTIL_DEBUG_INFO, " Request Data: %s", data); |
||||
+ |
||||
+ g_free(data); |
||||
+} |
||||
+ |
||||
+gchar * |
||||
+fb_api_work_gen_sso_url(FbApi *api, const gchar *user) |
||||
+{ |
||||
+ FbApiPrivate *priv = api->priv; |
||||
+ gchar *challenge, *verifier, *req_id, *email; |
||||
+ gchar *ret; |
||||
+ |
||||
+ fb_util_gen_sso_verifier(&challenge, &verifier, &req_id); |
||||
+ |
||||
+ email = g_uri_escape_string(user, NULL, FALSE); |
||||
+ |
||||
+ ret = g_strdup_printf(FB_API_SSO_URL, req_id, challenge, email); |
||||
+ |
||||
+ g_free(req_id); |
||||
+ g_free(challenge); |
||||
+ g_free(email); |
||||
+ |
||||
+ g_free(priv->sso_verifier); |
||||
+ priv->sso_verifier = verifier; |
||||
+ |
||||
+ return ret; |
||||
+} |
||||
+ |
||||
+void |
||||
+fb_api_work_got_nonce(FbApi *api, const gchar *url) |
||||
+{ |
||||
+ gchar **split; |
||||
+ gchar *uid = NULL; |
||||
+ gchar *nonce = NULL; |
||||
+ int i; |
||||
+ |
||||
+ if (!g_str_has_prefix(url, "fb-workchat-sso://sso/?")) { |
||||
+ return; |
||||
+ } |
||||
+ |
||||
+ split = g_strsplit(strchr(url, '?'), "&", -1); |
||||
+ |
||||
+ for (i = 0; split[i]; i++) { |
||||
+ gchar *eq = strchr(split[i], '='); |
||||
+ |
||||
+ if (g_str_has_prefix(split[i], "uid=")) { |
||||
+ uid = g_strstrip(eq + 1); |
||||
+ } else if (g_str_has_prefix(split[i], "nonce=")) { |
||||
+ nonce = g_strstrip(eq + 1); |
||||
+ } |
||||
+ } |
||||
+ |
||||
+ if (uid && nonce) { |
||||
+ fb_api_auth(api, uid, nonce, "work_sso_nonce"); |
||||
+ } |
||||
+ |
||||
+ g_strfreev(split); |
||||
+} |
||||
+ |
||||
static gchar * |
||||
fb_api_user_icon_checksum(gchar *icon) |
||||
{ |
||||
@@ -2277,6 +2610,8 @@ fb_api_cb_contacts_nodes(FbApi *api, Jso |
||||
"$.represented_profile.id"); |
||||
fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE, |
||||
"$.represented_profile.friendship_status"); |
||||
+ fb_json_values_add(values, FB_JSON_TYPE_BOOL, FALSE, |
||||
+ "$.is_on_viewer_contact_list"); |
||||
fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE, |
||||
"$.structured_name.text"); |
||||
fb_json_values_add(values, FB_JSON_TYPE_STR, FALSE, |
||||
@@ -2289,12 +2624,15 @@ fb_api_cb_contacts_nodes(FbApi *api, Jso |
||||
} |
||||
|
||||
while (fb_json_values_update(values, &err)) { |
||||
+ gboolean in_contact_list; |
||||
+ |
||||
str = fb_json_values_next_str(values, "0"); |
||||
uid = FB_ID_FROM_STR(str); |
||||
str = fb_json_values_next_str(values, NULL); |
||||
+ in_contact_list = fb_json_values_next_bool(values, FALSE); |
||||
|
||||
- if ((!purple_strequal(str, "ARE_FRIENDS") && |
||||
- (uid != priv->uid)) || (uid == 0)) |
||||
+ if ((!in_contact_list && !purple_strequal(str, "ARE_FRIENDS") && |
||||
+ (uid != priv->uid)) || (uid == 0)) |
||||
{ |
||||
if (!is_array) { |
||||
break; |
||||
Index: purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/api.h |
||||
=================================================================== |
||||
--- purple-facebook-0.9.6.orig/pidgin/libpurple/protocols/facebook/api.h |
||||
+++ purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/api.h |
||||
@@ -98,6 +98,20 @@ |
||||
#define FB_API_SECRET "374e60f8b9bb6b8cbb30f78030438895" |
||||
|
||||
/** |
||||
+ * FB_WORK_API_KEY: |
||||
+ * |
||||
+ * The Facebook workchat app API key. |
||||
+ */ |
||||
+#define FB_WORK_API_KEY "312713275593566" |
||||
+ |
||||
+/** |
||||
+ * FB_WORK_API_SECRET: |
||||
+ * |
||||
+ * The Facebook workchat app API secret. |
||||
+ */ |
||||
+#define FB_WORK_API_SECRET "d2901dc6cb685df3b074b30b56b78d28" |
||||
+ |
||||
+/** |
||||
* FB_ORCA_AGENT |
||||
* |
||||
* The part of the user agent that looks like the official client, since the |
||||
@@ -137,6 +151,15 @@ |
||||
#define FB_API_URL_AUTH FB_API_BHOST "/method/auth.login" |
||||
|
||||
/** |
||||
+ * FB_API_URL_WORK_PRELOGIN |
||||
+ * |
||||
+ * The URL for workchat pre-login information, indicating what auth method |
||||
+ * should be used |
||||
+ */ |
||||
+ |
||||
+#define FB_API_URL_WORK_PRELOGIN FB_API_GHOST "/at_work/pre_login_info" |
||||
+ |
||||
+/** |
||||
* FB_API_URL_GQL: |
||||
* |
||||
* The URL for GraphQL requests. |
||||
@@ -172,6 +195,14 @@ |
||||
#define FB_API_URL_TOPIC FB_API_AHOST "/method/messaging.setthreadname" |
||||
|
||||
/** |
||||
+ * FB_API_SSO_URL: |
||||
+ * |
||||
+ * Template for the URL shown to workchat users when trying to authenticate |
||||
+ * with SSO. |
||||
+ */ |
||||
+#define FB_API_SSO_URL "https://m.facebook.com/work/sso/mobile?app_id=312713275593566&response_url=fb-workchat-sso%%3A%%2F%%2Fsso&request_id=%s&code_challenge=%s&email=%s" |
||||
+ |
||||
+/** |
||||
* FB_API_QUERY_CONTACT: |
||||
* |
||||
* The query hash for the `UsersQuery`. |
||||
@@ -319,6 +350,16 @@ |
||||
#define FB_API_QUERY_XMA 10153919431161729 |
||||
|
||||
/** |
||||
+ * FB_API_WORK_COMMUNITY_PEEK: |
||||
+ * |
||||
+ * The docid with information about the work community of the currently |
||||
+ * authenticated user. |
||||
+ * |
||||
+ * Used when prelogin returns can_login_via_linked_account |
||||
+ */ |
||||
+#define FB_API_WORK_COMMUNITY_PEEK 1295334753880530 |
||||
+ |
||||
+/** |
||||
* FB_API_CONTACTS_COUNT: |
||||
* |
||||
* The maximum amount of contacts to fetch in a single request. If this |
||||
@@ -641,12 +682,49 @@ fb_api_error_emit(FbApi *api, GError *er |
||||
* @api: The #FbApi. |
||||
* @user: The Facebook user name, email, or phone number. |
||||
* @pass: The Facebook password. |
||||
+ * @credentials_type: Type of work account credentials, or NULL |
||||
* |
||||
* Sends an authentication request to Facebook. This will obtain |
||||
* session information, which is required for all other requests. |
||||
*/ |
||||
void |
||||
-fb_api_auth(FbApi *api, const gchar *user, const gchar *pass); |
||||
+fb_api_auth(FbApi *api, const gchar *user, const gchar *pass, const gchar *credentials_type); |
||||
+ |
||||
+/** |
||||
+ * fb_api_work_login: |
||||
+ * @api: The #FbApi. |
||||
+ * @user: The Facebook user name, email, or phone number. |
||||
+ * @pass: The Facebook password. |
||||
+ * |
||||
+ * Starts the workchat login sequence. |
||||
+ */ |
||||
+void |
||||
+fb_api_work_login(FbApi *api, const gchar *user, const gchar *pass); |
||||
+ |
||||
+/** |
||||
+ * fb_api_work_gen_sso_url: |
||||
+ * @api: The #FbApi. |
||||
+ * @user: The Facebook user email. |
||||
+ * |
||||
+ * Generates the URL to be shown to the user to get the SSO auth token. This |
||||
+ * url contains a challenge and the corresponding verifier is saved in the |
||||
+ * FbApi instance to be used later. |
||||
+ * |
||||
+ * Returns: a newly allocated string. |
||||
+ */ |
||||
+gchar * |
||||
+fb_api_work_gen_sso_url(FbApi *api, const gchar *user); |
||||
+ |
||||
+/** |
||||
+ * fb_api_work_got_nonce: |
||||
+ * @api: The #FbApi. |
||||
+ * @url: The fb-workchat-sso:// URL as entered by the user |
||||
+ * |
||||
+ * Parses the fb-workchat-sso:// URL that the user got redirected to and |
||||
+ * continues with work_sso_nonce auth |
||||
+ */ |
||||
+void |
||||
+fb_api_work_got_nonce(FbApi *api, const gchar *url); |
||||
|
||||
/** |
||||
* fb_api_contact: |
||||
Index: purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/facebook.c |
||||
=================================================================== |
||||
--- purple-facebook-0.9.6.orig/pidgin/libpurple/protocols/facebook/facebook.c |
||||
+++ purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/facebook.c |
||||
@@ -828,6 +828,59 @@ fb_cb_api_typing(FbApi *api, FbApiTyping |
||||
} |
||||
|
||||
static void |
||||
+fb_cb_work_sso_input_cb(gpointer user_data, const gchar *nonce) |
||||
+{ |
||||
+ FbApi *api; |
||||
+ FbData *fata = user_data; |
||||
+ |
||||
+ api = fb_data_get_api(fata); |
||||
+ |
||||
+ fb_api_work_got_nonce(api, nonce); |
||||
+} |
||||
+ |
||||
+static void |
||||
+fb_cb_work_sso_cancel_cb(gpointer user_data) |
||||
+{ |
||||
+ PurpleConnection *gc; |
||||
+ FbData *fata = user_data; |
||||
+ |
||||
+ gc = fb_data_get_connection(fata); |
||||
+ |
||||
+ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, |
||||
+ _("User cancelled authorization")); |
||||
+} |
||||
+ |
||||
+static void |
||||
+fb_cb_api_work_sso_login(FbApi *api, gpointer data) |
||||
+{ |
||||
+ FbData *fata = data; |
||||
+ PurpleAccount *acct; |
||||
+ PurpleConnection *gc; |
||||
+ gchar *url; |
||||
+ |
||||
+ gc = fb_data_get_connection(fata); |
||||
+ acct = purple_connection_get_account(gc); |
||||
+ |
||||
+ url = fb_api_work_gen_sso_url(api, purple_account_get_username(acct)); |
||||
+ |
||||
+ purple_notify_uri(gc, url); |
||||
+ purple_request_input(gc, _("Authorization Code"), "SSO auth", |
||||
+ _("Open this URL in your browser to authenticate.\n" |
||||
+ "Respond to this message with the URL starting with 'fb-workchat-sso://' that it attempts to redirect to.\n" |
||||
+ "If your browser says 'Address not understood' (like firefox), copy it from the address bar.\n" |
||||
+ "Otherwise you might have to right click -> view source in the last page and find it there. Good luck!"), |
||||
+ url, |
||||
+ FALSE, FALSE, NULL, |
||||
+ _("OK"), G_CALLBACK(fb_cb_work_sso_input_cb), |
||||
+ _("Cancel"), G_CALLBACK(fb_cb_work_sso_cancel_cb), |
||||
+ purple_connection_get_account(gc), NULL, NULL, fata); |
||||
+ |
||||
+ |
||||
+ g_free(url); |
||||
+} |
||||
+ |
||||
+ |
||||
+static void |
||||
fb_mark_read(FbData *fata, FbId id, gboolean thread) |
||||
{ |
||||
FbApi *api; |
||||
@@ -1056,6 +1109,10 @@ fb_login(PurpleAccount *acct) |
||||
"typing", |
||||
G_CALLBACK(fb_cb_api_typing), |
||||
fata); |
||||
+ g_signal_connect(api, |
||||
+ "work-sso-login", |
||||
+ G_CALLBACK(fb_cb_api_work_sso_login), |
||||
+ fata); |
||||
|
||||
purple_signal_connect(convh, |
||||
"conversation-updated", |
||||
@@ -1073,7 +1130,11 @@ fb_login(PurpleAccount *acct) |
||||
pass = purple_connection_get_password(gc); |
||||
purple_connection_update_progress(gc, _("Authenticating"), |
||||
1, 4); |
||||
- fb_api_auth(api, user, pass); |
||||
+ if (purple_account_get_bool(acct, "work", FALSE)) { |
||||
+ fb_api_work_login(api, user, pass); |
||||
+ } else { |
||||
+ fb_api_auth(api, user, pass, NULL); |
||||
+ } |
||||
return; |
||||
} |
||||
|
||||
@@ -1678,6 +1739,11 @@ purple_init_plugin(PurplePlugin *plugin) |
||||
"incoming messages"), |
||||
"group-chat-open", TRUE); |
||||
opts = g_list_prepend(opts, opt); |
||||
+ |
||||
+ opt = purple_account_option_bool_new(_("Login as a Workplace account"), |
||||
+ "work", FALSE); |
||||
+ opts = g_list_prepend(opts, opt); |
||||
+ |
||||
pinfo.protocol_options = g_list_reverse(opts); |
||||
|
||||
inited = TRUE; |
||||
Index: purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/util.c |
||||
=================================================================== |
||||
--- purple-facebook-0.9.6.orig/pidgin/libpurple/protocols/facebook/util.c |
||||
+++ purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/util.c |
||||
@@ -564,3 +564,57 @@ fb_util_zlib_inflate(const GByteArray *b |
||||
g_object_unref(conv); |
||||
return ret; |
||||
} |
||||
+ |
||||
+gchar * |
||||
+fb_util_urlsafe_base64_encode(const guchar *data, gsize len) |
||||
+{ |
||||
+ gchar *out = g_base64_encode(data, len); |
||||
+ gchar *c; |
||||
+ |
||||
+ for (c = out; *c; c++) { |
||||
+ if (*c == '+') { |
||||
+ *c = '-'; |
||||
+ } else if (*c == '/') { |
||||
+ *c = '_'; |
||||
+ } else if (*c == '=') { |
||||
+ *c = '\0'; |
||||
+ break; |
||||
+ } |
||||
+ } |
||||
+ |
||||
+ return out; |
||||
+} |
||||
+ |
||||
+/* bitlbee compat */ |
||||
+static void |
||||
+random_bytes(guint8 *buf, gsize len) |
||||
+{ |
||||
+ gsize i; |
||||
+ |
||||
+ for (i = 0; i < len; i++) { |
||||
+ buf[i] = (guint8) g_random_int_range(0, 256); |
||||
+ } |
||||
+} |
||||
+ |
||||
+void |
||||
+fb_util_gen_sso_verifier(gchar **challenge, gchar **verifier, gchar **req_id) |
||||
+{ |
||||
+ guint8 buf[32]; |
||||
+ GChecksum *gc; |
||||
+ gsize digest_len = sizeof buf; |
||||
+ |
||||
+ random_bytes(buf, sizeof buf); |
||||
+ |
||||
+ *verifier = fb_util_urlsafe_base64_encode(buf, sizeof buf); |
||||
+ |
||||
+ gc = g_checksum_new(G_CHECKSUM_SHA256); |
||||
+ g_checksum_update(gc, (guchar *) *verifier, -1); |
||||
+ g_checksum_get_digest(gc, buf, &digest_len); |
||||
+ g_checksum_free(gc); |
||||
+ |
||||
+ *challenge = fb_util_urlsafe_base64_encode(buf, sizeof buf); |
||||
+ |
||||
+ random_bytes(buf, 3); |
||||
+ |
||||
+ *req_id = fb_util_urlsafe_base64_encode(buf, 3); |
||||
+} |
||||
Index: purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/util.h |
||||
=================================================================== |
||||
--- purple-facebook-0.9.6.orig/pidgin/libpurple/protocols/facebook/util.h |
||||
+++ purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/util.h |
||||
@@ -347,4 +347,30 @@ fb_util_zlib_deflate(const GByteArray *b |
||||
GByteArray * |
||||
fb_util_zlib_inflate(const GByteArray *bytes, GError **error); |
||||
|
||||
+/** |
||||
+ * fb_util_urlsafe_base64_encode: |
||||
+ * @data: the binary data to encode. |
||||
+ * @len: the length of data |
||||
+ * |
||||
+ * Wrapper around g_base64_encode() which substitutes '-' instead of '+' |
||||
+ * and '_' instead of '/' and removes the padding |
||||
+ * |
||||
+ * Returns: A newly allocated string. |
||||
+ */ |
||||
+ |
||||
+gchar * |
||||
+fb_util_urlsafe_base64_encode(const guchar *data, gsize len); |
||||
+ |
||||
+/** |
||||
+ * fb_util_gen_sso_verifier: |
||||
+ * @challenge: base64 of sha256 of verifier |
||||
+ * @verifier: base64 of random data |
||||
+ * @req_id: base64 of random data |
||||
+ * |
||||
+ * Generates the challenge/response parameters used for the workchat SSO auth. |
||||
+ * All parameters are output parameters. |
||||
+ */ |
||||
+void |
||||
+fb_util_gen_sso_verifier(gchar **challenge, gchar **verifier, gchar **req_id); |
||||
+ |
||||
#endif /* _FACEBOOK_UTIL_H_ */ |
||||
Index: purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/data.c |
||||
=================================================================== |
||||
--- purple-facebook-0.9.6.orig/pidgin/libpurple/protocols/facebook/data.c |
||||
+++ purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/data.c |
||||
@@ -219,6 +219,13 @@ fb_data_load(FbData *fata) |
||||
ret = FALSE; |
||||
} |
||||
|
||||
+ if (purple_account_get_bool(acct, "work", FALSE)) { |
||||
+ g_value_init(&val, G_TYPE_BOOLEAN); |
||||
+ g_value_set_boolean(&val, TRUE); |
||||
+ g_object_set_property(G_OBJECT(priv->api), "work", &val); |
||||
+ g_value_unset(&val); |
||||
+ } |
||||
+ |
||||
fb_api_rehash(priv->api); |
||||
return ret; |
||||
} |
@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
|
||||
Index: purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/facebook.c |
||||
=================================================================== |
||||
--- purple-facebook-0.9.6.orig/pidgin/libpurple/protocols/facebook/facebook.c |
||||
+++ purple-facebook-0.9.6/pidgin/libpurple/protocols/facebook/facebook.c |
||||
@@ -299,6 +299,10 @@ fb_cb_api_contacts(FbApi *api, GSList *u |
||||
fb_data_image_add(fata, user->icon, fb_cb_icon, |
||||
bdy, NULL); |
||||
} |
||||
+ |
||||
+ if (purple_account_get_bool(acct, "inactive-as-away", FALSE)) { |
||||
+ purple_protocol_got_user_status(acct, uid, purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY), NULL); |
||||
+ } |
||||
} |
||||
|
||||
fb_data_image_queue(fata); |
||||
@@ -351,6 +355,10 @@ fb_cb_api_contacts_delta(FbApi *api, GSL |
||||
purple_blist_add_buddy(bdy, NULL, grp, NULL); |
||||
|
||||
purple_buddy_set_server_alias(bdy, user->name); |
||||
+ |
||||
+ if (purple_account_get_bool(acct, "inactive-as-away", FALSE)) { |
||||
+ purple_protocol_got_user_status(acct, uid, purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY), NULL); |
||||
+ } |
||||
} |
||||
|
||||
for (l = removed; l != NULL; l = l->next) { |
||||
@@ -631,7 +639,7 @@ fb_cb_api_presences(FbApi *api, GSList * |
||||
if (pres->active) { |
||||
pstat = PURPLE_STATUS_AVAILABLE; |
||||
} else { |
||||
- pstat = PURPLE_STATUS_OFFLINE; |
||||
+ pstat = purple_account_get_bool(acct, "inactive-as-away", FALSE) ? PURPLE_STATUS_AWAY : PURPLE_STATUS_OFFLINE; |
||||
} |
||||
|
||||
FB_ID_TO_STR(pres->uid, uid); |
||||
@@ -1740,6 +1748,10 @@ purple_init_plugin(PurplePlugin *plugin) |
||||
"group-chat-open", TRUE); |
||||
opts = g_list_prepend(opts, opt); |
||||
|
||||
+ opt = purple_account_option_bool_new(_("Show inactive buddies as away"), |
||||
+ "inactive-as-away", FALSE); |
||||
+ opts = g_list_prepend(opts, opt); |
||||
+ |
||||
opt = purple_account_option_bool_new(_("Login as a Workplace account"), |
||||
"work", FALSE); |
||||
opts = g_list_prepend(opts, opt); |
Loading…
Reference in new issue