Implemented IMAP-specific folder and message properties tables: #3805

This commit adds support for IMAP-specific properties, of which UIDValidity is crucial toward completing #3805.  The additional code is to integrate these tables into the SQLite Geary backend and to make sure this information is requested from the IMAP server.

NOTE: This commit changes the database schema.  Old databases will need to be blown away before running.
This commit is contained in:
Jim Nelson 2011-07-08 12:45:22 -07:00
parent 9792780edb
commit 6b8951bfd8
30 changed files with 496 additions and 99 deletions

View file

@ -7,6 +7,7 @@
public class Geary.Sqlite.Account : Geary.AbstractAccount, Geary.LocalAccount {
private MailDatabase db;
private FolderTable folder_table;
private ImapFolderPropertiesTable folder_properties_table;
private MessageTable message_table;
public Account(Geary.Credentials cred) {
@ -19,6 +20,7 @@ public class Geary.Sqlite.Account : Geary.AbstractAccount, Geary.LocalAccount {
}
folder_table = db.get_folder_table();
folder_properties_table = db.get_imap_folder_properties_table();
message_table = db.get_message_table();
}
@ -42,20 +44,21 @@ public class Geary.Sqlite.Account : Geary.AbstractAccount, Geary.LocalAccount {
public async void clone_folder_async(Geary.Folder folder, Cancellable? cancellable = null)
throws Error {
int64 parent_id = yield fetch_parent_id_async(folder.get_path(), cancellable);
yield folder_table.create_async(new FolderRow(folder_table, folder.get_path().basename,
parent_id), cancellable);
}
public async void clone_many_folders_async(Gee.Collection<Geary.Folder> folders,
Cancellable? cancellable = null) throws Error {
Gee.List<FolderRow> rows = new Gee.ArrayList<FolderRow>();
foreach (Geary.Folder folder in folders) {
int64 parent_id = yield fetch_parent_id_async(folder.get_path(), cancellable);
rows.add(new FolderRow(db.get_folder_table(), folder.get_path().basename, parent_id));
}
Geary.Imap.Folder imap_folder = (Geary.Imap.Folder) folder;
Geary.Imap.FolderProperties? imap_folder_properties = (Geary.Imap.FolderProperties?)
imap_folder.get_properties();
yield folder_table.create_many_async(rows, cancellable);
// properties *must* be available to perform a clone
assert(imap_folder_properties != null);
int64 parent_id = yield fetch_parent_id_async(folder.get_path(), cancellable);
int64 folder_id = yield folder_table.create_async(new FolderRow(folder_table,
imap_folder.get_path().basename, parent_id), cancellable);
yield folder_properties_table.create_async(
new ImapFolderPropertiesRow.from_imap_properties(folder_properties_table, folder_id,
imap_folder_properties));
}
public override async Gee.Collection<Geary.Folder> list_folders_async(Geary.FolderPath? parent,
@ -75,11 +78,14 @@ public class Geary.Sqlite.Account : Geary.AbstractAccount, Geary.LocalAccount {
Gee.Collection<Geary.Folder> folders = new Gee.ArrayList<Geary.Sqlite.Folder>();
foreach (FolderRow row in rows) {
ImapFolderPropertiesRow? properties = yield folder_properties_table.fetch_async(row.id,
cancellable);
Geary.FolderPath path = (parent != null)
? parent.get_child(row.name)
: new Geary.FolderRoot(row.name, "/", Geary.Imap.Folder.CASE_SENSITIVE);
folders.add(new Geary.Sqlite.Folder(db, row, path));
folders.add(new Geary.Sqlite.Folder(db, row, properties, path));
}
return folders;
@ -105,7 +111,10 @@ public class Geary.Sqlite.Account : Geary.AbstractAccount, Geary.LocalAccount {
if (row == null)
throw new EngineError.NOT_FOUND("%s not found in local database", path.to_string());
return new Geary.Sqlite.Folder(db, row, path);
ImapFolderPropertiesRow? properties = yield folder_properties_table.fetch_async(row.id,
cancellable);
return new Geary.Sqlite.Folder(db, row, properties, path);
}
public async bool has_message_id_async(Geary.RFC822.MessageID message_id, out int count,

View file

@ -10,20 +10,25 @@
public class Geary.Sqlite.Folder : Geary.AbstractFolder, Geary.LocalFolder {
private MailDatabase db;
private FolderRow folder_row;
private Geary.FolderProperties? properties;
private MessageTable message_table;
private MessageLocationTable location_table;
private ImapMessageLocationPropertiesTable imap_location_table;
private ImapMessagePropertiesTable imap_message_properties_table;
private Geary.FolderPath path;
private bool opened = false;
internal Folder(MailDatabase db, FolderRow folder_row, Geary.FolderPath path) throws Error {
internal Folder(MailDatabase db, FolderRow folder_row, ImapFolderPropertiesRow? properties,
Geary.FolderPath path) throws Error {
this.db = db;
this.folder_row = folder_row;
this.properties = (properties != null) ? properties.get_imap_folder_properties() : null;
this.path = path;
message_table = db.get_message_table();
location_table = db.get_message_location_table();
imap_location_table = db.get_imap_message_location_table();
imap_message_properties_table = db.get_imap_message_properties_table();
}
private void check_open() throws Error {
@ -36,7 +41,7 @@ public class Geary.Sqlite.Folder : Geary.AbstractFolder, Geary.LocalFolder {
}
public override Geary.FolderProperties? get_properties() {
return null;
return properties;
}
public override async void open_async(bool readonly, Cancellable? cancellable = null) throws Error {
@ -44,7 +49,8 @@ public class Geary.Sqlite.Folder : Geary.AbstractFolder, Geary.LocalFolder {
throw new EngineError.ALREADY_OPEN("%s already open", to_string());
opened = true;
notify_opened();
notify_opened(Geary.Folder.OpenState.LOCAL);
}
public override async void close_async(Cancellable? cancellable = null) throws Error {
@ -52,6 +58,7 @@ public class Geary.Sqlite.Folder : Geary.AbstractFolder, Geary.LocalFolder {
return;
opened = false;
notify_closed(CloseReason.FOLDER_CLOSED);
}
@ -89,15 +96,27 @@ public class Geary.Sqlite.Folder : Geary.AbstractFolder, Geary.LocalFolder {
ImapMessageLocationPropertiesRow imap_location_row = new ImapMessageLocationPropertiesRow(
imap_location_table, Row.INVALID_ID, location_id, location.uid);
yield imap_location_table.create_async(imap_location_row, cancellable);
// only write out the IMAP email properties if they're supplied and there's something to
// write out -- no need to create an empty row
Geary.Imap.EmailProperties? properties = (Geary.Imap.EmailProperties?) email.properties;
if (email.fields.fulfills(Geary.Email.Field.PROPERTIES) && properties != null && !properties.is_empty()) {
ImapMessagePropertiesRow properties_row = new ImapMessagePropertiesRow.from_imap_properties(
imap_message_properties_table, message_id, properties);
yield imap_message_properties_table.create_async(properties_row, cancellable);
}
}
public override async Gee.List<Geary.Email>? list_email_async(int low, int count,
Geary.Email.Field required_fields, Cancellable? cancellable) throws Error {
assert(low >= 1);
assert(count >= 1);
assert(count >= 0 || count == -1);
check_open();
if (count == 0)
return null;
Gee.List<MessageLocationRow>? list = yield location_table.list_async(folder_row.id, low,
count, cancellable);
@ -136,12 +155,23 @@ public class Geary.Sqlite.Folder : Geary.AbstractFolder, Geary.LocalFolder {
MessageRow? message_row = yield message_table.fetch_async(location_row.message_id,
required_fields, cancellable);
assert(message_row != null);
// only add to the list if the email contains all the required fields
if (!message_row.fields.is_set(required_fields))
continue;
emails.add(message_row.to_email(new Geary.Imap.EmailLocation(location_row.position,
imap_location_row.uid)));
ImapMessagePropertiesRow? properties = null;
if (required_fields.fulfills(Geary.Email.Field.PROPERTIES)) {
properties = yield imap_message_properties_table.fetch_async(location_row.message_id,
cancellable);
}
Geary.Email email = message_row.to_email(new Geary.Imap.EmailLocation(location_row.position,
imap_location_row.uid));
if (properties != null)
email.set_email_properties(properties.get_imap_email_properties());
emails.add(email);
}
return (emails.size > 0) ? emails : null;
@ -184,8 +214,18 @@ public class Geary.Sqlite.Folder : Geary.AbstractFolder, Geary.LocalFolder {
message_row.fields);
}
return message_row.to_email(new Geary.Imap.EmailLocation(location_row.position,
ImapMessagePropertiesRow? properties = null;
if (required_fields.fulfills(Geary.Email.Field.PROPERTIES)) {
properties = yield imap_message_properties_table.fetch_async(location_row.message_id,
cancellable);
}
Geary.Email email = message_row.to_email(new Geary.Imap.EmailLocation(location_row.position,
imap_location_row.uid));
if (properties != null)
email.set_email_properties(properties.get_imap_email_properties());
return email;
}
public async bool is_email_present_at(int position, out Geary.Email.Field available_fields,
@ -292,7 +332,6 @@ public class Geary.Sqlite.Folder : Geary.AbstractFolder, Geary.LocalFolder {
}
// TODO: The database should be locked around this method, as it should be atomic.
// TODO: Merge email properties
private async void merge_email_async(int64 message_id, Geary.Email email,
Cancellable? cancellable = null) throws Error {
assert(message_id != Row.INVALID_ID);
@ -310,6 +349,12 @@ public class Geary.Sqlite.Folder : Geary.AbstractFolder, Geary.LocalFolder {
// possible nothing has changed or been added
if (message_row.fields != Geary.Email.Field.NONE)
yield message_table.merge_async(message_row, cancellable);
// update IMAP properties
if (email.fields.fulfills(Geary.Email.Field.PROPERTIES)) {
yield imap_message_properties_table.update_async(message_id,
((Geary.Imap.EmailProperties) email.properties).flags.serialize(), cancellable);
}
}
}