Enable config file versioning

This (way too large patch) enables versioning in config files, and
provides a mechanism by which to load older versions from a newer
version of Geary. It also properly introduces a new v1 config format
that adds several groups to geary.ini to make it easier to read and to
distinguish between incoming/outgoig services rather than IMAP/SMTP.

To do this, a few things that should have happened in seperate patches
were also done:

 * Make AccountInformation's imap and smtp properties mutable (they
 aren't stateful any more anyway), make ServiceInformation non-abstract
 again and remove the subclasses (to get config versioning happening
 without an explosion of a classes, it all has to be handled from the
 AccountManager anyway), and some other misc things.
This commit is contained in:
Michael Gratton 2018-12-07 10:12:02 +11:00
parent 79c74c4310
commit 123f51dbb2
23 changed files with 1213 additions and 757 deletions

View file

@ -24,8 +24,6 @@ src/client/accounts/accounts-editor-row.vala
src/client/accounts/accounts-editor-servers-pane.vala
src/client/accounts/accounts-manager.vala
src/client/accounts/add-edit-page.vala
src/client/accounts/goa-service-information.vala
src/client/accounts/local-service-information.vala
src/client/accounts/login-dialog.vala
src/client/application/application-avatar-store.vala
src/client/application/autostart-manager.vala

View file

