- Give the user a "remember password" option that they can uncheck if they don't want Geary to remember their password. You've seen most of this part of the patch before, aside from a few bugfixes. - Display a nicer dialog when re-prompting the user for their password. This only shows (editable) fields for "password" and "remember password" by default, with (non-editable) fields for "Service" and "Real name" available in an expander. - Fix a crash that occurred whenever an account is connected when there was previously another account (or the same account) connected.
This commit is contained in:
parent
8ff56dd947
commit
c3340e91fb
14 changed files with 504 additions and 111 deletions
|
|
@ -185,6 +185,7 @@ client/ui/message-list-cell-renderer.vala
|
|||
client/ui/message-list-store.vala
|
||||
client/ui/message-list-view.vala
|
||||
client/ui/message-viewer.vala
|
||||
client/ui/password-dialog.vala
|
||||
client/ui/preferences-dialog.vala
|
||||
|
||||
client/ui/sidebar/sidebar-branch.vala
|
||||
|
|
|
|||
|
|
@ -165,9 +165,24 @@ along with Geary; if not, write to the Free Software Foundation, Inc.,
|
|||
return;
|
||||
}
|
||||
|
||||
private void initialize_account(bool replace_existing_data = false) {
|
||||
private void set_account(Geary.EngineAccount? account) {
|
||||
if (this.account == account)
|
||||
return;
|
||||
|
||||
if (this.account != null)
|
||||
this.account.report_problem.disconnect(on_report_problem);
|
||||
|
||||
this.account = account;
|
||||
|
||||
if (this.account != null)
|
||||
this.account.report_problem.connect(on_report_problem);
|
||||
|
||||
controller.connect_account(this.account);
|
||||
}
|
||||
|
||||
private void initialize_account() {
|
||||
string? username = get_username();
|
||||
if (username == null || replace_existing_data)
|
||||
if (username == null)
|
||||
create_account(username);
|
||||
else
|
||||
open_account(username);
|
||||
|
|
@ -213,16 +228,14 @@ along with Geary; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
if (success) {
|
||||
account_information.store_async.begin(cancellable);
|
||||
account = account_information.get_account();
|
||||
account.report_problem.connect(on_report_problem);
|
||||
controller.connect_account(account);
|
||||
set_account(account_information.get_account());
|
||||
} else {
|
||||
Geary.AccountInformation new_account_information =
|
||||
request_account_information(account_information);
|
||||
|
||||
// If the user refused to enter account information.
|
||||
if (new_account_information == null) {
|
||||
account = null;
|
||||
set_account(null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -230,30 +243,29 @@ along with Geary; if not, write to the Free Software Foundation, Inc.,
|
|||
}
|
||||
}
|
||||
|
||||
private void open_account(string username) {
|
||||
string? password = get_password(username);
|
||||
if (password == null) {
|
||||
password = request_password(username);
|
||||
|
||||
// If the user refused to enter a password.
|
||||
if (password == null) {
|
||||
account = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we know password is non-null.
|
||||
Geary.Credentials credentials = new Geary.Credentials(username, password);
|
||||
private void open_account(string username, string? old_password = null, Cancellable? cancellable = null) {
|
||||
Geary.Credentials credentials = new Geary.Credentials(username, null);
|
||||
Geary.AccountInformation account_information = new Geary.AccountInformation(credentials);
|
||||
try {
|
||||
account_information.load_info_from_file();
|
||||
} catch (Error err) {
|
||||
error("Problem loading account information: %s", err.message);
|
||||
// TODO: Handle this more gracefully?
|
||||
error("Problem loading account information from file: %s", err.message);
|
||||
}
|
||||
|
||||
account = account_information.get_account();
|
||||
account.report_problem.connect(on_report_problem);
|
||||
controller.connect_account(account);
|
||||
bool remember_password = account_information.remember_password;
|
||||
string? password = get_password(account_information.credentials.user, old_password, ref remember_password);
|
||||
// If there was no saved password and the user refused to enter a password.
|
||||
if (password == null) {
|
||||
set_account(null);
|
||||
return;
|
||||
}
|
||||
|
||||
account_information.remember_password = remember_password;
|
||||
account_information.store_async.begin(cancellable);
|
||||
|
||||
account_information.credentials.pass = password;
|
||||
set_account(account_information.get_account());
|
||||
}
|
||||
|
||||
private string? get_username() {
|
||||
|
|
@ -270,34 +282,51 @@ along with Geary; if not, write to the Free Software Foundation, Inc.,
|
|||
return null;
|
||||
}
|
||||
|
||||
private string? get_password(string username) {
|
||||
// TODO: For now we always get the password from the keyring. This will change when we
|
||||
// allow users to not save their password.
|
||||
string? password = keyring_get_password(username);
|
||||
return Geary.String.is_empty(password) ? null : password;
|
||||
}
|
||||
private string? get_password(string username, string? old_password, ref bool remember_password) {
|
||||
string? password = null;
|
||||
if (old_password == null && remember_password)
|
||||
password = keyring_get_password(username);
|
||||
|
||||
if (Geary.String.is_null_or_whitespace(password))
|
||||
password = request_password(username, old_password, out remember_password);
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
private string get_default_real_name() {
|
||||
string real_name = Environment.get_real_name();
|
||||
return real_name == "Unknown" ? "" : real_name;
|
||||
}
|
||||
|
||||
private string? request_password(string username) {
|
||||
// TODO: For now we use the full LoginDialog. This should be changed to a dialog that only
|
||||
// allows editting the password.
|
||||
|
||||
Geary.Credentials credentials = new Geary.Credentials(username, null);
|
||||
private string? request_password(string username, string? old_password, out bool remember_password) {
|
||||
Geary.Credentials credentials = new Geary.Credentials(username, old_password);
|
||||
|
||||
Geary.AccountInformation old_account_information = new Geary.AccountInformation(credentials);
|
||||
try {
|
||||
old_account_information.load_info_from_file();
|
||||
} catch (Error err) {
|
||||
debug("Problem loading account information: %s", err.message);
|
||||
old_account_information = null;
|
||||
// TODO: Handle this more gracefully?
|
||||
error("Error loading account information: %s", err.message);
|
||||
}
|
||||
|
||||
Geary.AccountInformation account_information = request_account_information(old_account_information);
|
||||
return account_information == null ? null : account_information.credentials.pass;
|
||||
PasswordDialog password_dialog = new PasswordDialog(old_account_information);
|
||||
if (!password_dialog.run()) {
|
||||
exit(1);
|
||||
remember_password = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
// password_dialog.password should never be null at this point. It will only be null when
|
||||
// password_dialog.run() returns false, in which case we have already exited/returned.
|
||||
string? password = password_dialog.password;
|
||||
remember_password = password_dialog.remember_password;
|
||||
|
||||
if (remember_password)
|
||||
keyring_save_password(new Geary.Credentials(username, password));
|
||||
else
|
||||
keyring_delete_password(username);
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
// Prompt the user for a service, real name, username, and password, and try to start Geary.
|
||||
|
|
@ -312,8 +341,10 @@ along with Geary; if not, write to the Free Software Foundation, Inc.,
|
|||
return null;
|
||||
}
|
||||
|
||||
// TODO: This should be optional.
|
||||
keyring_save_password(login_dialog.account_information.credentials);
|
||||
if (login_dialog.account_information.remember_password)
|
||||
keyring_save_password(login_dialog.account_information.credentials);
|
||||
else
|
||||
keyring_delete_password(login_dialog.account_information.credentials.user);
|
||||
|
||||
return login_dialog.account_information;
|
||||
}
|
||||
|
|
@ -330,10 +361,9 @@ along with Geary; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
case Geary.Account.Problem.LOGIN_FAILED:
|
||||
debug("Login failed.");
|
||||
if (controller != null)
|
||||
controller.stop();
|
||||
Geary.Credentials old_credentials = account.get_account_information().credentials;
|
||||
account.report_problem.disconnect(on_report_problem);
|
||||
initialize_account(true);
|
||||
open_account(old_credentials.user, old_credentials.pass);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -77,10 +77,7 @@ public class GearyController {
|
|||
private int conversations_added_counter = 0;
|
||||
private Gee.LinkedList<ComposerWindow> composer_windows = new Gee.LinkedList<ComposerWindow>();
|
||||
|
||||
private Geary.EngineAccount? _account = null;
|
||||
private Geary.EngineAccount? account {
|
||||
get { return _account; }
|
||||
}
|
||||
private Geary.EngineAccount? account { get; private set; }
|
||||
|
||||
public GearyController() {
|
||||
// Setup actions.
|
||||
|
|
@ -245,13 +242,16 @@ public class GearyController {
|
|||
return entries;
|
||||
}
|
||||
|
||||
public void connect_account(Geary.EngineAccount? account) {
|
||||
if (_account == account)
|
||||
public void connect_account(Geary.EngineAccount? new_account) {
|
||||
if (account == new_account)
|
||||
return;
|
||||
|
||||
|
||||
// Disconnect the old account, if any.
|
||||
if (_account != null) {
|
||||
_account.folders_added_removed.disconnect(on_folders_added_removed);
|
||||
if (account != null) {
|
||||
cancel_folder();
|
||||
cancel_message();
|
||||
|
||||
account.folders_added_removed.disconnect(on_folders_added_removed);
|
||||
|
||||
Gtk.Action delete_message = GearyApplication.instance.actions.get_action(ACTION_DELETE_MESSAGE);
|
||||
delete_message.label = DEFAULT_DELETE_MESSAGE_LABEL;
|
||||
|
|
@ -260,30 +260,29 @@ public class GearyController {
|
|||
|
||||
main_window.title = GearyApplication.NAME;
|
||||
|
||||
main_window.folder_list.set_user_folders_root_name("");
|
||||
main_window.folder_list.remove_all_branches();
|
||||
}
|
||||
|
||||
_account = account;
|
||||
account = new_account;
|
||||
|
||||
// Connect the new account, if any.
|
||||
if (_account != null) {
|
||||
_account.folders_added_removed.connect(on_folders_added_removed);
|
||||
if (account != null) {
|
||||
account.folders_added_removed.connect(on_folders_added_removed);
|
||||
|
||||
// Personality-specific setup.
|
||||
if (_account.delete_is_archive()) {
|
||||
if (account.delete_is_archive()) {
|
||||
Gtk.Action delete_message = GearyApplication.instance.actions.get_action(ACTION_DELETE_MESSAGE);
|
||||
delete_message.label = _("Archive Message");
|
||||
delete_message.tooltip = _("Archive the selected conversation");
|
||||
delete_message.icon_name = "archive-insert";
|
||||
}
|
||||
|
||||
if (_account.get_account_information().service_provider == Geary.ServiceProvider.YAHOO)
|
||||
if (account.get_account_information().service_provider == Geary.ServiceProvider.YAHOO)
|
||||
main_window.title = GearyApplication.NAME + "!";
|
||||
|
||||
main_window.folder_list.set_user_folders_root_name(_account.get_user_folders_label());
|
||||
main_window.folder_list.set_user_folders_root_name(account.get_user_folders_label());
|
||||
load_folders.begin(cancellable_folder);
|
||||
}
|
||||
|
||||
load_folders.begin(cancellable_folder);
|
||||
}
|
||||
|
||||
private bool is_viewed_conversation(Geary.Conversation? conversation) {
|
||||
|
|
@ -336,12 +335,6 @@ public class GearyController {
|
|||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
cancel_folder();
|
||||
cancel_message();
|
||||
connect_account(null);
|
||||
}
|
||||
|
||||
private void on_folder_selected(Geary.Folder? folder) {
|
||||
if (folder == null) {
|
||||
debug("no folder selected");
|
||||
|
|
|
|||
|
|
@ -114,9 +114,7 @@ public class FolderList : Sidebar.Tree {
|
|||
base(new Gtk.TargetEntry[0], Gdk.DragAction.ASK, drop_handler);
|
||||
entry_selected.connect(on_entry_selected);
|
||||
|
||||
user_folder_group = new Sidebar.Grouping("", IconFactory.instance.label_folder_icon);
|
||||
user_folder_branch = new Sidebar.Branch(user_folder_group,
|
||||
Sidebar.Branch.Options.STARTUP_OPEN_GROUPING, user_folder_comparator);
|
||||
reset_user_folder_group();
|
||||
graft(user_folder_branch, int.MAX);
|
||||
|
||||
// Set self as a drag destination.
|
||||
|
|
@ -146,6 +144,12 @@ public class FolderList : Sidebar.Tree {
|
|||
user_folder_group.rename(name);
|
||||
}
|
||||
|
||||
private void reset_user_folder_group() {
|
||||
user_folder_group = new Sidebar.Grouping("", IconFactory.instance.label_folder_icon);
|
||||
user_folder_branch = new Sidebar.Branch(user_folder_group,
|
||||
Sidebar.Branch.Options.STARTUP_OPEN_GROUPING, user_folder_comparator);
|
||||
}
|
||||
|
||||
public void add_folder(Geary.Folder folder) {
|
||||
FolderEntry folder_entry = new FolderEntry(folder);
|
||||
|
||||
|
|
@ -174,6 +178,12 @@ public class FolderList : Sidebar.Tree {
|
|||
entries.set(folder.get_path(), branch.get_root());
|
||||
}
|
||||
|
||||
public void remove_all_branches() {
|
||||
prune_all();
|
||||
entries.clear();
|
||||
reset_user_folder_group();
|
||||
}
|
||||
|
||||
public void select_path(Geary.FolderPath path) {
|
||||
Sidebar.Entry? entry = get_entry_for_folder_path(path);
|
||||
if (entry != null)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ public class LoginDialog {
|
|||
private Gtk.Entry entry_password;
|
||||
private Gtk.Entry entry_real_name;
|
||||
private Gtk.ComboBoxText combo_service;
|
||||
private Gtk.CheckButton check_remember_password;
|
||||
|
||||
private Gtk.Alignment other_info;
|
||||
private Gtk.Entry entry_imap_host;
|
||||
|
|
@ -29,17 +30,18 @@ public class LoginDialog {
|
|||
|
||||
public LoginDialog.from_account_information(Geary.AccountInformation default_account_information) {
|
||||
this(default_account_information.real_name, default_account_information.credentials.user,
|
||||
default_account_information.credentials.pass, default_account_information.service_provider,
|
||||
default_account_information.imap_server_host, default_account_information.imap_server_port,
|
||||
default_account_information.imap_server_ssl, default_account_information.smtp_server_host,
|
||||
default_account_information.smtp_server_port, default_account_information.smtp_server_ssl);
|
||||
default_account_information.credentials.pass, default_account_information.remember_password,
|
||||
default_account_information.service_provider, default_account_information.imap_server_host,
|
||||
default_account_information.imap_server_port, default_account_information.imap_server_ssl,
|
||||
default_account_information.smtp_server_host, default_account_information.smtp_server_port,
|
||||
default_account_information.smtp_server_ssl);
|
||||
}
|
||||
|
||||
public LoginDialog(string default_real_name, string? default_username = null,
|
||||
string? default_password = null, int default_service_provider = -1,string? default_imap_host = null,
|
||||
uint16 default_imap_port = Geary.Imap.ClientConnection.DEFAULT_PORT_SSL, bool default_imap_ssl = true,
|
||||
string? default_smtp_host = null, uint16 default_smtp_port = Geary.Smtp.ClientConnection.DEFAULT_PORT_SSL,
|
||||
bool default_smtp_ssl = true) {
|
||||
public LoginDialog(string? default_real_name = null, string? default_username = null,
|
||||
string? default_password = null, bool default_remember_password = true, int default_service_provider = -1,
|
||||
string? default_imap_host = null, uint16 default_imap_port = Geary.Imap.ClientConnection.DEFAULT_PORT_SSL,
|
||||
bool default_imap_ssl = true, string? default_smtp_host = null,
|
||||
uint16 default_smtp_port = Geary.Smtp.ClientConnection.DEFAULT_PORT_SSL, bool default_smtp_ssl = true) {
|
||||
Gtk.Builder builder = GearyApplication.instance.create_builder("login.glade");
|
||||
|
||||
dialog = builder.get_object("LoginDialog") as Gtk.Dialog;
|
||||
|
|
@ -50,6 +52,7 @@ public class LoginDialog {
|
|||
combo_service = builder.get_object("service") as Gtk.ComboBoxText;
|
||||
entry_username = builder.get_object("username") as Gtk.Entry;
|
||||
entry_password = builder.get_object("password") as Gtk.Entry;
|
||||
check_remember_password = builder.get_object("remember_password") as Gtk.CheckButton;
|
||||
|
||||
other_info = builder.get_object("other_info") as Gtk.Alignment;
|
||||
entry_imap_host = builder.get_object("imap host") as Gtk.Entry;
|
||||
|
|
@ -74,6 +77,7 @@ public class LoginDialog {
|
|||
entry_real_name.set_text(default_real_name ?? "");
|
||||
entry_username.set_text(default_username ?? "");
|
||||
entry_password.set_text(default_password ?? "");
|
||||
check_remember_password.active = default_remember_password;
|
||||
entry_imap_host.set_text(default_imap_host ?? "");
|
||||
entry_imap_port.set_text(default_imap_port.to_string());
|
||||
check_imap_ssl.active = default_imap_ssl;
|
||||
|
|
@ -89,6 +93,7 @@ public class LoginDialog {
|
|||
entry_username.changed.connect(on_changed);
|
||||
entry_password.changed.connect(on_changed);
|
||||
entry_real_name.changed.connect(on_changed);
|
||||
check_remember_password.toggled.connect(on_changed);
|
||||
combo_service.changed.connect(on_changed);
|
||||
entry_imap_host.changed.connect(on_changed);
|
||||
entry_imap_port.changed.connect(on_changed);
|
||||
|
|
@ -124,6 +129,7 @@ public class LoginDialog {
|
|||
account_information = new Geary.AccountInformation(credentials);
|
||||
|
||||
account_information.real_name = entry_real_name.text.strip();
|
||||
account_information.remember_password = check_remember_password.active;
|
||||
account_information.service_provider = get_service_provider();
|
||||
account_information.imap_server_host = entry_imap_host.text.strip();
|
||||
account_information.imap_server_port = (uint16) int.parse(entry_imap_port.text.strip());
|
||||
|
|
@ -132,6 +138,8 @@ public class LoginDialog {
|
|||
account_information.smtp_server_port = (uint16) int.parse(entry_smtp_port.text.strip());
|
||||
account_information.smtp_server_ssl = check_smtp_ssl.active;
|
||||
|
||||
on_changed();
|
||||
|
||||
dialog.destroy();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -184,8 +192,8 @@ public class LoginDialog {
|
|||
}
|
||||
|
||||
private bool is_complete() {
|
||||
if (Geary.String.is_empty(entry_username.text.strip()) ||
|
||||
Geary.String.is_empty(entry_password.text.strip()))
|
||||
if (Geary.String.is_null_or_whitespace(entry_username.text.strip()) ||
|
||||
Geary.String.is_null_or_whitespace(entry_password.text.strip()))
|
||||
return false;
|
||||
|
||||
// For "other" providers, check server settings.
|
||||
|
|
|
|||
80
src/client/ui/password-dialog.vala
Normal file
80
src/client/ui/password-dialog.vala
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/* Copyright 2011-2012 Yorba Foundation
|
||||
*
|
||||
* This software is licensed under the GNU Lesser General Public License
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Displays a dialog for collecting the user's password, without allowing them to change their
|
||||
* other data.
|
||||
*/
|
||||
public class PasswordDialog {
|
||||
private Gtk.Dialog dialog;
|
||||
private Gtk.Entry password_entry;
|
||||
private Gtk.CheckButton remember_password_checkbutton;
|
||||
private Gtk.Button ok_button;
|
||||
|
||||
public string password { get; private set; default = ""; }
|
||||
|
||||
public bool remember_password { get; private set; }
|
||||
|
||||
public PasswordDialog(Geary.AccountInformation account_information) {
|
||||
Gtk.Builder builder = GearyApplication.instance.create_builder("password-dialog.glade");
|
||||
|
||||
// Load dialog
|
||||
dialog = (Gtk.Dialog)builder.get_object("PasswordDialog");
|
||||
dialog.set_type_hint(Gdk.WindowTypeHint.DIALOG);
|
||||
dialog.set_default_response(Gtk.ResponseType.OK);
|
||||
|
||||
// Load editable widgets
|
||||
password_entry = (Gtk.Entry)builder.get_object("password_entry");
|
||||
remember_password_checkbutton = (Gtk.CheckButton)builder.get_object("remember_password_checkbutton");
|
||||
|
||||
// Load non-editable widgets
|
||||
Gtk.Label email_label = (Gtk.Label)builder.get_object("email_label");
|
||||
Gtk.Label real_name_label = (Gtk.Label)builder.get_object("real_name_label");
|
||||
Gtk.Label service_label = (Gtk.Label)builder.get_object("service_label");
|
||||
|
||||
// Load default values
|
||||
email_label.set_text(account_information.credentials.user ?? "");
|
||||
password_entry.set_text(account_information.credentials.pass ?? "");
|
||||
remember_password_checkbutton.active = account_information.remember_password;
|
||||
real_name_label.set_text(account_information.real_name ?? "");
|
||||
service_label.set_text(account_information.service_provider.display_name() ?? "");
|
||||
|
||||
// Add action buttons
|
||||
Gtk.Button cancel_button = new Gtk.Button.from_stock(Gtk.Stock.CANCEL);
|
||||
ok_button = new Gtk.Button.from_stock(Gtk.Stock.OK);
|
||||
ok_button.can_default = true;
|
||||
dialog.add_action_widget(cancel_button, Gtk.ResponseType.CANCEL);
|
||||
dialog.add_action_widget(ok_button, Gtk.ResponseType.OK);
|
||||
dialog.set_default_response(Gtk.ResponseType.OK);
|
||||
|
||||
// Setup listeners
|
||||
refresh_ok_button_sensitivity();
|
||||
password_entry.changed.connect(refresh_ok_button_sensitivity);
|
||||
}
|
||||
|
||||
private void refresh_ok_button_sensitivity() {
|
||||
ok_button.sensitive = !Geary.String.is_null_or_whitespace(password_entry.get_text());
|
||||
|
||||
}
|
||||
|
||||
public bool run() {
|
||||
dialog.show();
|
||||
dialog.get_action_area().show_all();
|
||||
|
||||
Gtk.ResponseType response = (Gtk.ResponseType)dialog.run();
|
||||
if (response != Gtk.ResponseType.OK) {
|
||||
dialog.destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
password = password_entry.get_text();
|
||||
remember_password = remember_password_checkbutton.active;
|
||||
|
||||
dialog.destroy();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -237,7 +237,13 @@ public class Sidebar.Tree : Gtk.TreeView {
|
|||
public bool is_selected(Sidebar.Entry entry) {
|
||||
EntryWrapper? wrapper = get_wrapper(entry);
|
||||
|
||||
return (wrapper != null) ? get_selection().path_is_selected(wrapper.get_path()) : false;
|
||||
// Even though get_selection() does not report its return type as nullable, it can be null
|
||||
// if the window has been destroyed.
|
||||
Gtk.TreeSelection selection = get_selection();
|
||||
if (selection == null)
|
||||
return false;
|
||||
|
||||
return (wrapper != null) ? selection.path_is_selected(wrapper.get_path()) : false;
|
||||
}
|
||||
|
||||
public bool is_any_selected() {
|
||||
|
|
@ -456,6 +462,16 @@ public class Sidebar.Tree : Gtk.TreeView {
|
|||
return new_wrapper;
|
||||
}
|
||||
|
||||
protected void prune_all() {
|
||||
while (branches.keys.size > 0) {
|
||||
Gee.Iterator<Sidebar.Branch> iterator = branches.keys.iterator();
|
||||
if (!iterator.next())
|
||||
break;
|
||||
|
||||
prune(iterator.get());
|
||||
}
|
||||
}
|
||||
|
||||
public void prune(Sidebar.Branch branch) {
|
||||
assert(branches.has_key(branch));
|
||||
|
||||
|
|
|
|||
|
|
@ -9,17 +9,22 @@ const string GEARY_USERNAME_PREFIX = "org.yorba.geary username:";
|
|||
public static bool keyring_save_password(Geary.Credentials credentials) {
|
||||
string name = GEARY_USERNAME_PREFIX + credentials.user;
|
||||
|
||||
GnomeKeyring.Result res = GnomeKeyring.store_password_sync(GnomeKeyring.NETWORK_PASSWORD, null,
|
||||
name, credentials.pass, "user", name);
|
||||
GnomeKeyring.Result result = GnomeKeyring.store_password_sync(GnomeKeyring.NETWORK_PASSWORD,
|
||||
null, name, credentials.pass, "user", name);
|
||||
|
||||
return result == GnomeKeyring.Result.OK;
|
||||
}
|
||||
|
||||
return res == GnomeKeyring.Result.OK;
|
||||
public static void keyring_delete_password(string username) {
|
||||
GnomeKeyring.delete_password_sync(GnomeKeyring.NETWORK_PASSWORD, "user",
|
||||
GEARY_USERNAME_PREFIX + username);
|
||||
}
|
||||
|
||||
// Returns the password for the given username, or null if not set.
|
||||
public static string? keyring_get_password(string username) {
|
||||
string password;
|
||||
GnomeKeyring.Result res = GnomeKeyring.find_password_sync(GnomeKeyring.NETWORK_PASSWORD, out password,
|
||||
"user", GEARY_USERNAME_PREFIX + username);
|
||||
GnomeKeyring.Result res = GnomeKeyring.find_password_sync(GnomeKeyring.NETWORK_PASSWORD,
|
||||
out password, "user", GEARY_USERNAME_PREFIX + username);
|
||||
|
||||
return res == GnomeKeyring.Result.OK ? password : null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ public class Geary.AccountInformation : Object {
|
|||
private const string GROUP = "AccountInformation";
|
||||
private const string REAL_NAME_KEY = "real_name";
|
||||
private const string SERVICE_PROVIDER_KEY = "service_provider";
|
||||
private const string REMEMBER_PASSWORD_KEY = "remember_password";
|
||||
private const string IMAP_HOST = "imap_host";
|
||||
private const string IMAP_PORT = "imap_port";
|
||||
private const string IMAP_SSL = "imap_ssl";
|
||||
|
|
@ -18,6 +19,7 @@ public class Geary.AccountInformation : Object {
|
|||
|
||||
public const string SETTINGS_FILENAME = "geary.ini";
|
||||
|
||||
internal File? settings_dir;
|
||||
internal File? file = null;
|
||||
public string real_name { get; set; }
|
||||
public Geary.ServiceProvider service_provider { get; set; }
|
||||
|
|
@ -32,10 +34,13 @@ public class Geary.AccountInformation : Object {
|
|||
public bool smtp_server_ssl { get; set; default = true; }
|
||||
|
||||
public Geary.Credentials credentials { get; private set; }
|
||||
public bool remember_password { get; set; default = true; }
|
||||
|
||||
public AccountInformation(Geary.Credentials credentials) {
|
||||
this.credentials = credentials;
|
||||
this.file = get_settings_file();
|
||||
|
||||
this.settings_dir = Geary.Engine.user_data_dir.get_child(credentials.user);
|
||||
this.file = settings_dir.get_child(SETTINGS_FILENAME);
|
||||
}
|
||||
|
||||
public void load_info_from_file() throws Error {
|
||||
|
|
@ -46,6 +51,7 @@ public class Geary.AccountInformation : Object {
|
|||
// The file didn't exist. No big deal -- just means we give you the defaults.
|
||||
} finally {
|
||||
real_name = get_string_value(key_file, GROUP, REAL_NAME_KEY);
|
||||
remember_password = get_bool_value(key_file, GROUP, REMEMBER_PASSWORD_KEY, true);
|
||||
service_provider = Geary.ServiceProvider.from_string(get_string_value(key_file, GROUP,
|
||||
SERVICE_PROVIDER_KEY));
|
||||
|
||||
|
|
@ -177,6 +183,15 @@ public class Geary.AccountInformation : Object {
|
|||
public async void store_async(Cancellable? cancellable = null) {
|
||||
assert(file != null);
|
||||
|
||||
if (!settings_dir.query_exists(cancellable)) {
|
||||
try {
|
||||
settings_dir.make_directory_with_parents();
|
||||
} catch (Error err) {
|
||||
error("Error creating settings directory for user '%s': %s", credentials.user,
|
||||
err.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (!file.query_exists(cancellable)) {
|
||||
try {
|
||||
yield file.create_async(FileCreateFlags.REPLACE_DESTINATION);
|
||||
|
|
@ -189,6 +204,7 @@ public class Geary.AccountInformation : Object {
|
|||
|
||||
key_file.set_value(GROUP, REAL_NAME_KEY, real_name);
|
||||
key_file.set_value(GROUP, SERVICE_PROVIDER_KEY, service_provider.to_string());
|
||||
key_file.set_boolean(GROUP, REMEMBER_PASSWORD_KEY, remember_password);
|
||||
|
||||
key_file.set_value(GROUP, IMAP_HOST, imap_server_host);
|
||||
key_file.set_integer(GROUP, IMAP_PORT, imap_server_port);
|
||||
|
|
@ -206,11 +222,7 @@ public class Geary.AccountInformation : Object {
|
|||
yield file.replace_contents_async(data.data, null, false, FileCreateFlags.NONE,
|
||||
cancellable, out new_etag);
|
||||
} catch (Error err) {
|
||||
debug("Error writign to account info file: %s", err.message);
|
||||
debug("Error writing to account info file: %s", err.message);
|
||||
}
|
||||
}
|
||||
|
||||
private File get_settings_file() {
|
||||
return Geary.Engine.user_data_dir.get_child(credentials.user).get_child(SETTINGS_FILENAME);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,9 @@ public class Geary.ConversationMonitor : Object {
|
|||
id_ascending.add(email);
|
||||
id_descending.add(email);
|
||||
|
||||
message_ids.add_all(email.get_ancestors());
|
||||
Gee.Set<RFC822.MessageID>? ancestors = email.get_ancestors();
|
||||
if (ancestors != null)
|
||||
message_ids.add_all(ancestors);
|
||||
}
|
||||
|
||||
public void remove(Email email) {
|
||||
|
|
@ -502,21 +504,23 @@ public class Geary.ConversationMonitor : Object {
|
|||
|
||||
// Right now, all threading is done with Message-IDs (no parsing of subject lines, etc.)
|
||||
// If a message doesn't have a Message-ID, it's treated as its own conversation
|
||||
Gee.Set<RFC822.MessageID> ancestors = email.get_ancestors();
|
||||
Gee.Set<RFC822.MessageID>? ancestors = email.get_ancestors();
|
||||
|
||||
// see if any of these ancestor IDs maps to an existing conversation
|
||||
ImplConversation? conversation = null;
|
||||
foreach (ImplConversation known in conversations) {
|
||||
foreach (RFC822.MessageID ancestor in ancestors) {
|
||||
if (known.tracks_message_id(ancestor)) {
|
||||
conversation = known;
|
||||
|
||||
break;
|
||||
if (ancestors != null) {
|
||||
foreach (ImplConversation known in conversations) {
|
||||
foreach (RFC822.MessageID ancestor in ancestors) {
|
||||
if (known.tracks_message_id(ancestor)) {
|
||||
conversation = known;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (conversation != null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (conversation != null)
|
||||
break;
|
||||
}
|
||||
|
||||
// create new conversation if not seen before
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
public class Geary.Credentials {
|
||||
public string user { get; private set; }
|
||||
public string pass { get; private set; }
|
||||
public string pass { get; set; }
|
||||
|
||||
public Credentials(string? user, string? pass) {
|
||||
this.user = user ?? "";
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@
|
|||
|
||||
namespace Geary.String {
|
||||
|
||||
public inline bool is_null_or_whitespace(string? str) {
|
||||
return str == null || str.strip()[0] == 0;
|
||||
}
|
||||
|
||||
public inline bool is_empty(string? str) {
|
||||
return (str == null || str[0] == 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.24"/>
|
||||
<!-- interface-requires gtk+ 3.0 -->
|
||||
<object class="GtkDialog" id="LoginDialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can_default">True</property>
|
||||
|
|
@ -187,10 +187,24 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
<object class="GtkCheckButton" id="remember_password">
|
||||
<property name="label" translatable="yes">_Remember password</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
|
|
|
|||
216
ui/password-dialog.glade
Normal file
216
ui/password-dialog.glade
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 3.0 -->
|
||||
<object class="GtkDialog" id="PasswordDialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">5</property>
|
||||
<property name="title" translatable="yes">Login</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="dialog-vbox1">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">2</property>
|
||||
<property name="column_spacing">4</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Email address:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">_Password:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">password_entry</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="email_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="xpad">2</property>
|
||||
<property name="label" translatable="yes">example@example.com</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="password_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="visibility">False</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="activates_default">True</property>
|
||||
<property name="invisible_char_set">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="remember_password_checkbutton">
|
||||
<property name="label" translatable="yes">_Remember password</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox" id="dialog-action_area1">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkExpander" id="expander1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">2</property>
|
||||
<property name="column_spacing">4</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Service:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Real name:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="service_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Service</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="real_name_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Real name</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Details</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
Loading…
Add table
Add a link
Reference in a new issue