Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/EionRobb/skype4pidgin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordequis <dx@dxzone.com.ar>2017-06-21 09:07:06 +0300
committerdequis <dx@dxzone.com.ar>2017-06-21 09:35:38 +0300
commitd48d9d509e8d033f0868ff724afb681124ed9f8c (patch)
tree63ce78778e82d0dd4d9b7b1dc9c78d5a4d4b2455
parent1b11ad7c96feaaa4161e13c899981614452924a0 (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.c21
-rw-r--r--skypeweb/skypeweb_login.c247
-rw-r--r--skypeweb/skypeweb_login.h2
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 */