@ -159,16 +159,18 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
string message = "";
Gtk.Widget? to_focus = null;
Geary.ServiceInformation imap = new_imap_service();
Geary.ServiceInformation smtp = new_smtp_service();
Geary.AccountInformation account =
this.accounts.new_orphan_account(this.provider, imap, smtp);
this.accounts.new_orphan_account(
this.provider,
new Geary.RFC822.MailboxAddress(
this.real_name.value.text.strip(),
this.email.value.text.strip()
)
);
account.imap = new_imap_service();
account.imap = new_smtp_service();
account.append_sender(new Geary.RFC822.MailboxAddress(
this.real_name.value.text.strip(),
this.email.value.text.strip()
));
account.nickname = account.primary_mailbox.address;
if (this.provider == Geary.ServiceProvider.OTHER) {
@ -275,8 +277,8 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
}
}
private LocalServiceInformation new_imap_service() {
LocalServiceInformation service =
private Geary.ServiceInformation new_imap_service() {
Geary.ServiceInformation service =
this.accounts.new_libsecret_service(Geary.Protocol.IMAP);
if (this.provider == Geary.ServiceProvider.OTHER) {
@ -309,8 +311,8 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
return service;
}
private LocalServiceInformation new_smtp_service() {
LocalServiceInformation service =
private Geary.ServiceInformation new_smtp_service() {
Geary.ServiceInformation service =
this.accounts.new_libsecret_service(Geary.Protocol.SMTP);
if (this.provider == Geary.ServiceProvider.OTHER) {

View file

@ -454,7 +454,7 @@ private class Accounts.AddServiceProviderRow : EditorRow<EditorListPane> {
bool add_local = false;
try {
pane.accounts.add_goa_account.end(res);
} catch (Error.INVALID err) {
} catch (GLib.IOError.NOT_SUPPORTED err) {
// Not a supported type, so don't bother logging the error
add_local = true;
} catch (GLib.Error err) {

View file

@ -61,8 +61,8 @@ internal class Accounts.EditorServersPane : Gtk.Grid, EditorPane, AccountPane {
this.editor = editor;
this.account = account;
this.engine = ((GearyApplication) editor.application).engine;
this.imap_mutable = account.imap.temp_copy();
this.smtp_mutable = account.smtp.temp_copy();
this.imap_mutable = new Geary.ServiceInformation.copy(account.imap);
this.smtp_mutable = new Geary.ServiceInformation.copy(account.smtp);
this.pane_content.set_focus_vadjustment(this.pane_adjustment);

File diff suppressed because it is too large Load diff

View file

@ -690,20 +690,23 @@ public class AddEditPage : Gtk.Box {
}
}
Geary.ServiceInformation? imap = null;
Geary.ServiceInformation? smtp = null;
if (info == null) {
// New account
Geary.ServiceInformation imap =
this.application.controller.account_manager.new_libsecret_service(
Geary.Protocol.IMAP
);
Geary.ServiceInformation smtp =
this.application.controller.account_manager.new_libsecret_service(
Geary.Protocol.SMTP
);
imap = this.application.controller.account_manager.new_libsecret_service(
Geary.Protocol.IMAP
);
smtp = this.application.controller.account_manager.new_libsecret_service(
Geary.Protocol.SMTP
);
try {
info = this.application.controller.account_manager.new_orphan_account(
this.get_service_provider(), imap, smtp
this.get_service_provider(),
new Geary.RFC822.MailboxAddress(
this.real_name.strip(), this.email_address.strip()
)
);
} catch (Error err) {
debug("Unable to create account %s for %s: %s",
@ -712,13 +715,10 @@ public class AddEditPage : Gtk.Box {
} else {
// Existing account: create a copy so we don't mess up the
// original.
info = new Geary.AccountInformation.temp_copy(info);
//info = new Geary.AccountInformation.temp_copy(info);
}
if (info != null) {
//info.primary_mailbox = new Geary.RFC822.MailboxAddress(
// this.real_name.strip(), this.email_address.strip()
//);
info.nickname = this.nickname.strip();
info.imap.credentials = imap_credentials;
info.smtp.credentials = smtp_credentials;
@ -740,6 +740,9 @@ public class AddEditPage : Gtk.Box {
info.use_email_signature = this.use_email_signature;
info.email_signature = this.email_signature;
info.imap = imap;
info.smtp = smtp;
on_changed();
}

View file

@ -1,105 +0,0 @@
/* Copyright 2017 Software Freedom Conservancy Inc.
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
/* A service implementation using GNOME Online Accounts.
* This loads IMAP and SMTP settings from GOA.
*/
public class GoaServiceInformation : Geary.ServiceInformation {
internal Goa.Object account { get; private set; }
public GoaServiceInformation(Geary.Protocol protocol,
GoaMediator mediator,
Goa.Object account) {
base(protocol, mediator);
this.account = account;
update();
}
public void update() {
Goa.Mail? mail = this.account.get_mail();
if (mail != null) {
switch (this.protocol) {
case Geary.Protocol.IMAP:
parse_host_name(mail.imap_host);
this.use_ssl = mail.imap_use_ssl;
this.use_starttls = mail.imap_use_tls;
if (this.port == 0) {
this.port = this.use_ssl
? Geary.Imap.IMAP_TLS_PORT
: Geary.Imap.IMAP_PORT;
}
this.credentials = new Geary.Credentials(
((GoaMediator) this.mediator).method,
mail.imap_user_name
);
break;
case Geary.Protocol.SMTP:
parse_host_name(mail.smtp_host);
this.use_ssl = mail.smtp_use_ssl;
this.use_starttls = mail.smtp_use_tls;
this.smtp_noauth = !(mail.smtp_use_auth);
this.smtp_use_imap_credentials = false;
if (this.port == 0) {
if (this.use_ssl) {
this.port = Geary.Smtp.SUBMISSION_TLS_PORT;
} else if (this.smtp_noauth) {
this.port = Geary.Smtp.SMTP_PORT;
} else {
this.port = Geary.Smtp.SUBMISSION_PORT;
}
}
if (!this.smtp_noauth) {
this.credentials = new Geary.Credentials(
((GoaMediator) this.mediator).method,
mail.smtp_user_name
);
}
break;
}
}
}
public override Geary.ServiceInformation temp_copy() {
GoaServiceInformation copy = new GoaServiceInformation(
this.protocol, (GoaMediator) this.mediator, this.account
);
copy.copy_from(this);
return copy;
}
private void parse_host_name(string host_name) {
// Fall back to trying to use the host name as-is.
// At least the user can see it in the settings if
// they look.
this.host = host_name;
this.port = 0;
try {
GLib.NetworkAddress address = GLib.NetworkAddress.parse(
host_name, this.port
);
this.host = address.hostname;
this.port = (uint16) address.port;
} catch (GLib.Error err) {
warning(
"GOA account \"%s\" %s hostname \"%s\": %",
this.account.get_account().id,
this.protocol.to_value(),
host_name,
err.message
);
}
}
}

View file

@ -1,84 +0,0 @@
/* Copyright 2017 Software Freedom Conservancy Inc.
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
/* A local service implementation. This loads and saves IMAP and SMTP settings
* from and to an account's configuration file. */
public class LocalServiceInformation : Geary.ServiceInformation {
private const string HOST = "host";
private const string PORT = "port";
private const string REMEMBER_PASSWORD_KEY = "remember_password";
private const string SMTP_NOAUTH = "noauth";
private const string SMTP_USE_IMAP_CREDENTIALS = "use_imap_credentials";
private const string SSL = "ssl";
private const string STARTTLS = "starttls";
private const string USERNAME_KEY = "username";
public LocalServiceInformation(Geary.Protocol protocol,
Geary.CredentialsMediator mediator) {
base(protocol, mediator);
}
public override Geary.ServiceInformation temp_copy() {
LocalServiceInformation copy = new LocalServiceInformation(
this.protocol, this.mediator
);
copy.copy_from(this);
return copy;
}
public void load_credentials(Geary.ConfigFile.Group config,
Geary.Credentials.Method method,
string default_login) {
this.credentials = new Geary.Credentials(
method, config.get_string(USERNAME_KEY, default_login)
);
this.remember_password = config.get_bool(
REMEMBER_PASSWORD_KEY, this.remember_password
);
}
public void load_settings(Geary.ConfigFile.Group config) {
this.host = config.get_string(HOST, this.host);
this.port = config.get_uint16(PORT, this.port);
this.use_ssl = config.get_bool(SSL, this.use_ssl);
this.use_starttls = config.get_bool(STARTTLS, this.use_starttls);
if (this.protocol == Geary.Protocol.SMTP) {
this.smtp_noauth = config.get_bool(
SMTP_NOAUTH, this.smtp_noauth
);
this.smtp_use_imap_credentials = config.get_bool(
SMTP_USE_IMAP_CREDENTIALS,
this.smtp_use_imap_credentials
);
if (this.smtp_noauth || this.smtp_use_imap_credentials) {
this.credentials = null;
}
}
}
public void save_settings(Geary.ConfigFile.Group config) {
config.set_string(HOST, this.host);
config.set_int(PORT, this.port);
config.set_bool(SSL, this.use_ssl);
config.set_bool(STARTTLS, this.use_starttls);
config.set_bool(REMEMBER_PASSWORD_KEY, this.remember_password);
if (this.credentials != null) {
config.set_string(USERNAME_KEY, this.credentials.user);
}
if (this.protocol == Geary.Protocol.SMTP) {
config.set_bool(SMTP_USE_IMAP_CREDENTIALS, this.smtp_use_imap_credentials);
config.set_bool(SMTP_NOAUTH, this.smtp_noauth);
}
}
}

View file

@ -43,6 +43,24 @@ public class GoaMediator : Geary.CredentialsMediator, Object {
this.account = account;
}
public Geary.ServiceProvider get_service_provider() {
Geary.ServiceProvider provider = Geary.ServiceProvider.OTHER;
switch (this.account.get_account().provider_type) {
case "google":
provider = Geary.ServiceProvider.GMAIL;
break;
case "windows_live":
provider = Geary.ServiceProvider.OUTLOOK;
break;
}
return provider;
}
public string get_service_label() {
return this.account.get_account().provider_name;
}
public async void update(Geary.AccountInformation geary_account,
GLib.Cancellable? cancellable)
throws GLib.Error {
@ -54,11 +72,8 @@ public class GoaMediator : Geary.CredentialsMediator, Object {
this.oauth2 = this.account.get_oauth2_based();
this.password = this.account.get_password_based();
GoaServiceInformation imap = (GoaServiceInformation) geary_account.imap;
imap.update();
GoaServiceInformation smtp = (GoaServiceInformation) geary_account.smtp;
smtp.update();
update_imap_config(geary_account.imap);
update_smtp_config(geary_account.smtp);
}
public virtual async bool load_token(Geary.AccountInformation account,
@ -118,4 +133,84 @@ public class GoaMediator : Geary.CredentialsMediator, Object {
return this.is_valid;
}
private void update_imap_config(Geary.ServiceInformation service) {
Goa.Mail? mail = this.account.get_mail();
if (mail != null) {
parse_host_name(service, mail.imap_host);
if (mail.imap_use_ssl) {
service.transport_security = Geary.TlsNegotiationMethod.TRANSPORT;
} else if (mail.imap_use_tls) {
service.transport_security = Geary.TlsNegotiationMethod.START_TLS;
} else {
service.transport_security = Geary.TlsNegotiationMethod.NONE;
}
service.credentials = new Geary.Credentials(
this.method, mail.imap_user_name
);
if (service.port == 0) {
service.port = service.get_default_port();
}
}
}
private void update_smtp_config(Geary.ServiceInformation service) {
Goa.Mail? mail = this.account.get_mail();
if (mail != null) {
parse_host_name(service, mail.smtp_host);
if (mail.imap_use_ssl) {
service.transport_security = Geary.TlsNegotiationMethod.TRANSPORT;
} else if (mail.imap_use_tls) {
service.transport_security = Geary.TlsNegotiationMethod.START_TLS;
} else {
service.transport_security = Geary.TlsNegotiationMethod.NONE;
}
if (mail.smtp_use_auth) {
service.smtp_credentials_source = Geary.SmtpCredentials.CUSTOM;
} else {
service.smtp_credentials_source = Geary.SmtpCredentials.NONE;
}
if (mail.smtp_use_auth) {
service.credentials = new Geary.Credentials(
this.method, mail.smtp_user_name
);
}
if (service.port == 0) {
service.port = service.get_default_port();
}
}
}
private void parse_host_name(Geary.ServiceInformation service,
string host_name) {
// Fall back to trying to use the host name as-is.
// At least the user can see it in the settings if
// they look.
service.host = host_name;
service.port = 0;
try {
GLib.NetworkAddress address = GLib.NetworkAddress.parse(
host_name, service.port
);
service.host = address.hostname;
service.port = (uint16) address.port;
} catch (GLib.Error err) {
warning(
"GOA account \"%s\" %s hostname \"%s\": %",
this.account.get_account().id,
service.protocol.to_value(),
host_name,
err.message
);
}
}
}

View file

@ -28,8 +28,6 @@ geary_client_vala_sources = files(
'accounts/accounts-editor-servers-pane.vala',
'accounts/accounts-manager.vala',
'accounts/add-edit-page.vala',
'accounts/goa-service-information.vala',
'accounts/local-service-information.vala',
'accounts/login-dialog.vala',
'components/client-web-view.vala',

View file

@ -55,10 +55,6 @@ public class Geary.AccountInformation : BaseObject {
*/
public File? data_dir { get; private set; default = null; }
//
// IMPORTANT: When adding new properties, be sure to add them to the copy method.
//
/**
* A unique, immutable, machine-readable identifier for this account.
*
@ -72,10 +68,31 @@ public class Geary.AccountInformation : BaseObject {
/** Specifies the email provider for this account. */
public Geary.ServiceProvider service_provider { get; private set; }
/** A human-readable label describing the email service provider. */
/**
* A human-readable label describing the email service provider.
*
* Known providers such as Gmail will have a label specified by
* clients, but other accounts can only really be identified by
* their server names. This attempts to extract a 'nice' value for
* label based on the service's host names.
*/
public string service_label {
get; public set;
owned get {
string? value = this._service_label;
if (value == null) {
string[] host_parts = this.imap.host.split(".");
if (host_parts.length > 1) {
host_parts = host_parts[1:host_parts.length];
}
// don't stash this in _service_label since we want it
// updated if the service host names change
value = string.joinv(".", host_parts);
}
return value;
}
set { this._service_label = value; }
}
private string? _service_label = null;
/**
* A unique human-readable display name for this account.
@ -148,10 +165,17 @@ public class Geary.AccountInformation : BaseObject {
get; set; default = AccountInformation.next_ordinal++;
}
/* Information related to the account's server-side authentication
* and configuration. */
public ServiceInformation imap { get; private set; }
public ServiceInformation smtp { get; private set; }
/* Incoming email service configuration. */
public ServiceInformation imap {
get; set;
default = new ServiceInformation(Protocol.IMAP, null);
}
/* Outgoing email service configuration. */
public ServiceInformation smtp {
get; set;
default = new ServiceInformation(Protocol.SMTP, null);
}
/** A lock that can be used to ensure saving is serialised. */
public Nonblocking.Mutex write_lock {
@ -200,59 +224,10 @@ public class Geary.AccountInformation : BaseObject {
*/
public AccountInformation(string id,
ServiceProvider provider,
ServiceInformation imap,
ServiceInformation smtp) {
RFC822.MailboxAddress primary_mailbox) {
this.id = id;
this.service_provider = provider;
this.imap = imap;
this.smtp = smtp;
// Known providers such as Gmail will have a label specified
// by clients, but other accounts can only really be
// identified by their server names. Try to extract a 'nice'
// value for label based on service host names.
string imap_host = imap.host;
string[] host_parts = imap_host.split(".");
if (host_parts.length > 1) {
host_parts = host_parts[1:host_parts.length];
}
this.service_label = string.joinv(".", host_parts);
}
/**
* Creates a copy of an instance.
*/
public AccountInformation.temp_copy(AccountInformation from) {
this(
from.id,
from.service_provider,
from.imap.temp_copy(),
from.smtp.temp_copy()
);
copy_from(from);
this.is_copy = true;
}
/** Copies all properties from an instance into this one. */
public void copy_from(AccountInformation from) {
this.id = from.id;
this.nickname = from.nickname;
this.mailboxes.clear();
this.mailboxes.add_all(from.sender_mailboxes);
this.prefetch_period_days = from.prefetch_period_days;
this.save_sent_mail = from.save_sent_mail;
this.ordinal = from.ordinal;
this.imap.copy_from(from.imap);
this.smtp.copy_from(from.smtp);
this.drafts_folder_path = from.drafts_folder_path;
this.sent_mail_folder_path = from.sent_mail_folder_path;
this.spam_folder_path = from.spam_folder_path;
this.trash_folder_path = from.trash_folder_path;
this.archive_folder_path = from.archive_folder_path;
this.save_drafts = from.save_drafts;
this.use_email_signature = from.use_email_signature;
this.email_signature = from.email_signature;
append_sender(primary_mailbox);
}
/** Sets the location of the account's storage directories. */
@ -405,10 +380,13 @@ public class Geary.AccountInformation : BaseObject {
*/
public Credentials? get_smtp_credentials() {
Credentials? smtp = null;
if (!this.smtp.smtp_noauth) {
smtp = this.smtp.smtp_use_imap_credentials
? this.imap.credentials
: this.smtp.credentials;
switch (this.smtp.smtp_credentials_source) {
case IMAP:
smtp = this.imap.credentials;
break;
case CUSTOM:
smtp = this.smtp.credentials;
break;
}
return smtp;
}
@ -497,4 +475,36 @@ public class Geary.AccountInformation : BaseObject {
return path;
}
public bool equal_to(AccountInformation other) {
return (
this == other || (
this.id == other.id &&
this.ordinal == other.ordinal &&
this.service_provider == other.service_provider &&
this.service_label == other.service_label &&
this.nickname == other.nickname &&
this.primary_mailbox.equal_to(other.primary_mailbox) &&
this.has_sender_aliases == other.has_sender_aliases &&
this.sender_mailboxes.size == other.sender_mailboxes.size &&
traverse(this.sender_mailboxes).all(
addr => other.sender_mailboxes.contains(addr)
) &&
this.prefetch_period_days == other.prefetch_period_days &&
this.save_sent_mail == other.save_sent_mail &&
this.imap.equal_to(other.imap) &&
this.smtp.equal_to(other.smtp) &&
this.use_email_signature == other.use_email_signature &&
this.email_signature == other.email_signature &&
this.save_drafts == other.save_drafts &&
this.drafts_folder_path == other.drafts_folder_path &&
this.sent_mail_folder_path == other.sent_mail_folder_path &&
this.spam_folder_path == other.spam_folder_path &&
this.trash_folder_path == other.trash_folder_path &&
this.archive_folder_path == other.archive_folder_path &&
this.config_dir == other.config_dir &&
this.data_dir == other.data_dir
)
);
}
}

View file

@ -437,7 +437,7 @@ public class Geary.Engine : BaseObject {
* Changes the service configuration for an account.
*
* This updates an account's service configuration with the given
* configuration, by copying it over the account's existing
* configuration, by replacing the account's existing
* configuration for that service. The corresponding {@link
* Account.incoming} or {@link Account.outgoing} client service
* will also be updated so that the new configuration will start
@ -456,31 +456,31 @@ public class Geary.Engine : BaseObject {
);
}
ServiceInformation? existing = null;
ClientService? service = null;
bool was_updated = false;
switch (updated.protocol) {
case Protocol.IMAP:
existing = account.imap;
if (!account.imap.equal_to(updated)) {
was_updated = true;
account.imap = updated;
}
service = impl.incoming;
break;
case Protocol.SMTP:
existing = account.smtp;
if (!account.smtp.equal_to(updated)) {
was_updated = true;
account.smtp = updated;
}
service = impl.outgoing;
break;
}
bool was_updated = false;
if (service != null) {
if (!existing.equal_to(updated)) {
existing.copy_from(updated);
was_updated = true;
Endpoint endpoint = get_shared_endpoint(
account.service_provider, existing
);
impl.set_endpoint(service, endpoint);
account.information_changed();
}
if (was_updated) {
Endpoint endpoint = get_shared_endpoint(
account.service_provider, updated
);
impl.set_endpoint(service, endpoint);
account.information_changed();
}
return was_updated;

View file

@ -110,12 +110,9 @@ public enum Geary.SmtpCredentials {
/**
* This class encloses all the information used when connecting with the server,
* how to authenticate with it and which credentials to use. Derived classes
* implement specific ways of doing that. For now, the only known implementation
* resides in Geary.LocalServiceInformation.
* Encapsulates configuration information for a network service.
*/
public abstract class Geary.ServiceInformation : GLib.Object {
public class Geary.ServiceInformation : GLib.Object {
/** Specifies if this service is for IMAP or SMTP. */
@ -125,7 +122,7 @@ public abstract class Geary.ServiceInformation : GLib.Object {
public string host { get; set; default = ""; }
/** The server's port. */
public uint16 port { get; set; }
public uint16 port { get; set; default = 0; }
/** The transport security method to use */
public TlsNegotiationMethod transport_security {
@ -169,7 +166,7 @@ public abstract class Geary.ServiceInformation : GLib.Object {
* The credentials mediator used with this service.
*
* It is responsible for fetching and storing the credentials if
* applicable.
* as needed.
*/
public CredentialsMediator mediator { get; private set; }
@ -227,14 +224,32 @@ public abstract class Geary.ServiceInformation : GLib.Object {
public bool smtp_use_imap_credentials { get; set; default = true; }
protected ServiceInformation(Protocol proto, CredentialsMediator mediator) {
/**
* Constructs a new configuration for a specific service.
*/
public ServiceInformation(Protocol proto, CredentialsMediator mediator) {
this.protocol = proto;
this.mediator = mediator;
}
/**
* Constructs a copy of the given service configuration.
*/
public ServiceInformation.copy(ServiceInformation other) {
this(other.protocol, other.mediator);
this.host = other.host;
this.port = other.port;
this.use_starttls = other.use_starttls;
this.use_ssl = other.use_ssl;
this.credentials = (
other.credentials != null ? other.credentials.copy() : null
);
this.mediator = other.mediator;
this.remember_password = other.remember_password;
this.smtp_noauth = other.smtp_noauth;
this.smtp_use_imap_credentials = other.smtp_use_imap_credentials;
}
/** Returns a temporary copy of this service for editing. */
public abstract ServiceInformation temp_copy();
/**
* Returns the default port for this service type and settings.
@ -273,7 +288,7 @@ public abstract class Geary.ServiceInformation : GLib.Object {
this.port == other.port &&
this.use_starttls == other.use_starttls &&
this.use_ssl == other.use_ssl &&
(this.credentials == null && other.credentials != null ||
(this.credentials == null && other.credentials == null ||
this.credentials != null && this.credentials.equal_to(other.credentials)) &&
this.mediator == other.mediator &&
this.remember_password == other.remember_password &&
@ -282,18 +297,4 @@ public abstract class Geary.ServiceInformation : GLib.Object {
);
}
public void copy_from(Geary.ServiceInformation from) {
this.host = from.host;
this.port = from.port;
this.use_starttls = from.use_starttls;
this.use_ssl = from.use_ssl;
this.credentials = (
from.credentials != null ? from.credentials.copy() : null
);
this.mediator = from.mediator;
this.remember_password = from.remember_password;
this.smtp_noauth = from.smtp_noauth;
this.smtp_use_imap_credentials = from.smtp_use_imap_credentials;
}
}

View file

@ -77,27 +77,8 @@ public class Geary.ConfigFile {
}
}
public string get_string(string key, string def = "") {
string ret = def;
foreach (GroupLookup lookup in this.lookups) {
try {
ret = this.backing.get_value(
lookup.group, lookup.prefix + key
);
break;
} catch (GLib.KeyFileError err) {
// continue
}
}
return ret;
}
public void set_string(string key, string value) {
this.backing.set_value(this.name, key, value);
}
public string get_escaped_string(string key, string def = "") {
string ret = def;
public string? get_string(string key, string? def = null) {
string? ret = def;
foreach (GroupLookup lookup in this.lookups) {
try {
ret = this.backing.get_string(
@ -111,7 +92,32 @@ public class Geary.ConfigFile {
return ret;
}
public void set_escaped_string(string key, string value) {
public string get_required_string(string key)
throws GLib.KeyFileError {
string? ret = null;
GLib.KeyFileError? key_err = null;
foreach (GroupLookup lookup in this.lookups) {
try {
ret = this.backing.get_string(
lookup.group, lookup.prefix + key
);
break;
} catch (GLib.KeyFileError err) {
if (key_err == null) {
key_err = err;
}
// continue
}
}
if (key_err != null) {
throw key_err;
}
return ret;
}
public void set_string(string key, string value) {
this.backing.set_string(this.name, key, value);
}
@ -126,6 +132,12 @@ public class Geary.ConfigFile {
return new Gee.ArrayList<string>();
}
public Gee.List<string> get_required_string_list(string key)
throws GLib.KeyFileError {
string[] list = this.backing.get_string_list(this.name, key);
return Geary.Collection.array_list_wrap<string>(list);
}
public void set_string_list(string key, Gee.List<string> value) {
this.backing.set_string_list(this.name, key, value.to_array());
}
@ -177,12 +189,12 @@ public class Geary.ConfigFile {
}
/** Removes a key from this group. */
public void remove_key(string name) throws GLib.Error {
public void remove_key(string name) throws GLib.KeyFileError {
this.backing.remove_key(this.name, name);
}
/** Removes this group from the config file. */
public void remove() throws GLib.Error {
public void remove() throws GLib.KeyFileError {
this.backing.remove_group(this.name);
}
@ -208,7 +220,7 @@ public class Geary.ConfigFile {
* accessed from it before doing so. Use {@link Group.exists} to
* determine if the group has previously been created.
*/
public Group get_group(string name) throws GLib.Error {
public Group get_group(string name) {
return new Group(this, name, this.backing);
}

View file

@ -43,13 +43,13 @@ public void unmirror_properties(Gee.List<Binding> bindings) {
}
/** Convenience method for getting an enum value's nick name. */
internal string to_enum_nick<E>(GLib.Type type, E value) {
public string to_enum_nick<E>(GLib.Type type, E value) {
GLib.EnumClass enum_type = (GLib.EnumClass) type.class_ref();
return enum_type.get_value((int) value).value_nick;
}
/** Convenience method for getting an enum value's from its nick name. */
internal E from_enum_nick<E>(GLib.Type type, string nick) throws EngineError {
public E from_enum_nick<E>(GLib.Type type, string nick) throws EngineError {
GLib.EnumClass enum_type = (GLib.EnumClass) type.class_ref();
unowned GLib.EnumValue? e_value = enum_type.get_value_by_nick(nick);
if (e_value == null) {

View file

@ -8,7 +8,11 @@
class Accounts.ManagerTest : TestCase {
private const string TEST_ID = "test";
private Manager? test = null;
private Geary.AccountInformation? account = null;
private Geary.ServiceInformation? service = null;
private File? tmp = null;
@ -17,6 +21,10 @@ class Accounts.ManagerTest : TestCase {
add_test("create_account", create_account);
add_test("create_orphan_account", create_orphan_account);
add_test("create_orphan_account_with_legacy", create_orphan_account_with_legacy);
add_test("account_config_v1", account_config_v1);
add_test("account_config_legacy", account_config_legacy);
add_test("service_config_v1", service_config_v1);
add_test("service_config_legacy", service_config_legacy);
}
public override void set_up() throws GLib.Error {
@ -34,21 +42,24 @@ class Accounts.ManagerTest : TestCase {
data.make_directory();
this.test = new Manager(new GearyApplication(), config, data);
this.account = new Geary.AccountInformation(
TEST_ID,
Geary.ServiceProvider.OTHER,
new Geary.RFC822.MailboxAddress(null, "test1@example.com")
);
this.service = new Geary.ServiceInformation(Geary.Protocol.SMTP, null);
}
public override void tear_down() throws GLib.Error {
this.test = null;
this.account = null;
this.service = null;
@delete(this.tmp);
}
public void create_account() throws GLib.Error {
const string ID = "test";
Geary.AccountInformation account = new Geary.AccountInformation(
ID,
Geary.ServiceProvider.OTHER,
new Geary.MockServiceInformation(),
new Geary.MockServiceInformation()
);
bool was_added = false;
bool was_enabled = false;
@ -64,7 +75,7 @@ class Accounts.ManagerTest : TestCase {
this.test.create_account.end(async_result());
assert_int(1, this.test.size, "Account manager size");
assert_equal(account, this.test.get_account(ID), "Is not contained");
assert_equal(account, this.test.get_account(TEST_ID), "Is not contained");
assert_true(was_added, "Was not added");
assert_true(was_enabled, "Was not enabled");
}
@ -72,8 +83,7 @@ class Accounts.ManagerTest : TestCase {
public void create_orphan_account() throws GLib.Error {
Geary.AccountInformation account1 = this.test.new_orphan_account(
Geary.ServiceProvider.OTHER,
new Geary.MockServiceInformation(),
new Geary.MockServiceInformation()
new Geary.RFC822.MailboxAddress(null, "test1@example.com")
);
assert(account1.id == "account_01");
@ -85,21 +95,12 @@ class Accounts.ManagerTest : TestCase {
Geary.AccountInformation account2 = this.test.new_orphan_account(
Geary.ServiceProvider.OTHER,
new Geary.MockServiceInformation(),
new Geary.MockServiceInformation()
new Geary.RFC822.MailboxAddress(null, "test1@example.com")
);
assert(account2.id == "account_02");
}
public void create_orphan_account_with_legacy() throws GLib.Error {
const string ID = "test";
Geary.AccountInformation account = new Geary.AccountInformation(
ID,
Geary.ServiceProvider.OTHER,
new Geary.MockServiceInformation(),
new Geary.MockServiceInformation()
);
this.test.create_account.begin(
account, new GLib.Cancellable(),
(obj, res) => { async_complete(res); }
@ -108,8 +109,7 @@ class Accounts.ManagerTest : TestCase {
Geary.AccountInformation account1 = this.test.new_orphan_account(
Geary.ServiceProvider.OTHER,
new Geary.MockServiceInformation(),
new Geary.MockServiceInformation()
new Geary.RFC822.MailboxAddress(null, "test1@example.com")
);
assert(account1.id == "account_01");
@ -121,12 +121,90 @@ class Accounts.ManagerTest : TestCase {
Geary.AccountInformation account2 = this.test.new_orphan_account(
Geary.ServiceProvider.OTHER,
new Geary.MockServiceInformation(),
new Geary.MockServiceInformation()
new Geary.RFC822.MailboxAddress(null, "test1@example.com")
);
assert(account2.id == "account_02");
}
public void account_config_v1() throws GLib.Error {
this.account.email_signature = "blarg";
this.account.nickname = "test-name";
this.account.ordinal = 100;
this.account.prefetch_period_days = 42;
this.account.save_drafts = false;
this.account.save_sent_mail = false;
this.account.use_email_signature = false;
Accounts.AccountConfigV1 config = new Accounts.AccountConfigV1(false);
Geary.ConfigFile file =
new Geary.ConfigFile(this.tmp.get_child("config"));
config.save(this.account, file);
Geary.AccountInformation copy = config.load(file, TEST_ID, null, null);
assert_true(this.account.equal_to(copy));
}
public void account_config_legacy() throws GLib.Error {
this.account.email_signature = "blarg";
this.account.nickname = "test-name";
this.account.ordinal = 100;
this.account.prefetch_period_days = 42;
this.account.save_drafts = false;
this.account.save_sent_mail = false;
this.account.use_email_signature = false;
Accounts.AccountConfigLegacy config =
new Accounts.AccountConfigLegacy();
Geary.ConfigFile file =
new Geary.ConfigFile(this.tmp.get_child("config"));
config.save(this.account, file);
Geary.AccountInformation copy = config.load(file, TEST_ID, null, null);
assert_true(this.account.equal_to(copy));
}
public void service_config_v1() throws GLib.Error {
this.service.host = "blarg";
this.service.port = 1234;
this.service.transport_security = Geary.TlsNegotiationMethod.NONE;
this.service.smtp_credentials_source = Geary.SmtpCredentials.CUSTOM;
this.service.credentials = new Geary.Credentials(
Geary.Credentials.Method.PASSWORD, "testerson"
);
Accounts.ServiceConfigV1 config = new Accounts.ServiceConfigV1();
Geary.ConfigFile file =
new Geary.ConfigFile(this.tmp.get_child("config"));
config.save(this.account, this.service, file);
Geary.ServiceInformation copy = config.load(
file, this.account, this.service.protocol, null
);
assert_true(this.service.equal_to(copy));
}
public void service_config_legacy() throws GLib.Error {
this.service.host = "blarg";
this.service.port = 1234;
this.service.transport_security = Geary.TlsNegotiationMethod.NONE;
this.service.smtp_credentials_source = Geary.SmtpCredentials.CUSTOM;
this.service.credentials = new Geary.Credentials(
Geary.Credentials.Method.PASSWORD, "testerson"
);
Accounts.ServiceConfigLegacy config = new Accounts.ServiceConfigLegacy();
Geary.ConfigFile file =
new Geary.ConfigFile(this.tmp.get_child("config"));
config.save(this.account, this.service, file);
Geary.ServiceInformation copy = config.load(
file, this.account, this.service.protocol, null
);
assert_true(this.service.equal_to(copy));
}
private void delete(File parent) throws GLib.Error {
FileInfo info = parent.query_info(
"standard::*",

View file

@ -17,19 +17,19 @@ class Geary.AccountInformationTest : TestCase {
AccountInformation test = new AccountInformation(
"test",
ServiceProvider.OTHER,
new MockServiceInformation(),
new MockServiceInformation()
new RFC822.MailboxAddress(null, "test1@example.com")
);
test.append_sender(new RFC822.MailboxAddress(null, "test1@example.com"));
assert_false(test.has_sender_aliases);
test.append_sender(new RFC822.MailboxAddress(null, "test2@example.com"));
test.append_sender(new RFC822.MailboxAddress(null, "test3@example.com"));
assert_true(test.has_sender_aliases);
assert_true(test.primary_mailbox.equal_to(
new RFC822.MailboxAddress(null, "test1@example.com")));
assert_false(test.has_sender_aliases);
test.append_sender(new RFC822.MailboxAddress(null, "test2@example.com"));
assert_true(test.has_sender_aliases);
test.append_sender(new RFC822.MailboxAddress(null, "test3@example.com"));
assert_true(test.has_sender_aliases);
assert_true(
test.has_sender_mailbox(new RFC822.MailboxAddress(null, "test1@example.com")),
"Primary address not found"

View file

@ -66,8 +66,7 @@ class Geary.EngineTest : TestCase {
AccountInformation info = new AccountInformation(
"test",
ServiceProvider.OTHER,
new MockServiceInformation(),
new MockServiceInformation()
new RFC822.MailboxAddress(null, "test1@example.com")
);
assert_false(this.engine.has_account(info.id));
@ -86,8 +85,7 @@ class Geary.EngineTest : TestCase {
AccountInformation info = new AccountInformation(
"test",
ServiceProvider.OTHER,
new MockServiceInformation(),
new MockServiceInformation()
new RFC822.MailboxAddress(null, "test1@example.com")
);
this.engine.add_account(info);
assert_true(this.engine.has_account(info.id));
@ -103,8 +101,7 @@ class Geary.EngineTest : TestCase {
AccountInformation info = new AccountInformation(
"test",
ServiceProvider.OTHER,
new MockServiceInformation(),
new MockServiceInformation()
new RFC822.MailboxAddress(null, "test1@example.com")
);
assert_false(this.engine.has_account(info.id));

View file

@ -1,29 +0,0 @@
/*
* Copyright 2017 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
public class Geary.MockServiceInformation : ServiceInformation, MockObject {
protected Gee.Queue<ExpectedCall> expected {
get; set; default = new Gee.LinkedList<ExpectedCall>();
}
public MockServiceInformation() {
base(Protocol.IMAP, new MockCredentialsMediator());
}
public override Geary.ServiceInformation temp_copy() {
try {
return object_call<Geary.ServiceInformation>(
"temp_copy", { }, new MockServiceInformation()
);
} catch (GLib.Error err) {
assert_not_reached();
}
}
}

View file

@ -31,8 +31,7 @@ class Geary.App.ConversationMonitorTest : TestCase {
this.account_info = new AccountInformation(
"account_01",
ServiceProvider.OTHER,
new MockServiceInformation(),
new MockServiceInformation()
new RFC822.MailboxAddress(null, "test1@example.com")
);
this.account = new MockAccount(this.account_info);
this.base_folder = new MockFolder(

View file

@ -72,8 +72,7 @@ public class Geary.ImapEngine.AccountProcessorTest : TestCase {
this.info = new Geary.AccountInformation(
"test-info",
ServiceProvider.OTHER,
new MockServiceInformation(),
new MockServiceInformation()
new RFC822.MailboxAddress(null, "test1@example.com")
);
this.account = new Geary.MockAccount(this.info);

View file

@ -19,7 +19,6 @@ class Geary.ConfigFileTest : TestCase {
base("Geary.ConfigFileTest");
add_test("test_string", test_string);
add_test("test_string_fallback", test_string_fallback);
add_test("test_escaped_string", test_escaped_string);
add_test("test_string_list", test_string_list);
add_test("test_string_list", test_string_list);
add_test("test_bool", test_bool);
@ -55,12 +54,6 @@ class Geary.ConfigFileTest : TestCase {
assert_string("a string", this.test_group.get_string(TEST_KEY));
}
public void test_escaped_string() throws Error {
this.test_group.set_escaped_string(TEST_KEY, "a\nstring");
assert_string("a\nstring", this.test_group.get_escaped_string(TEST_KEY));
assert_string("=default", this.test_group.get_escaped_string(TEST_KEY_MISSING, "=default"));
}
public void test_string_list() throws Error {
this.test_group.set_string_list(
TEST_KEY, new Gee.ArrayList<string>.wrap({ "a", "b"})

View file

@ -19,7 +19,6 @@ geary_test_engine_sources = [
'engine/api/geary-email-properties-mock.vala',
'engine/api/geary-folder-mock.vala',
'engine/api/geary-folder-path-mock.vala',
'engine/api/geary-service-information-mock.vala',
'engine/api/geary-account-information-test.vala',
'engine/api/geary-attachment-test.vala',
@ -68,7 +67,6 @@ geary_test_client_sources = [
# geary-engine_internal.vapi, which leads to duplicate symbols when
# linking
'engine/api/geary-credentials-mediator-mock.vala',
'engine/api/geary-service-information-mock.vala',
'client/accounts/accounts-manager-test.vala',
'client/application/geary-configuration-test.vala',