4028, 3700 login dialog and password persistence
Adds a login dialog box Support for Glade UI files Gnome keyring for password storage Assumption of single Geary account (for now)
This commit is contained in:
parent
6a5d1fa048
commit
9d2b10530c
9 changed files with 302 additions and 16 deletions
|
|
@ -55,11 +55,38 @@ along with Geary; if not, write to the Free Software Foundation, Inc.,
|
||||||
base (NAME, "geary", "org.yorba.geary");
|
base (NAME, "geary", "org.yorba.geary");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void startup() {
|
public override int startup() {
|
||||||
config = new Configuration(YorbaApplication.instance.get_install_dir() != null,
|
config = new Configuration(YorbaApplication.instance.get_install_dir() != null,
|
||||||
YorbaApplication.instance.get_exec_dir().get_child("build/src/client").get_path());
|
YorbaApplication.instance.get_exec_dir().get_child("build/src/client").get_path());
|
||||||
|
|
||||||
Geary.Credentials cred = new Geary.Credentials("imap.gmail.com", args[1], args[2]);
|
// Get saved credentials. If not present, ask user.
|
||||||
|
string username = "";
|
||||||
|
string? password = null;
|
||||||
|
try {
|
||||||
|
Gee.List<string> accounts = Geary.Engine.get_usernames();
|
||||||
|
if (accounts.size > 0) {
|
||||||
|
username = accounts.get(0);
|
||||||
|
password = keyring_get_password(username);
|
||||||
|
}
|
||||||
|
} catch (Error e) {
|
||||||
|
debug("Unable to fetch accounts. Error: %s", e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password == null) {
|
||||||
|
LoginDialog login = new LoginDialog(username);
|
||||||
|
login.show();
|
||||||
|
if (login.get_response() == Gtk.ResponseType.OK) {
|
||||||
|
username = login.username;
|
||||||
|
password = login.password;
|
||||||
|
|
||||||
|
// TODO: check credentials before saving password in keyring.
|
||||||
|
keyring_save_password(username, password);
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Geary.Credentials cred = new Geary.Credentials("imap.gmail.com", username, password);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
account = Geary.Engine.open(cred);
|
account = Geary.Engine.open(cred);
|
||||||
|
|
@ -69,6 +96,7 @@ along with Geary; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
main_window.show_all();
|
main_window.show_all();
|
||||||
main_window.start(account);
|
main_window.start(account);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void activate() {
|
public override void activate() {
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,6 @@ int main(string[] args) {
|
||||||
// initialize GTK, which modifies the command-line arguments
|
// initialize GTK, which modifies the command-line arguments
|
||||||
Gtk.init(ref args);
|
Gtk.init(ref args);
|
||||||
|
|
||||||
if (args.length != 3) {
|
|
||||||
stderr.printf("usage: geary <user> <pass>\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// if already registered, silently exit
|
// if already registered, silently exit
|
||||||
if (!GearyApplication.instance.register())
|
if (!GearyApplication.instance.register())
|
||||||
|
|
|
||||||
63
src/client/ui/geary-login.vala
Normal file
63
src/client/ui/geary-login.vala
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/* Copyright 2011 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 login data.
|
||||||
|
public class LoginDialog {
|
||||||
|
private Gtk.Dialog dialog;
|
||||||
|
private Gtk.Entry entry_username;
|
||||||
|
private Gtk.Entry entry_password;
|
||||||
|
private Gtk.ResponseType response;
|
||||||
|
private Gtk.Button ok_button;
|
||||||
|
|
||||||
|
public string username { get; private set; default = ""; }
|
||||||
|
public string password { get; private set; default = ""; }
|
||||||
|
|
||||||
|
public LoginDialog(string default_username = "", string default_password = "") {
|
||||||
|
Gtk.Builder builder = YorbaApplication.instance.create_builder("login.glade");
|
||||||
|
|
||||||
|
dialog = builder.get_object("LoginDialog") as Gtk.Dialog;
|
||||||
|
dialog.set_type_hint(Gdk.WindowTypeHint.DIALOG);
|
||||||
|
dialog.set_default_response(Gtk.ResponseType.OK);
|
||||||
|
|
||||||
|
entry_username = builder.get_object("username") as Gtk.Entry;
|
||||||
|
entry_password = builder.get_object("password") as Gtk.Entry;
|
||||||
|
|
||||||
|
entry_username.set_text(default_username);
|
||||||
|
entry_password.set_text(default_password);
|
||||||
|
|
||||||
|
entry_username.changed.connect(on_changed);
|
||||||
|
entry_password.changed.connect(on_changed);
|
||||||
|
|
||||||
|
dialog.add_action_widget(new Gtk.Button.from_stock(Gtk.Stock.CANCEL), Gtk.ResponseType.CANCEL);
|
||||||
|
ok_button = new Gtk.Button.from_stock(Gtk.Stock.OK);
|
||||||
|
ok_button.can_default = true;
|
||||||
|
ok_button.sensitive = false;
|
||||||
|
dialog.add_action_widget(ok_button, Gtk.ResponseType.OK);
|
||||||
|
dialog.set_default_response(Gtk.ResponseType.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runs the dialog.
|
||||||
|
public void show() {
|
||||||
|
dialog.show_all();
|
||||||
|
response = (Gtk.ResponseType) dialog.run();
|
||||||
|
username = entry_username.text.strip();
|
||||||
|
password = entry_password.text.strip();
|
||||||
|
dialog.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call this after Show to get the response. Will either be OK or cancel.
|
||||||
|
public Gtk.ResponseType get_response() {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_changed() {
|
||||||
|
ok_button.sensitive = is_complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool is_complete() {
|
||||||
|
return entry_username.text.strip() != "" && entry_password.text.strip() != "";
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/client/util/util-keyring.vala
Normal file
25
src/client/util/util-keyring.vala
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* Copyright 2011 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const string GEARY_USERNAME_PREFIX = "org.yorba.geary username:";
|
||||||
|
|
||||||
|
public static void keyring_save_password(string username, string password) {
|
||||||
|
string name = GEARY_USERNAME_PREFIX + username;
|
||||||
|
|
||||||
|
GnomeKeyring.Result res = GnomeKeyring.store_password_sync(GnomeKeyring.NETWORK_PASSWORD, null,
|
||||||
|
name, password, "user", name);
|
||||||
|
|
||||||
|
assert(res == GnomeKeyring.Result.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
return res == GnomeKeyring.Result.OK ? password : null;
|
||||||
|
}
|
||||||
|
|
@ -8,21 +8,24 @@ client_src = [
|
||||||
'geary-config.vala',
|
'geary-config.vala',
|
||||||
'main.vala',
|
'main.vala',
|
||||||
|
|
||||||
|
'ui/geary-login.vala',
|
||||||
'ui/folder-list-store.vala',
|
'ui/folder-list-store.vala',
|
||||||
'ui/folder-list-view.vala',
|
'ui/folder-list-view.vala',
|
||||||
'ui/main-window.vala',
|
'ui/main-window.vala',
|
||||||
'ui/message-buffer.vala',
|
'ui/message-buffer.vala',
|
||||||
'ui/message-list-store.vala',
|
'ui/message-list-store.vala',
|
||||||
'ui/message-list-view.vala',
|
'ui/message-list-view.vala',
|
||||||
'ui/message-viewer.vala'
|
'ui/message-viewer.vala',
|
||||||
|
|
||||||
|
'util/util-keyring.vala'
|
||||||
]
|
]
|
||||||
|
|
||||||
gsettings_schemas = [
|
gsettings_schemas = [
|
||||||
'org.yorba.geary.gschema.xml'
|
'org.yorba.geary.gschema.xml'
|
||||||
]
|
]
|
||||||
|
|
||||||
client_uselib = 'GLIB GEE GTK'
|
client_uselib = 'GLIB GEE GTK GNOME-KEYRING'
|
||||||
client_packages = [ 'gtk+-2.0', 'glib-2.0', 'gee-1.0' ]
|
client_packages = [ 'gtk+-2.0', 'glib-2.0', 'gee-1.0', 'gnome-keyring-1' ]
|
||||||
|
|
||||||
app = bld.program(
|
app = bld.program(
|
||||||
target = 'geary',
|
target = 'geary',
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,8 @@ public abstract class YorbaApplication {
|
||||||
*
|
*
|
||||||
* The args[] array will be available when this signal is fired.
|
* The args[] array will be available when this signal is fired.
|
||||||
*/
|
*/
|
||||||
public virtual signal void startup() {
|
public virtual signal int startup() {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual signal void activate() {
|
public virtual signal void activate() {
|
||||||
|
|
@ -107,10 +108,11 @@ public abstract class YorbaApplication {
|
||||||
exec_dir = (File.new_for_path(Environment.find_program_in_path(args[0]))).get_parent();
|
exec_dir = (File.new_for_path(Environment.find_program_in_path(args[0]))).get_parent();
|
||||||
|
|
||||||
running = true;
|
running = true;
|
||||||
startup();
|
exitcode = startup();
|
||||||
|
|
||||||
// enter the main loop
|
// enter the main loop
|
||||||
Gtk.main();
|
if (exitcode == 0)
|
||||||
|
Gtk.main();
|
||||||
|
|
||||||
return exitcode;
|
return exitcode;
|
||||||
}
|
}
|
||||||
|
|
@ -167,5 +169,18 @@ public abstract class YorbaApplication {
|
||||||
File prefix_dir = File.new_for_path(PREFIX);
|
File prefix_dir = File.new_for_path(PREFIX);
|
||||||
return exec_dir.has_prefix(prefix_dir) ? prefix_dir : null;
|
return exec_dir.has_prefix(prefix_dir) ? prefix_dir : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates a GTK builder given the filename of a UI file in the ui directory.
|
||||||
|
public Gtk.Builder create_builder(string ui_filename) {
|
||||||
|
Gtk.Builder builder = new Gtk.Builder();
|
||||||
|
try {
|
||||||
|
builder.add_from_file(get_resource_directory().get_child("ui").get_child(
|
||||||
|
ui_filename).get_path());
|
||||||
|
} catch(GLib.Error error) {
|
||||||
|
warning("Unable to create Gtk.Builder: %s".printf(error.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,4 +20,20 @@ public class Geary.Engine {
|
||||||
new Geary.Imap.Account(cred, Imap.ClientConnection.DEFAULT_PORT_TLS),
|
new Geary.Imap.Account(cred, Imap.ClientConnection.DEFAULT_PORT_TLS),
|
||||||
new Geary.Sqlite.Account(cred));
|
new Geary.Sqlite.Account(cred));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a list of usernames associated with Geary.
|
||||||
|
public static Gee.List<string> get_usernames() throws Error {
|
||||||
|
Gee.ArrayList<string> list = new Gee.ArrayList<string>();
|
||||||
|
|
||||||
|
FileEnumerator enumerator = YorbaApplication.instance.get_user_data_directory().
|
||||||
|
enumerate_children("standard::*", FileQueryInfoFlags.NONE);
|
||||||
|
|
||||||
|
FileInfo? info = null;
|
||||||
|
while ((info = enumerator.next_file()) != null) {
|
||||||
|
if (info.get_file_type() == FileType.DIRECTORY)
|
||||||
|
list.add(info.get_name());
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
136
ui/login.glade
Normal file
136
ui/login.glade
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="2.24"/>
|
||||||
|
<!-- interface-naming-policy project-wide -->
|
||||||
|
<object class="GtkDialog" id="LoginDialog">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="can_default">True</property>
|
||||||
|
<property name="border_width">5</property>
|
||||||
|
<property name="type_hint">dialog</property>
|
||||||
|
<child internal-child="vbox">
|
||||||
|
<object class="GtkVBox" id="dialog-vbox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">2</property>
|
||||||
|
<child internal-child="action_area">
|
||||||
|
<object class="GtkHButtonBox" id="action area">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<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">False</property>
|
||||||
|
<property name="padding">6</property>
|
||||||
|
<property name="pack_type">end</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="welcome">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="xpad">12</property>
|
||||||
|
<property name="ypad">12</property>
|
||||||
|
<property name="label" translatable="yes">Welcome to Geary.</property>
|
||||||
|
<property name="wrap">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTable" id="table1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="n_rows">2</property>
|
||||||
|
<property name="n_columns">2</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label: username">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="label" translatable="yes">Username:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="x_options">GTK_FILL</property>
|
||||||
|
<property name="y_options">GTK_FILL</property>
|
||||||
|
<property name="x_padding">6</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label: password">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="label" translatable="yes">Password:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="bottom_attach">2</property>
|
||||||
|
<property name="x_options">GTK_FILL</property>
|
||||||
|
<property name="x_padding">6</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="username">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="invisible_char">•</property>
|
||||||
|
<property name="activates_default">True</property>
|
||||||
|
<property name="width_chars">0</property>
|
||||||
|
<property name="primary_icon_activatable">False</property>
|
||||||
|
<property name="secondary_icon_activatable">False</property>
|
||||||
|
<property name="primary_icon_sensitive">True</property>
|
||||||
|
<property name="secondary_icon_sensitive">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="x_options">GTK_FILL</property>
|
||||||
|
<property name="y_options">GTK_FILL</property>
|
||||||
|
<property name="x_padding">6</property>
|
||||||
|
<property name="y_padding">4</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="password">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">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>
|
||||||
|
<property name="primary_icon_activatable">False</property>
|
||||||
|
<property name="secondary_icon_activatable">False</property>
|
||||||
|
<property name="primary_icon_sensitive">True</property>
|
||||||
|
<property name="secondary_icon_sensitive">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="bottom_attach">2</property>
|
||||||
|
<property name="x_padding">6</property>
|
||||||
|
<property name="y_padding">4</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
||||||
10
wscript
10
wscript
|
|
@ -80,17 +80,23 @@ def configure(conf):
|
||||||
atleast_version='2.4.14',
|
atleast_version='2.4.14',
|
||||||
mandatory=1,
|
mandatory=1,
|
||||||
args='--cflags --libs')
|
args='--cflags --libs')
|
||||||
|
|
||||||
|
conf.check_cfg(
|
||||||
|
package='gnome-keyring-1',
|
||||||
|
uselib_store='GNOME-KEYRING',
|
||||||
|
atleast_version='2.32.0',
|
||||||
|
mandatory=1,
|
||||||
|
args='--cflags --libs')
|
||||||
|
|
||||||
def build(bld):
|
def build(bld):
|
||||||
bld.add_post_fun(post_build)
|
bld.add_post_fun(post_build)
|
||||||
|
|
||||||
bld.env.append_value('CFLAGS', ['-O2', '-g', '-D_PREFIX="' + bld.env.PREFIX + '"'])
|
bld.env.append_value('CFLAGS', ['-O2', '-g', '-D_PREFIX="' + bld.env.PREFIX + '"'])
|
||||||
bld.env.append_value('LINKFLAGS', ['-O2', '-g'])
|
bld.env.append_value('LINKFLAGS', ['-O2', '-g'])
|
||||||
bld.env.append_value('VALAFLAGS', ['-g', '--enable-checking', '--fatal-warnings'])
|
bld.env.append_value('VALAFLAGS', ['-g', '--enable-checking', '--fatal-warnings'])
|
||||||
|
|
||||||
bld.recurse('src')
|
bld.recurse('src')
|
||||||
|
|
||||||
|
|
||||||
def post_build(bld):
|
def post_build(bld):
|
||||||
# Copy executable to root folder.
|
# Copy executable to root folder.
|
||||||
geary_path = 'build/src/client/geary'
|
geary_path = 'build/src/client/geary'
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue