Persist messages locally: #3742
This completes the heavy lifting of persisting messages locally. The strategy is that the local database may be sparsely populated, both in the availability of messages in a folder and the fields of a message that is partially stored. As data is pulled from the remote server it's always stored in the database. Future requests will always go to the database first, preventing unnecessary network traffic. Also, this patch will detect when a message is stored in multiple folders on the server. The database uses soft links from the folder to the message, so the message is stored only once in the database. This technique relies heavily on the availability and validity of the Message-ID header, but we expect this to be reliable the vast majority of the time.
This commit is contained in:
parent
4ccabcbd3e
commit
d179cb9bdd
26 changed files with 1143 additions and 273 deletions
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
public class Geary.Sqlite.MessageRow : Geary.Sqlite.Row {
|
||||
public int64 id { get; set; default = INVALID_ID; }
|
||||
public Geary.Email.Field fields { get; set; default = Geary.Email.Field.NONE; }
|
||||
|
||||
public string? date { get; set; }
|
||||
public time_t date_time_t { get; set; default = -1; }
|
||||
|
|
@ -34,33 +35,19 @@ public class Geary.Sqlite.MessageRow : Geary.Sqlite.Row {
|
|||
public MessageRow.from_email(MessageTable table, Geary.Email email) {
|
||||
base (table);
|
||||
|
||||
date = (email.date != null) ? email.date.original : null;
|
||||
date_time_t = (email.date != null) ? email.date.as_time_t : -1;
|
||||
|
||||
from = flatten_addresses(email.from);
|
||||
sender = flatten_addresses(email.sender);
|
||||
reply_to = flatten_addresses(email.reply_to);
|
||||
|
||||
to = flatten_addresses(email.to);
|
||||
cc = flatten_addresses(email.cc);
|
||||
bcc = flatten_addresses(email.bcc);
|
||||
|
||||
message_id = (email.message_id != null) ? email.message_id.value : null;
|
||||
in_reply_to = (email.in_reply_to != null) ? email.in_reply_to.value : null;
|
||||
|
||||
subject = (email.subject != null) ? email.subject.value : null;
|
||||
|
||||
header = (email.header != null) ? email.header.buffer.to_ascii_string() : null;
|
||||
|
||||
body = (email.body != null) ? email.body.buffer.to_ascii_string() : null;
|
||||
set_from_email(email.fields, email);
|
||||
}
|
||||
|
||||
public MessageRow.from_query_result(Table table, Geary.Email.Field fields, SQLHeavy.QueryResult result)
|
||||
throws Error {
|
||||
public MessageRow.from_query_result(Table table, Geary.Email.Field requested_fields,
|
||||
SQLHeavy.QueryResult result) throws Error {
|
||||
base (table);
|
||||
|
||||
id = fetch_int64_for(result, MessageTable.Column.ID);
|
||||
|
||||
// the available fields are an intersection of what's available in the database and
|
||||
// what was requested
|
||||
fields = requested_fields & fetch_int_for(result, MessageTable.Column.FIELDS);
|
||||
|
||||
if ((fields & Geary.Email.Field.DATE) != 0) {
|
||||
date = fetch_string_for(result, MessageTable.Column.DATE_FIELD);
|
||||
date_time_t = (time_t) fetch_int64_for(result, MessageTable.Column.DATE_INT64);
|
||||
|
|
@ -96,31 +83,46 @@ public class Geary.Sqlite.MessageRow : Geary.Sqlite.Row {
|
|||
public Geary.Email to_email(Geary.EmailLocation location) throws Error {
|
||||
Geary.Email email = new Geary.Email(location);
|
||||
|
||||
email.date = (date != null) ? new RFC822.Date(date) : null;
|
||||
if (((fields & Geary.Email.Field.DATE) != 0) && (date != null))
|
||||
email.set_send_date(new RFC822.Date(date));
|
||||
|
||||
email.from = unflatten_addresses(from);
|
||||
email.sender = unflatten_addresses(sender);
|
||||
email.reply_to = unflatten_addresses(reply_to);
|
||||
if ((fields & Geary.Email.Field.ORIGINATORS) != 0) {
|
||||
email.set_originators(unflatten_addresses(from), unflatten_addresses(sender),
|
||||
unflatten_addresses(reply_to));
|
||||
}
|
||||
|
||||
email.to = unflatten_addresses(to);
|
||||
email.cc = unflatten_addresses(cc);
|
||||
email.bcc = unflatten_addresses(bcc);
|
||||
if ((fields & Geary.Email.Field.RECEIVERS) != 0) {
|
||||
email.set_receivers(unflatten_addresses(to), unflatten_addresses(cc),
|
||||
unflatten_addresses(bcc));
|
||||
}
|
||||
|
||||
email.message_id = (message_id != null) ? new RFC822.MessageID(message_id) : null;
|
||||
email.in_reply_to = (in_reply_to != null) ? new RFC822.MessageID(in_reply_to) : null;
|
||||
if ((fields & Geary.Email.Field.REFERENCES) != 0) {
|
||||
email.set_references((message_id != null) ? new RFC822.MessageID(message_id) : null,
|
||||
(in_reply_to != null) ? new RFC822.MessageID(in_reply_to) : null);
|
||||
}
|
||||
|
||||
email.subject = (subject != null) ? new RFC822.Subject(subject) : null;
|
||||
if (((fields & Geary.Email.Field.SUBJECT) != 0) && (subject != null))
|
||||
email.set_message_subject(new RFC822.Subject(subject));
|
||||
|
||||
email.header = (header != null) ? new RFC822.Header(new Geary.Memory.StringBuffer(header))
|
||||
: null;
|
||||
if (((fields & Geary.Email.Field.HEADER) != 0) && (header != null))
|
||||
email.set_message_header(new RFC822.Header(new Geary.Memory.StringBuffer(header)));
|
||||
|
||||
email.body = (body != null) ? new RFC822.Text(new Geary.Memory.StringBuffer(body))
|
||||
: null;
|
||||
if (((fields & Geary.Email.Field.BODY) != 0) && (body != null))
|
||||
email.set_message_body(new RFC822.Text(new Geary.Memory.StringBuffer(body)));
|
||||
|
||||
return email;
|
||||
}
|
||||
|
||||
public string? flatten_addresses(RFC822.MailboxAddresses? addrs) {
|
||||
public void merge_from_network(Geary.Email email) {
|
||||
foreach (Geary.Email.Field field in Geary.Email.Field.all()) {
|
||||
if ((email.fields & field) != 0)
|
||||
set_from_email(field, email);
|
||||
else
|
||||
unset_fields(field);
|
||||
}
|
||||
}
|
||||
|
||||
private string? flatten_addresses(RFC822.MailboxAddresses? addrs) {
|
||||
if (addrs == null)
|
||||
return null;
|
||||
|
||||
|
|
@ -144,8 +146,111 @@ public class Geary.Sqlite.MessageRow : Geary.Sqlite.Row {
|
|||
}
|
||||
}
|
||||
|
||||
public RFC822.MailboxAddresses? unflatten_addresses(string? str) {
|
||||
private RFC822.MailboxAddresses? unflatten_addresses(string? str) {
|
||||
return String.is_empty(str) ? null : new RFC822.MailboxAddresses.from_rfc822_string(str);
|
||||
}
|
||||
|
||||
private void set_from_email(Geary.Email.Field fields, Geary.Email email) {
|
||||
// Although the fields bitmask might indicate various fields are set, they may still be
|
||||
// null if empty
|
||||
|
||||
if ((fields & Geary.Email.Field.DATE) != 0) {
|
||||
date = (email.date != null) ? email.date.original : null;
|
||||
date_time_t = (email.date != null) ? email.date.as_time_t : -1;
|
||||
|
||||
this.fields = this.fields.set(Geary.Email.Field.DATE);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.ORIGINATORS) != 0) {
|
||||
from = flatten_addresses(email.from);
|
||||
sender = flatten_addresses(email.sender);
|
||||
reply_to = flatten_addresses(email.reply_to);
|
||||
|
||||
this.fields = this.fields.set(Geary.Email.Field.ORIGINATORS);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.RECEIVERS) != 0) {
|
||||
to = flatten_addresses(email.to);
|
||||
cc = flatten_addresses(email.cc);
|
||||
bcc = flatten_addresses(email.bcc);
|
||||
|
||||
this.fields = this.fields.set(Geary.Email.Field.RECEIVERS);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.REFERENCES) != 0) {
|
||||
message_id = (email.message_id != null) ? email.message_id.value : null;
|
||||
in_reply_to = (email.in_reply_to != null) ? email.in_reply_to.value : null;
|
||||
|
||||
this.fields = this.fields.set(Geary.Email.Field.REFERENCES);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.SUBJECT) != 0) {
|
||||
subject = (email.subject != null) ? email.subject.value : null;
|
||||
|
||||
this.fields = this.fields.set(Geary.Email.Field.SUBJECT);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.HEADER) != 0) {
|
||||
header = (email.header != null) ? email.header.buffer.to_ascii_string() : null;
|
||||
|
||||
this.fields = this.fields.set(Geary.Email.Field.HEADER);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.BODY) != 0) {
|
||||
body = (email.body != null) ? email.body.buffer.to_ascii_string() : null;
|
||||
|
||||
this.fields = this.fields.set(Geary.Email.Field.BODY);
|
||||
}
|
||||
}
|
||||
|
||||
private void unset_fields(Geary.Email.Field fields) {
|
||||
if ((fields & Geary.Email.Field.DATE) != 0) {
|
||||
date = null;
|
||||
date_time_t = -1;
|
||||
|
||||
this.fields = this.fields.clear(Geary.Email.Field.DATE);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.ORIGINATORS) != 0) {
|
||||
from = null;
|
||||
sender = null;
|
||||
reply_to = null;
|
||||
|
||||
this.fields = this.fields.clear(Geary.Email.Field.ORIGINATORS);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.RECEIVERS) != 0) {
|
||||
to = null;
|
||||
cc = null;
|
||||
bcc = null;
|
||||
|
||||
this.fields = this.fields.clear(Geary.Email.Field.RECEIVERS);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.REFERENCES) != 0) {
|
||||
message_id = null;
|
||||
in_reply_to = null;
|
||||
|
||||
this.fields = this.fields.clear(Geary.Email.Field.REFERENCES);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.SUBJECT) != 0) {
|
||||
subject = null;
|
||||
|
||||
this.fields = this.fields.clear(Geary.Email.Field.SUBJECT);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.HEADER) != 0) {
|
||||
header = null;
|
||||
|
||||
this.fields = this.fields.clear(Geary.Email.Field.HEADER);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.BODY) != 0) {
|
||||
body = null;
|
||||
|
||||
this.fields = this.fields.clear(Geary.Email.Field.BODY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue