diff options
author | dequis <dx@dxzone.com.ar> | 2017-06-21 09:07:06 +0300 |
---|---|---|
committer | dequis <dx@dxzone.com.ar> | 2017-06-21 09:35:38 +0300 |
commit | d48d9d509e8d033f0868ff724afb681124ed9f8c (patch) | |
tree | 63ce78778e82d0dd4d9b7b1dc9c78d5a4d4b2455 | |
parent | 1b11ad7c96feaaa4161e13c899981614452924a0 (diff) |
Add alternative login methods
These methods return better error messages, should be better at handling
reconnections (because only obvious auth errors are marked as such), and
will probably work with 2FA when combined with app passwords.
This adds two closely related methods that return skypetokens in a
fairly straightforward way.
- For skype usernames: api.skype.com/login/skypetoken
Takes an old style skype md5 hash.
- For microsoft accounts: api.skype.com/rps/skypetoken
Takes a ticket token (magic T) for "wl.skype.com"
The only complicated part is getting that token. One way could be to use
oauth with the code that is already implemented, but that's no fun.
Instead, this uses SOAP login (login.live.com/RST.srf) from the MSN era.
A little verbose but very reliable and simpler than scraping HTML.
And it gets decent error messages.
This is disabled by default, can be enabled with the "Use alternative
login method" setting.
Things left to be explored:
- It's technically possible to use SOAP login for skype usernames too,
but I got "Profile accrual is required" when I tried to request the
wl.skype.com scope. It works for other scopes.
- wl.skype.com and lw.skype.com are not the same thing, apparently.
- I'm not doing anything regarding the 24 hour expiration. I'm not sure
the term "refresh token" makes sense.
- This could be combined with oauth too, for fun.
-rw-r--r-- | skypeweb/libskypeweb.c | 21 | ||||
-rw-r--r-- | skypeweb/skypeweb_login.c | 247 | ||||
-rw-r--r-- | skypeweb/skypeweb_login.h | 2 |
3 files changed, 265 insertions, 5 deletions
diff --git a/skypeweb/libskypeweb.c b/skypeweb/libskypeweb.c index 6953fcd..c46de02 100644 --- a/skypeweb/libskypeweb.c +++ b/skypeweb/libskypeweb.c @@ -345,11 +345,19 @@ skypeweb_login(PurpleAccount *account) sa->keepalive_pool = purple_http_keepalive_pool_new();
purple_http_keepalive_pool_set_limit_per_host(sa->keepalive_pool, SKYPEWEB_MAX_CONNECTIONS);
sa->conns = purple_http_connection_set_new();
-
- if (purple_account_get_string(account, "refresh-token", NULL) && purple_account_get_remember_password(account)) {
- skypeweb_refresh_token_login(sa);
+
+ if (purple_account_get_bool(account, "alt-login", FALSE)) {
+ if (!SKYPEWEB_BUDDY_IS_MSN(purple_account_get_username(account))) {
+ skypeweb_begin_skyper_login(sa);
+ } else {
+ skypeweb_begin_soapy_login(sa);
+ }
} else {
- skypeweb_begin_oauth_login(sa);
+ if (purple_account_get_string(account, "refresh-token", NULL) && purple_account_get_remember_password(account)) {
+ skypeweb_refresh_token_login(sa);
+ } else {
+ skypeweb_begin_oauth_login(sa);
+ }
}
if (!conversation_updated_signal) {
@@ -743,7 +751,7 @@ skypeweb_protocol_init(PurpleProtocol *prpl_info) {
PurpleProtocol *info = prpl_info;
#endif
- PurpleAccountOption *option, *typing_type1, *typing_type2;
+ PurpleAccountOption *option, *typing_type1, *typing_type2, *alt_login;
PurpleBuddyIconSpec icon_spec = {"jpeg", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY};
//PurpleProtocol
@@ -753,16 +761,19 @@ skypeweb_protocol_init(PurpleProtocol *prpl_info) option = purple_account_option_bool_new("", "", FALSE);
typing_type1 = purple_account_option_bool_new(N_("Show 'Typing' status as system message in chat window."), "show-typing-as-text", FALSE);
typing_type2 = purple_account_option_bool_new(N_("Show 'Typing' status with 'Voice' icon near buddy name."), "show-typing-as-icon", FALSE);
+ alt_login = purple_account_option_bool_new(N_("Use alternative login method"), "alt-login", FALSE);
#if !PURPLE_VERSION_CHECK(3, 0, 0)
prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, typing_type1);
prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, typing_type2);
+ prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, alt_login);
prpl_info->icon_spec = icon_spec;
#else
prpl_info->account_options = g_list_append(prpl_info->account_options, option);
prpl_info->account_options = g_list_append(prpl_info->account_options, typing_type1);
prpl_info->account_options = g_list_append(prpl_info->account_options, typing_type2);
+ prpl_info->account_options = g_list_append(prpl_info->account_options, alt_login);
prpl_info->icon_spec = &icon_spec;
#endif
diff --git a/skypeweb/skypeweb_login.c b/skypeweb/skypeweb_login.c index 9d6118e..dd22d4d 100644 --- a/skypeweb/skypeweb_login.c +++ b/skypeweb/skypeweb_login.c @@ -317,3 +317,250 @@ skypeweb_refresh_token_login(SkypeWebAccount *sa) purple_connection_update_progress(sa->pc, _("Authenticating"), 2, 4);
}
+
+
+static void
+skypeweb_login_did_got_api_skypetoken(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
+{
+ SkypeWebAccount *sa = user_data;
+ const gchar *data;
+ gsize len;
+ JsonParser *parser = NULL;
+ JsonNode *node;
+ JsonObject *obj;
+ gchar *error = NULL;
+ PurpleConnectionError error_type = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+
+ data = purple_http_response_get_data(response, &len);
+
+ parser = json_parser_new();
+ if (!json_parser_load_from_data(parser, data, len, NULL)) {
+ goto fail;
+ }
+
+ node = json_parser_get_root(parser);
+ if (node == NULL || json_node_get_node_type(node) != JSON_NODE_OBJECT) {
+ goto fail;
+ }
+ obj = json_node_get_object(node);
+
+ if (!json_object_has_member(obj, "skypetoken")) {
+ JsonObject *status = json_object_get_object_member(obj, "status");
+ if (status) {
+ //{"status":{"code":40120,"text":"Authentication failed. Bad username or password."}}
+ error = g_strdup_printf(_("Login error: %s (code %d)"),
+ json_object_get_string_member(status, "text"),
+ json_object_get_int_member(status, "code")
+ );
+ error_type = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+ }
+ goto fail;
+ }
+
+ sa->skype_token = g_strdup(json_object_get_string_member(obj, "skypetoken"));
+
+ skypeweb_do_all_the_things(sa);
+
+ g_object_unref(parser);
+ return;
+fail:
+ if (parser) {
+ g_object_unref(parser);
+ }
+
+ purple_connection_error(sa->pc, error_type,
+ error ? error : _("Failed getting Skype Token (alt)"));
+
+ g_free(error);
+}
+
+/* base64(md5(user + "\nskyper\n" + pass)) */
+static gchar *
+skypeweb_skyper_hash(const gchar *username, const gchar *password)
+{
+ guint8 hashed[16];
+ gsize len = sizeof(hashed);
+ GChecksum *gc;
+
+ gc = g_checksum_new(G_CHECKSUM_MD5);
+ g_checksum_update(gc, username, -1);
+ g_checksum_update(gc, "\nskyper\n", -1);
+ g_checksum_update(gc, password, -1);
+ g_checksum_get_digest(gc, hashed, &len);
+ g_checksum_free(gc);
+
+ return purple_base64_encode(hashed, len);
+}
+
+static void
+skypeweb_login_get_api_skypetoken(SkypeWebAccount *sa, const gchar *url, const gchar *username, const gchar *password)
+{
+ PurpleAccount *account = sa->account;
+ PurpleHttpRequest *request;
+ JsonObject *obj;
+ gchar *postdata;
+
+ obj = json_object_new();
+
+ if (username) {
+ json_object_set_string_member(obj, "username", username);
+ json_object_set_string_member(obj, "passwordHash", password);
+ } else {
+ json_object_set_int_member(obj, "partner", 999);
+ json_object_set_string_member(obj, "access_token", password);
+ }
+ json_object_set_string_member(obj, "scopes", "client");
+ postdata = skypeweb_jsonobj_to_string(obj);
+
+ request = purple_http_request_new(url);
+ purple_http_request_set_method(request, "POST");
+ purple_http_request_set_contents(request, postdata, -1);
+ purple_http_request_header_set(request, "Accept", "application/json; ver=1.0");
+ purple_http_request_header_set(request, "Content-Type", "application/json");
+ purple_http_request(sa->pc, request, skypeweb_login_did_got_api_skypetoken, sa);
+ purple_http_request_unref(request);
+
+ g_free(postdata);
+ json_object_unref(obj);
+}
+
+static void
+skypeweb_login_soap_got_token(SkypeWebAccount *sa, gchar *token)
+{
+ const gchar *login_url = "https://api.skype.com/rps/skypetoken";
+
+ skypeweb_login_get_api_skypetoken(sa, login_url, NULL, token);
+}
+
+static void
+skypeweb_login_did_soap(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
+{
+ SkypeWebAccount *sa = user_data;
+ const gchar *data;
+ gsize len;
+ PurpleXmlNode *envelope, *main, *node, *fault;
+ gchar *token;
+ const char *error = NULL;
+
+ data = purple_http_response_get_data(response, &len);
+ envelope = purple_xmlnode_from_str(data, len);
+
+ if (!data) {
+ error = _("Error parsing SOAP response");
+ goto fail;
+ }
+
+ main = purple_xmlnode_get_child(envelope, "Body/RequestSecurityTokenResponseCollection/RequestSecurityTokenResponse");
+
+ if ((fault = purple_xmlnode_get_child(envelope, "Fault")) ||
+ (main && (fault = purple_xmlnode_get_child(main, "Fault")))) {
+ gchar *code, *string, *error_;
+
+ code = purple_xmlnode_get_data(purple_xmlnode_get_child(fault, "faultcode"));
+ string = purple_xmlnode_get_data(purple_xmlnode_get_child(fault, "faultstring"));
+
+ if (purple_strequal(code, "wsse:FailedAuthentication")) {
+ error_ = g_strdup_printf(_("Login error: Bad username or password (%s)"), string);
+ } else {
+ error_ = g_strdup_printf(_("Login error: %s - %s"), code, string);
+ }
+
+ purple_connection_error(sa->pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, error_);
+
+ g_free(code);
+ g_free(string);
+ g_free(error_);
+ goto fail;
+ }
+
+ node = purple_xmlnode_get_child(main, "RequestedSecurityToken/BinarySecurityToken");
+
+ if (!node) {
+ error = _("Error getting BinarySecurityToken");
+ goto fail;
+ }
+
+ token = purple_xmlnode_get_data(node);
+ skypeweb_login_soap_got_token(sa, token);
+ g_free(token);
+
+fail:
+ if (error) {
+ purple_connection_error(sa->pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error);
+ }
+ purple_xmlnode_free(envelope);
+ return;
+}
+
+#define SIMPLE_OBJECT_ACCESS_PROTOCOL \
+"<Envelope xmlns='http://schemas.xmlsoap.org/soap/envelope/'\n" \
+" xmlns:wsse='http://schemas.xmlsoap.org/ws/2003/06/secext'\n" \
+" xmlns:wsp='http://schemas.xmlsoap.org/ws/2002/12/policy'\n" \
+" xmlns:wsa='http://schemas.xmlsoap.org/ws/2004/03/addressing'\n" \
+" xmlns:wst='http://schemas.xmlsoap.org/ws/2004/04/trust'\n" \
+" xmlns:ps='http://schemas.microsoft.com/Passport/SoapServices/PPCRL'>\n" \
+" <Header>\n" \
+" <wsse:Security>\n" \
+" <wsse:UsernameToken Id='user'>\n" \
+" <wsse:Username>%s</wsse:Username>\n" \
+" <wsse:Password>%s</wsse:Password>\n" \
+" </wsse:UsernameToken>\n" \
+" </wsse:Security>\n" \
+" </Header>\n" \
+" <Body>\n" \
+" <ps:RequestMultipleSecurityTokens Id='RSTS'>\n" \
+" <wst:RequestSecurityToken Id='RST0'>\n" \
+" <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\n" \
+" <wsp:AppliesTo>\n" \
+" <wsa:EndpointReference>\n" \
+" <wsa:Address>wl.skype.com</wsa:Address>\n" \
+" </wsa:EndpointReference>\n" \
+" </wsp:AppliesTo>\n" \
+" <wsse:PolicyReference URI='MBI_SSL'></wsse:PolicyReference>\n" \
+" </wst:RequestSecurityToken>\n" \
+" </ps:RequestMultipleSecurityTokens>\n" \
+" </Body>\n" \
+"</Envelope>" \
+
+void
+skypeweb_begin_soapy_login(SkypeWebAccount *sa)
+{
+ PurpleAccount *account = sa->account;
+ const gchar *login_url = "https://login.live.com/RST.srf";
+ const gchar *template = SIMPLE_OBJECT_ACCESS_PROTOCOL;
+ gchar *postdata;
+ PurpleHttpRequest *request;
+
+ postdata = g_markup_printf_escaped(template,
+ purple_account_get_username(account),
+ purple_connection_get_password(sa->pc)
+ );
+
+ request = purple_http_request_new(login_url);
+ purple_http_request_set_method(request, "POST");
+ purple_http_request_set_contents(request, postdata, -1);
+ purple_http_request_header_set(request, "Accept", "*/*");
+ purple_http_request_header_set(request, "Content-Type", "text/xml; charset=UTF-8");
+ purple_http_request(sa->pc, request, skypeweb_login_did_soap, sa);
+ purple_http_request_unref(request);
+
+ purple_connection_update_progress(sa->pc, _("Authenticating"), 2, 4);
+
+ g_free(postdata);
+}
+
+void
+skypeweb_begin_skyper_login(SkypeWebAccount *sa)
+{
+ gchar *hash;
+ const gchar *login_url = "https://api.skype.com/login/skypetoken";
+ const gchar *username = purple_account_get_username(sa->account);
+
+ hash = skypeweb_skyper_hash(username, purple_connection_get_password(sa->pc));
+
+ skypeweb_login_get_api_skypetoken(sa, login_url, username, hash);
+
+ purple_connection_update_progress(sa->pc, _("Authenticating"), 2, 4);
+
+ g_free(hash);
+}
diff --git a/skypeweb/skypeweb_login.h b/skypeweb/skypeweb_login.h index d7a6291..b8ed589 100644 --- a/skypeweb/skypeweb_login.h +++ b/skypeweb/skypeweb_login.h @@ -28,5 +28,7 @@ void skypeweb_logout(SkypeWebAccount *sa); void skypeweb_begin_web_login(SkypeWebAccount *sa); void skypeweb_begin_oauth_login(SkypeWebAccount *sa); void skypeweb_refresh_token_login(SkypeWebAccount *sa); +void skypeweb_begin_soapy_login(SkypeWebAccount *sa); +void skypeweb_begin_skyper_login(SkypeWebAccount *sa); #endif /* SKYPEWEB_LOGIN_H */ |