+static void
+handle_typeahead (saver_info *si)
+{
+ passwd_dialog_data *pw = si->pw_data;
+ int i;
+ if (!si->unlock_typeahead)
+ return;
+
+ pw->passwd_changed_p = True;
+
+ i = strlen (si->unlock_typeahead);
+ if (i >= sizeof(pw->typed_passwd) - 1)
+ i = sizeof(pw->typed_passwd) - 1;
+
+ memcpy (pw->typed_passwd, si->unlock_typeahead, i);
+ pw->typed_passwd [i] = 0;
+
+ memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
+ si->unlock_typeahead[i] = 0;
+ update_passwd_window (si, si->unlock_typeahead, pw->ratio);
+
+ free (si->unlock_typeahead);
+ si->unlock_typeahead = 0;
+}
+
+
+/**
+ * Returns a copy of the input string with trailing whitespace removed.
+ * Whitespace is anything considered so by isspace().
+ * It is safe to call this with NULL, in which case NULL will be returned.
+ * The returned string (if not NULL) should be freed by the caller with free().
+ */
+static char *
+remove_trailing_whitespace(const char *str)
+{
+ size_t len;
+ char *newstr, *chr;
+
+ if (!str)
+ return NULL;
+
+ len = strlen(str);
+
+ newstr = malloc(len + 1);
+ (void) strcpy(newstr, str);
+
+ if (!newstr)
+ return NULL;
+
+ chr = newstr + len;
+ while (isspace(*--chr) && chr >= newstr)
+ *chr = '\0';
+
+ return newstr;
+}
+
+
+/*
+ * The authentication conversation function.
+ * Like a PAM conversation function, this accepts multiple messages in a single
+ * round. It then splits them into individual messages for display on the
+ * passwd dialog. A message sequence of info or error followed by a prompt will
+ * be reduced into a single dialog window.
+ *
+ * Returns 0 on success or -1 if some problem occurred (cancelled auth, OOM, ...)
+ */
+int
+gui_auth_conv(int num_msg,
+ const struct auth_message auth_msgs[],
+ struct auth_response **resp,
+ saver_info *si)
+{
+ int i;
+ const char *info_msg, *prompt;
+ struct auth_response *responses;
+
+ if (!(responses = calloc(num_msg, sizeof(struct auth_response))))
+ goto fail;
+
+ for (i = 0; i < num_msg; ++i)
+ {
+ info_msg = prompt = NULL;
+
+ /* See if there is a following message that can be shown at the same
+ * time */
+ if (auth_msgs[i].type == AUTH_MSGTYPE_INFO
+ && i+1 < num_msg
+ && ( auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_NOECHO
+ || auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_ECHO)
+ )
+ {
+ info_msg = auth_msgs[i].msg;
+ prompt = auth_msgs[++i].msg;
+ }
+ else
+ {
+ if ( auth_msgs[i].type == AUTH_MSGTYPE_INFO
+ || auth_msgs[i].type == AUTH_MSGTYPE_ERROR)
+ info_msg = auth_msgs[i].msg;
+ else
+ prompt = auth_msgs[i].msg;
+ }
+
+ {
+ char *info_msg_trimmed, *prompt_trimmed;
+
+ /* Trailing whitespace looks bad in a GUI */
+ info_msg_trimmed = remove_trailing_whitespace(info_msg);
+ prompt_trimmed = remove_trailing_whitespace(prompt);
+
+ make_passwd_window(si, info_msg_trimmed, prompt_trimmed,
+ auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO
+ ? True : False);
+
+ if (info_msg_trimmed)
+ free(info_msg_trimmed);
+
+ if (prompt_trimmed)
+ free(prompt_trimmed);
+ }
+
+ compose_status = calloc (1, sizeof (*compose_status));
+ if (!compose_status)
+ goto fail;
+
+ si->unlock_state = ul_read;
+
+ handle_typeahead (si);
+ passwd_event_loop (si);
+
+ if (si->unlock_state == ul_cancel)
+ goto fail;
+
+ responses[i].response = strdup(si->pw_data->typed_passwd);
+
+ /* Cache the first response to a PROMPT_NOECHO to save prompting for
+ * each auth mechanism. */
+ if (si->cached_passwd == NULL &&
+ auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO)
+ si->cached_passwd = strdup(responses[i].response);
+
+ free (compose_status);
+ compose_status = 0;
+ }
+
+ *resp = responses;
+
+ return (si->unlock_state == ul_finished) ? 0 : -1;
+
+fail:
+ if (compose_status)
+ free (compose_status);
+
+ if (responses)
+ {
+ for (i = 0; i < num_msg; ++i)
+ if (responses[i].response)
+ free (responses[i].response);
+ free (responses);
+ }
+
+ return -1;
+}
+
+
+void
+auth_finished_cb (saver_info *si)
+{
+ if (si->unlock_state == ul_fail)
+ {
+ make_passwd_window (si, "Authentication failed!", NULL, True);
+ sleep (2); /* Not very nice, I know */
+
+ /* Swallow any keyboard or mouse events that were received while the
+ * dialog was up */
+ {
+ XEvent e;
+ long mask = (KeyPressMask | KeyReleaseMask |
+ ButtonPressMask | ButtonReleaseMask);
+ while (XCheckMaskEvent (si->dpy, mask, &e))
+ ;
+ }
+ }
+
+ if (si->pw_data)
+ destroy_passwd_window (si);
+}
+
+