]> www.infradead.org Git - pidgin-chime.git/commitdiff
Add a fallback mechanism to get user input.
authorIsaac Jurado <ijurado@amazon.com>
Wed, 9 Aug 2017 13:31:24 +0000 (15:31 +0200)
committerIsaac Jurado <ijurado@amazon.com>
Wed, 9 Aug 2017 13:32:57 +0000 (15:32 +0200)
Not all libpurple users implement request fields.  Therefore, implement an
alternative way of obtaining credentials by using simpler UI operations.

Fixes #1.

login-amazon.c
login-warpdrive.c
login.c

index df5f7f1e551e39ce823399f7aae56dd11e8b2655..0031ab3b1b0a14b5f405ea3233c541e123a4ac01 100644 (file)
 #define SIGN_IN_FORM  "//form[@id='ap_signin_form']"
 #define CONSENT_FORM  "//form[@name='consent-form']"
 #define PASS_FIELD  "password"
+#define PASS_TITLE  _("Amazon Login")
+#define PASS_LABEL  _("Please enter the password for <%s>")
+#define PASS_FAIL   _("Authentication failed")
+#define PASS_OK  _("Sign In")
+#define PASS_CANCEL  _("Cancel")
 
 struct login_amzn {
        struct login b;  /* Base */
@@ -107,12 +112,15 @@ static void login_result_cb(SoupSession *session, SoupMessage *msg, gpointer dat
        chime_login_token_cb(session, msg, state);
 }
 
-static void send_credentials(struct login_amzn *state, PurpleRequestFields *fields)
+static void send_credentials(struct login_amzn *state, const gchar *password)
 {
        SoupMessage *msg;
-       const gchar *password;
 
-       password = purple_request_fields_get_string(fields, PASS_FIELD);
+       if (!(password && *password)) {
+               request_credentials(state, TRUE);
+               return;
+       }
+
        g_hash_table_insert(state->form->params,
                            g_strdup(state->form->password_name),
                            g_strdup(password));
@@ -125,7 +133,12 @@ static void send_credentials(struct login_amzn *state, PurpleRequestFields *fiel
        clear_form(state);
 }
 
-static void request_credentials(struct login_amzn *state, gboolean retry)
+static void gather_credentials_and_send(struct login_amzn *state, PurpleRequestFields *fields)
+{
+       send_credentials(state, purple_request_fields_get_string(fields, PASS_FIELD));
+}
+
+static void request_credentials_with_fields(struct login_amzn *state, gboolean retry)
 {
        PurpleRequestField *password;
        PurpleRequestFieldGroup *group;
@@ -141,18 +154,42 @@ static void request_credentials(struct login_amzn *state, gboolean retry)
        purple_request_field_group_add_field(group, password);
 
        purple_request_fields_add_group(fields, group);
-       text = g_strdup_printf(_("Please enter the password for <%s>"),
-                              login_account_email(state));
+       text = g_strdup_printf(PASS_LABEL, login_account_email(state));
+
        purple_request_fields(login_connection(state)->prpl_conn,
-                             _("Amazon Login"), text,
-                             retry ? _("Authentication failed") : NULL,
-                             fields,
-                             _("Sign In"), G_CALLBACK(send_credentials),
-                             _("Cancel"), G_CALLBACK(chime_login_cancel_ui),
+                             PASS_TITLE, text, retry ? PASS_FAIL : NULL, fields,
+                             PASS_OK, G_CALLBACK(gather_credentials_and_send),
+                             PASS_CANCEL, G_CALLBACK(chime_login_cancel_ui),
                              login_connection(state)->prpl_conn->account,
                              NULL, NULL, state);
 }
 
+static void request_credentials_with_input(struct login_amzn *state, gboolean retry)
+{
+       gchar *text;
+
+       text = g_strdup_printf(PASS_LABEL, login_account_email(state));
+
+       purple_request_input(login_connection(state)->prpl_conn,
+                            PASS_TITLE, text, retry ? PASS_FAIL : NULL, NULL,
+                            FALSE, TRUE, (gchar *) "password",
+                            PASS_OK, G_CALLBACK(send_credentials),
+                            PASS_CANCEL, G_CALLBACK(chime_login_cancel_ui),
+                            login_connection(state)->prpl_conn->account,
+                            NULL, NULL, state);
+}
+
+static void request_credentials(struct login_amzn *state, gboolean retry)
+{
+       /* When loging in with Amazon, we only request a password.  Therefore we
+          may only use request_input.  However, request_fields provides a
+          better user experience, so we still prefer it */
+       if (purple_request_get_ui_ops()->request_fields)
+               request_credentials_with_fields(state, retry);
+       else
+               request_credentials_with_input(state, retry);
+}
+
 void chime_login_amazon(SoupSession *session, SoupMessage *msg, gpointer data)
 {
        struct login_amzn *state;
index 20a8dcb4c4edae1f8b86af20803e56af490476e1..82ae6b7f003952c48482c6209bf19072ae3c0de6 100644 (file)
 #define GWT_RPC_PATH  "WarpDriveLogin/GalaxyInternalService"
 #define USER_FIELD  "username"
 #define PASS_FIELD  "password"
+#define AUTH_TITLE  _("Corporate Login")
+#define AUTH_FAIL  _("Authentication failed")
+#define AUTH_SEND  _("Sign In")
+#define AUTH_CANCEL  _("Cancel")
 
 #define discovery_failure(state, label)                                        \
        do {                                                            \
@@ -40,6 +44,7 @@ struct login_wd {
        gchar *client_id;
        gchar *redirect_url;
        gchar *region;
+       gchar *username;
        /* GWT-RPC specific parameters */
        SoupURI *gwt_rpc_uri;
        gchar *gwt_module_base;
@@ -56,6 +61,7 @@ static void free_wd_state(struct login_wd *state)
        g_free(state->client_id);
        g_free(state->redirect_url);
        g_free(state->region);
+       g_free(state->username);
        soup_uri_free(state->gwt_rpc_uri);
        g_free(state->gwt_module_base);
        g_free(state->gwt_permutation);
@@ -75,6 +81,14 @@ static gchar *escape_backslash(const gchar *src)
        return g_string_free(dst, FALSE);
 }
 
+static void set_username(struct login_wd *state, const gchar *username)
+{
+       if (state->username)
+               g_free(state->username);
+
+       state->username = escape_backslash(username);
+}
+
 /*
  * Compose a GWT-RPC request.  For more information about how these requests are
  * formatted, check the following links:
@@ -259,26 +273,44 @@ static void gwt_auth_cb(SoupSession *session, SoupMessage *msg, gpointer data)
        g_strfreev(response);
 }
 
-static void send_credentials(struct login_wd *state, PurpleRequestFields *fields)
+static void send_credentials(struct login_wd *state, const gchar *password)
 {
        SoupMessage *msg;
-       gchar *username, *password;
+       gchar *escaped;
        static const gchar *type = "com.amazonaws.warpdrive.console.shared.LoginRequest_v4/3859384737";
 
-       username = escape_backslash(purple_request_fields_get_string(fields, USER_FIELD));
-       password = escape_backslash(purple_request_fields_get_string(fields, PASS_FIELD));
+       if (!(password && *password)) {
+               request_credentials(state, TRUE);
+               return;
+       }
+
+       escaped = escape_backslash(password);
 
        msg = gwt_request(state, WARPDRIVE_INTERFACE, "authenticateUser", 11,
                          type, type, "", "", state->client_id, "", NULL,
-                         state->directory, password, "", username);
+                         state->directory, escaped, "", state->username);
 
        soup_session_queue_message(login_session(state), msg, gwt_auth_cb, state);
 
-       g_free(password);
-       g_free(username);
+       g_free(escaped);
 }
 
-static void request_credentials(struct login_wd *state, gboolean retry)
+static void gather_credentials_and_send(struct login_wd *state, PurpleRequestFields *fields)
+{
+       const gchar *username, *password;
+
+       username = purple_request_fields_get_string(fields, USER_FIELD);
+       password = purple_request_fields_get_string(fields, PASS_FIELD);
+       if (!(username && *username && password && *password)) {
+               request_credentials(state, TRUE);
+               return;
+       }
+
+       set_username(state, username);
+       send_credentials(state, password);
+}
+
+static void request_credentials_with_fields(struct login_wd *state, gboolean retry)
 {
        PurpleRequestField *username, *password;
        PurpleRequestFieldGroup *group;
@@ -298,17 +330,52 @@ static void request_credentials(struct login_wd *state, gboolean retry)
 
        purple_request_fields_add_group(fields, group);
 
-       purple_request_fields(login_connection(state)->prpl_conn,
-                             _("Corporate Login"),
+       purple_request_fields(login_connection(state)->prpl_conn, AUTH_TITLE,
                              _("Please sign in with your corporate credentials"),
-                             retry ? _("Authentication failed") : NULL,
-                             fields,
-                             _("Sign In"), G_CALLBACK(send_credentials),
-                             _("Cancel"), G_CALLBACK(chime_login_cancel_ui),
+                             retry ? AUTH_FAIL : NULL, fields,
+                             AUTH_SEND, G_CALLBACK(gather_credentials_and_send),
+                             AUTH_CANCEL, G_CALLBACK(chime_login_cancel_ui),
                              login_connection(state)->prpl_conn->account,
                              NULL, NULL, state);
 }
 
+static void request_password_with_input(struct login_wd *state, const gchar *username)
+{
+       if (!(username && *username)) {
+               request_credentials(state, TRUE);
+               return;
+       }
+
+       set_username(state, username);
+
+       purple_request_input(login_connection(state)->prpl_conn, AUTH_TITLE,
+                            _("Corporate password"), NULL,
+                            NULL, FALSE, TRUE, (gchar *) "password",
+                            AUTH_SEND, G_CALLBACK(send_credentials),
+                            AUTH_CANCEL, G_CALLBACK(chime_login_cancel_ui),
+                            login_connection(state)->prpl_conn->account,
+                            NULL, NULL, state);
+}
+
+static void request_username_with_input(struct login_wd *state, gboolean retry)
+{
+       purple_request_input(login_connection(state)->prpl_conn, AUTH_TITLE,
+                            _("Corporate username"), retry ? AUTH_FAIL : NULL,
+                            NULL, FALSE, FALSE, NULL,
+                            _("OK"), G_CALLBACK(request_password_with_input),
+                            AUTH_CANCEL, G_CALLBACK(chime_login_cancel_ui),
+                            login_connection(state)->prpl_conn->account,
+                            NULL, NULL, state);
+}
+
+static void request_credentials(struct login_wd *state, gboolean retry)
+{
+       if (purple_request_get_ui_ops()->request_fields)
+               request_credentials_with_fields(state, retry);
+       else
+               request_username_with_input(state, retry);
+}
+
 static void gwt_region_cb(SoupSession *session, SoupMessage *msg, gpointer data)
 {
        gboolean ok;
diff --git a/login.c b/login.c
index 35a52708eeb87102ffbb0015c5852e24190c69ce..e3c339e0d196f7ab1a1da74945c541309cd82fb1 100644 (file)
--- a/login.c
+++ b/login.c
@@ -16,6 +16,7 @@
  */
 
 #include <debug.h>
+#include <request.h>
 #include <glib/gi18n.h>
 #include <libxml/HTMLparser.h>
 #include <libxml/tree.h>
@@ -463,11 +464,20 @@ static void signin_page_cb(SoupSession *session, SoupMessage *msg, gpointer data
 void chime_initial_login(ChimeConnection *cxn)
 {
        ChimeConnectionPrivate *priv;
+       PurpleRequestUiOps *ui_ops;
        SoupMessage *msg;
        struct login *state;
 
        g_return_if_fail(CHIME_IS_CONNECTION(cxn));
 
+       ui_ops = purple_request_get_ui_ops();
+       if (!(ui_ops && ui_ops->request_action && ui_ops->request_input)) {
+               purple_debug_error("chime", "Cannot proceed to Chime login: missing essential UI operations");
+               chime_connection_fail(cxn, CHIME_ERROR_AUTH_FAILED,
+                                     _("Cannot login to Chime with this software"));
+               return;
+       }
+
        state = g_new0(struct login, 1);
        state->connection = g_object_ref(cxn);
        state->session = soup_session_new_with_options(SOUP_SESSION_ADD_FEATURE_BY_TYPE,