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:
parent
9792780edb
commit
6b8951bfd8
30 changed files with 496 additions and 99 deletions
|
|
@ -69,8 +69,7 @@ CREATE TABLE ImapFolderPropertiesTable (
|
|||
id INTEGER PRIMARY KEY,
|
||||
folder_id INTEGER UNIQUE REFERENCES FolderTable ON DELETE CASCADE,
|
||||
uid_validity INTEGER,
|
||||
supports_children INTEGER,
|
||||
is_openable INTEGER
|
||||
attributes TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX ImapFolderPropertiesTableFolderIDIndex ON ImapFolderPropertiesTable(folder_id);
|
||||
|
|
@ -82,13 +81,7 @@ CREATE INDEX ImapFolderPropertiesTableFolderIDIndex ON ImapFolderPropertiesTable
|
|||
CREATE TABLE ImapMessagePropertiesTable (
|
||||
id INTEGER PRIMARY KEY,
|
||||
message_id INTEGER UNIQUE REFERENCES MessageTable ON DELETE CASCADE,
|
||||
answered INTEGER,
|
||||
deleted INTEGER,
|
||||
draft INTEGER,
|
||||
flagged INTEGER,
|
||||
recent INTEGER,
|
||||
seen INTEGER,
|
||||
all_flags TEXT
|
||||
flags TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX ImapMessagePropertiesTableMessageIDIndex ON ImapMessagePropertiesTable(message_id);
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ public class MainWindow : Gtk.Window {
|
|||
else
|
||||
debug("no folders");
|
||||
} catch (Error err) {
|
||||
error("%s", err.message);
|
||||
warning("%s", err.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,8 @@ class ImapConsole : Gtk.Window {
|
|||
"exit",
|
||||
"quit",
|
||||
"gmail",
|
||||
"keepalive"
|
||||
"keepalive",
|
||||
"status"
|
||||
};
|
||||
|
||||
private void exec(string input) {
|
||||
|
|
@ -180,6 +181,10 @@ class ImapConsole : Gtk.Window {
|
|||
keepalive(cmd, args);
|
||||
break;
|
||||
|
||||
case "status":
|
||||
folder_status(cmd, args);
|
||||
break;
|
||||
|
||||
default:
|
||||
status("Unknown command \"%s\"".printf(cmd));
|
||||
break;
|
||||
|
|
@ -393,6 +398,28 @@ class ImapConsole : Gtk.Window {
|
|||
}
|
||||
}
|
||||
|
||||
private void folder_status(string cmd, string[] args) throws Error {
|
||||
check_min_connected(cmd, args, 2, "<folder> <data-item...>");
|
||||
|
||||
status("Status %s".printf(args[0]));
|
||||
|
||||
Geary.Imap.StatusDataType[] data_items = new Geary.Imap.StatusDataType[0];
|
||||
for (int ctr = 1; ctr < args.length; ctr++)
|
||||
data_items += Geary.Imap.StatusDataType.decode(args[ctr]);
|
||||
|
||||
cx.send_async.begin(new Geary.Imap.StatusCommand(cx.generate_tag(), args[0], data_items),
|
||||
null, on_get_status);
|
||||
}
|
||||
|
||||
private void on_get_status(Object? source, AsyncResult result) {
|
||||
try {
|
||||
cx.send_async.end(result);
|
||||
status("Get status");
|
||||
} catch (Error err) {
|
||||
exception(err);
|
||||
}
|
||||
}
|
||||
|
||||
private void quit(string cmd, string[] args) throws Error {
|
||||
Gtk.main_quit();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
*/
|
||||
|
||||
public abstract class Geary.AbstractFolder : Object, Geary.Folder {
|
||||
protected virtual void notify_opened() {
|
||||
opened();
|
||||
protected virtual void notify_opened(Geary.Folder.OpenState state) {
|
||||
opened(state);
|
||||
}
|
||||
|
||||
protected virtual void notify_closed(Geary.Folder.CloseReason reason) {
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@
|
|||
private class Geary.EngineFolder : Geary.AbstractFolder {
|
||||
private const int REMOTE_FETCH_CHUNK_COUNT = 10;
|
||||
|
||||
private RemoteAccount remote;
|
||||
private LocalAccount local;
|
||||
private RemoteFolder? remote_folder = null;
|
||||
private LocalFolder local_folder;
|
||||
protected RemoteAccount remote;
|
||||
protected LocalAccount local;
|
||||
protected RemoteFolder? remote_folder = null;
|
||||
protected LocalFolder local_folder;
|
||||
|
||||
private bool opened = false;
|
||||
private Geary.Common.NonblockingSemaphore remote_semaphore =
|
||||
new Geary.Common.NonblockingSemaphore(true);
|
||||
|
|
@ -59,8 +60,6 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
|
|||
open_remote_async.begin(readonly, cancellable, on_open_remote_completed);
|
||||
|
||||
opened = true;
|
||||
|
||||
notify_opened();
|
||||
}
|
||||
|
||||
private async void open_remote_async(bool readonly, Cancellable? cancellable) throws Error {
|
||||
|
|
@ -77,6 +76,8 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
|
|||
private void on_open_remote_completed(Object? source, AsyncResult result) {
|
||||
try {
|
||||
open_remote_async.end(result);
|
||||
|
||||
notify_opened(Geary.Folder.OpenState.BOTH);
|
||||
} catch (Error err) {
|
||||
debug("Unable to open remote folder %s: %s", to_string(), err.message);
|
||||
|
||||
|
|
@ -86,6 +87,8 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
|
|||
} catch (Error err) {
|
||||
debug("Unable to notify remote folder ready: %s", err.message);
|
||||
}
|
||||
|
||||
notify_opened(Geary.Folder.OpenState.LOCAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -142,7 +145,7 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
|
|||
Gee.List<Geary.Email>? accumulator, EmailCallback? cb, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
assert(low >= 1);
|
||||
assert(count >= 0);
|
||||
assert(count >= 0 || count == -1);
|
||||
|
||||
if (!opened)
|
||||
throw new EngineError.OPEN_REQUIRED("%s is not open", to_string());
|
||||
|
|
@ -361,8 +364,10 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
|
|||
list = needed_by_position;
|
||||
}
|
||||
|
||||
// Always get the flags, and the generic end-user won't know to ask for them until they
|
||||
// need them
|
||||
Gee.List<Geary.Email>? remote_list = yield remote_folder.list_email_sparse_async(
|
||||
list, required_fields, cancellable);
|
||||
list, required_fields | Geary.Email.Field.PROPERTIES, cancellable);
|
||||
|
||||
if (remote_list == null || remote_list.size == 0)
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,12 @@
|
|||
public delegate void Geary.EmailCallback(Gee.List<Geary.Email>? emails, Error? err);
|
||||
|
||||
public interface Geary.Folder : Object {
|
||||
public enum OpenState {
|
||||
REMOTE,
|
||||
LOCAL,
|
||||
BOTH
|
||||
}
|
||||
|
||||
public enum CloseReason {
|
||||
LOCAL_CLOSE,
|
||||
REMOTE_CLOSE,
|
||||
|
|
@ -15,9 +21,9 @@ public interface Geary.Folder : Object {
|
|||
|
||||
/**
|
||||
* This is fired when the Folder is successfully opened by a caller. It will only fire once
|
||||
* until the Folder is closed.
|
||||
* until the Folder is closed, with the OpenState indicating what has been opened.
|
||||
*/
|
||||
public signal void opened();
|
||||
public signal void opened(OpenState state);
|
||||
|
||||
/**
|
||||
* This is fired when the Folder is successfully closed by a caller. It will only fire once
|
||||
|
|
@ -50,8 +56,8 @@ public interface Geary.Folder : Object {
|
|||
* directly. This allows subclasses and superclasses the opportunity to inspect the email
|
||||
* and update state before and/or after the signal has been fired.
|
||||
*/
|
||||
protected virtual void notify_opened() {
|
||||
opened();
|
||||
protected virtual void notify_opened(OpenState state) {
|
||||
opened(state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -140,7 +146,8 @@ public interface Geary.Folder : Object {
|
|||
|
||||
/**
|
||||
* Returns a list of messages that fulfill the required_fields flags starting at the low
|
||||
* position and moving up to (low + count). The list is not guaranteed to be in any
|
||||
* position and moving up to (low + count). If count is -1, the returned list starts at low
|
||||
* and proceeds to all available emails. The returned list is not guaranteed to be in any
|
||||
* particular order.
|
||||
*
|
||||
* If any position in low to (low + count) are out of range, only the email within range are
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ private class Geary.GenericImapAccount : Geary.EngineAccount {
|
|||
Gee.Collection<Geary.Folder> engine_list = new Gee.ArrayList<Geary.Folder>();
|
||||
if (local_list != null && local_list.size > 0) {
|
||||
foreach (Geary.Folder local_folder in local_list)
|
||||
engine_list.add(new EngineFolder(remote, local, (LocalFolder) local_folder));
|
||||
engine_list.add(new GenericImapFolder(remote, local, (LocalFolder) local_folder));
|
||||
}
|
||||
|
||||
background_update_folders.begin(parent, engine_list);
|
||||
|
|
@ -72,7 +72,7 @@ private class Geary.GenericImapAccount : Geary.EngineAccount {
|
|||
try {
|
||||
local_folder = (LocalFolder) yield local.fetch_folder_async(path, cancellable);
|
||||
|
||||
return new EngineFolder(remote, local, local_folder);
|
||||
return new GenericImapFolder(remote, local, local_folder);
|
||||
} catch (EngineError err) {
|
||||
// don't thrown NOT_FOUND's, that means we need to fall through and clone from the
|
||||
// server
|
||||
|
|
@ -94,10 +94,10 @@ private class Geary.GenericImapAccount : Geary.EngineAccount {
|
|||
yield local.clone_folder_async(remote_folder, cancellable);
|
||||
}
|
||||
|
||||
// Fetch the local account's version of the folder for the EngineFolder
|
||||
// Fetch the local account's version of the folder for the GenericImapFolder
|
||||
local_folder = (LocalFolder) yield local.fetch_folder_async(path, cancellable);
|
||||
|
||||
return new EngineFolder(remote, local, local_folder);
|
||||
return new GenericImapFolder(remote, local, local_folder);
|
||||
}
|
||||
|
||||
private Gee.Set<string> get_folder_names(Gee.Collection<Geary.Folder> folders) {
|
||||
|
|
@ -140,11 +140,15 @@ private class Geary.GenericImapAccount : Geary.EngineAccount {
|
|||
if (to_remove.size == 0)
|
||||
to_remove = null;
|
||||
|
||||
if (to_add != null) {
|
||||
foreach (Geary.Folder folder in to_add) {
|
||||
try {
|
||||
if (to_add != null)
|
||||
yield local.clone_many_folders_async(to_add);
|
||||
yield local.clone_folder_async(folder);
|
||||
} catch (Error err) {
|
||||
error("Unable to add/remove folders: %s", err.message);
|
||||
debug("Unable to add/remove folder %s: %s", folder.get_path().to_string(),
|
||||
err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Gee.Collection<Geary.Folder> engine_added = null;
|
||||
|
|
@ -154,7 +158,7 @@ private class Geary.GenericImapAccount : Geary.EngineAccount {
|
|||
try {
|
||||
LocalFolder local_folder = (LocalFolder) yield local.fetch_folder_async(
|
||||
remote_folder.get_path());
|
||||
engine_added.add(new EngineFolder(remote, local, local_folder));
|
||||
engine_added.add(new GenericImapFolder(remote, local, local_folder));
|
||||
} catch (Error convert_err) {
|
||||
error("Unable to fetch local folder: %s", convert_err.message);
|
||||
}
|
||||
|
|
|
|||
11
src/engine/api/geary-generic-imap-folder.vala
Normal file
11
src/engine/api/geary-generic-imap-folder.vala
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
private class Geary.GenericImapFolder : Geary.EngineFolder {
|
||||
public GenericImapFolder(RemoteAccount remote, LocalAccount local, LocalFolder local_folder) {
|
||||
base (remote, local, local_folder);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,9 +8,6 @@ public interface Geary.LocalAccount : Object, Geary.Account {
|
|||
public abstract async void clone_folder_async(Geary.Folder folder, Cancellable? cancellable = null)
|
||||
throws Error;
|
||||
|
||||
public abstract async void clone_many_folders_async(Gee.Collection<Geary.Folder> folders,
|
||||
Cancellable? cancellable = null) throws Error;
|
||||
|
||||
/**
|
||||
* Returns true if the email (identified by its Message-ID) already exists in the account's
|
||||
* local store, no matter the folder.
|
||||
|
|
|
|||
|
|
@ -66,7 +66,18 @@ public class Geary.Imap.Account : Geary.AbstractAccount, Geary.RemoteAccount {
|
|||
if (processed == null)
|
||||
delims.set(path.get_root().basename, mbox.delim);
|
||||
|
||||
folders.add(new Geary.Imap.Folder(session_mgr, path, mbox));
|
||||
UIDValidity? uid_validity = null;
|
||||
if (!mbox.attrs.contains(MailboxAttribute.NO_SELECT)) {
|
||||
try {
|
||||
StatusResults results = yield session_mgr.status_async(path.get_fullpath(),
|
||||
{ StatusDataType.UIDVALIDITY }, cancellable);
|
||||
uid_validity = results.uidvalidity;
|
||||
} catch (Error status_err) {
|
||||
message("Unable to fetch UID Validity for %s: %s", path.to_string(), status_err.message);
|
||||
}
|
||||
}
|
||||
|
||||
folders.add(new Geary.Imap.Folder(session_mgr, path, uid_validity, mbox));
|
||||
}
|
||||
|
||||
return folders;
|
||||
|
|
@ -93,7 +104,14 @@ public class Geary.Imap.Account : Geary.AbstractAccount, Geary.RemoteAccount {
|
|||
if (mbox == null)
|
||||
throw_not_found(path);
|
||||
|
||||
return new Geary.Imap.Folder(session_mgr, processed, mbox);
|
||||
UIDValidity? uid_validity = null;
|
||||
if (!mbox.attrs.contains(MailboxAttribute.NO_SELECT)) {
|
||||
StatusResults results = yield session_mgr.status_async(processed.get_fullpath(),
|
||||
{ StatusDataType.UIDVALIDITY }, cancellable);
|
||||
uid_validity = results.uidvalidity;
|
||||
}
|
||||
|
||||
return new Geary.Imap.Folder(session_mgr, processed, uid_validity, mbox);
|
||||
} catch (ImapError err) {
|
||||
if (err is ImapError.SERVER_ERROR)
|
||||
throw_not_found(path);
|
||||
|
|
|
|||
|
|
@ -5,10 +5,27 @@
|
|||
*/
|
||||
|
||||
public class Geary.Imap.EmailProperties : Geary.EmailProperties {
|
||||
public bool answered { get; private set; }
|
||||
public bool deleted { get; private set; }
|
||||
public bool draft { get; private set; }
|
||||
public bool flagged { get; private set; }
|
||||
public bool recent { get; private set; }
|
||||
public bool seen { get; private set; }
|
||||
public MessageFlags flags { get; private set; }
|
||||
|
||||
public EmailProperties(MessageFlags flags) {
|
||||
this.flags = flags;
|
||||
|
||||
answered = flags.contains(MessageFlag.ANSWERED);
|
||||
deleted = flags.contains(MessageFlag.DELETED);
|
||||
draft = flags.contains(MessageFlag.DRAFT);
|
||||
flagged = flags.contains(MessageFlag.FLAGGED);
|
||||
recent = flags.contains(MessageFlag.RECENT);
|
||||
seen = flags.contains(MessageFlag.SEEN);
|
||||
}
|
||||
|
||||
public bool is_empty() {
|
||||
return (flags.size == 0);
|
||||
}
|
||||
|
||||
public override bool is_unread() {
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
|
||||
public class Geary.Imap.FolderProperties : Geary.FolderProperties {
|
||||
public UID? uid_validity { get; set; }
|
||||
public UIDValidity? uid_validity { get; set; }
|
||||
public MailboxAttributes attrs { get; private set; }
|
||||
public Trillian supports_children { get; private set; }
|
||||
public Trillian has_children { get; private set; }
|
||||
public Trillian is_openable { get; private set; }
|
||||
|
||||
public FolderProperties(UID? uid_validity, MailboxAttributes attrs) {
|
||||
public FolderProperties(UIDValidity? uid_validity, MailboxAttributes attrs) {
|
||||
this.uid_validity = uid_validity;
|
||||
this.attrs = attrs;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,13 +14,14 @@ public class Geary.Imap.Folder : Geary.AbstractFolder, Geary.RemoteFolder {
|
|||
private Imap.FolderProperties properties;
|
||||
private Mailbox? mailbox = null;
|
||||
|
||||
internal Folder(ClientSessionManager session_mgr, Geary.FolderPath path, MailboxInformation info) {
|
||||
internal Folder(ClientSessionManager session_mgr, Geary.FolderPath path, UIDValidity? uid_validity,
|
||||
MailboxInformation info) {
|
||||
this.session_mgr = session_mgr;
|
||||
this.info = info;
|
||||
this.path = path;
|
||||
|
||||
readonly = Trillian.UNKNOWN;
|
||||
properties = new Imap.FolderProperties(null, info.attrs);
|
||||
properties = new Imap.FolderProperties(uid_validity, info.attrs);
|
||||
}
|
||||
|
||||
public override Geary.FolderPath get_path() {
|
||||
|
|
@ -46,7 +47,7 @@ public class Geary.Imap.Folder : Geary.AbstractFolder, Geary.RemoteFolder {
|
|||
this.readonly = Trillian.from_boolean(readonly);
|
||||
properties.uid_validity = mailbox.uid_validity;
|
||||
|
||||
notify_opened();
|
||||
notify_opened(Geary.Folder.OpenState.REMOTE);
|
||||
}
|
||||
|
||||
public override async void close_async(Cancellable? cancellable = null) throws Error {
|
||||
|
|
@ -79,7 +80,11 @@ public class Geary.Imap.Folder : Geary.AbstractFolder, Geary.RemoteFolder {
|
|||
if (mailbox == null)
|
||||
throw new EngineError.OPEN_REQUIRED("%s not opened", to_string());
|
||||
|
||||
return yield mailbox.list_set_async(new MessageSet.range(low, count), fields, cancellable);
|
||||
MessageSet msg_set = (count != -1)
|
||||
? new MessageSet.range(low, count)
|
||||
: new MessageSet.range_to_highest(low);
|
||||
|
||||
return yield mailbox.list_set_async(msg_set, fields, cancellable);
|
||||
}
|
||||
|
||||
public override async Gee.List<Geary.Email>? list_email_sparse_async(int[] by_position,
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ public class Geary.Imap.SelectExamineResults : Geary.Imap.CommandResults {
|
|||
* -1 if not specified.
|
||||
*/
|
||||
public int unseen { get; private set; }
|
||||
public UID? uid_validity { get; private set; }
|
||||
public UIDValidity? uid_validity { get; private set; }
|
||||
public Flags? flags { get; private set; }
|
||||
public Flags? permanentflags { get; private set; }
|
||||
public bool readonly { get; private set; }
|
||||
|
||||
private SelectExamineResults(StatusResponse status_response, int exists, int recent, int unseen,
|
||||
UID? uidvalidity, Flags? flags, Flags? permanentflags, bool readonly) {
|
||||
UIDValidity? uidvalidity, Flags? flags, Flags? permanentflags, bool readonly) {
|
||||
base (status_response);
|
||||
|
||||
this.exists = exists;
|
||||
|
|
@ -41,7 +41,7 @@ public class Geary.Imap.SelectExamineResults : Geary.Imap.CommandResults {
|
|||
int exists = -1;
|
||||
int recent = -1;
|
||||
int unseen = -1;
|
||||
UID? uidvalidity = null;
|
||||
UIDValidity? uidvalidity = null;
|
||||
UID? uidnext = null;
|
||||
MessageFlags? flags = null;
|
||||
MessageFlags? permanentflags = null;
|
||||
|
|
@ -75,7 +75,7 @@ public class Geary.Imap.SelectExamineResults : Geary.Imap.CommandResults {
|
|||
break;
|
||||
|
||||
case ResponseCodeType.UIDVALIDITY:
|
||||
uidvalidity = new UID(
|
||||
uidvalidity = new UIDValidity(
|
||||
ok_response.response_code.get_as_string(1).as_int());
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ public class Geary.Imap.StatusResults : Geary.Imap.CommandResults {
|
|||
*/
|
||||
public int recent { get; private set; }
|
||||
public UID? uidnext { get; private set; }
|
||||
public UID? uidvalidity { get; private set; }
|
||||
public UIDValidity? uidvalidity { get; private set; }
|
||||
/**
|
||||
* -1 if not set.
|
||||
*/
|
||||
public int unseen { get; private set; }
|
||||
|
||||
public StatusResults(StatusResponse status_response, string mailbox, int messages, int recent,
|
||||
UID? uidnext, UID? uidvalidity, int unseen) {
|
||||
UID? uidnext, UIDValidity? uidvalidity, int unseen) {
|
||||
base (status_response);
|
||||
|
||||
this.mailbox = mailbox;
|
||||
|
|
@ -54,7 +54,7 @@ public class Geary.Imap.StatusResults : Geary.Imap.CommandResults {
|
|||
int messages = -1;
|
||||
int recent = -1;
|
||||
UID? uidnext = null;
|
||||
UID? uidvalidity = null;
|
||||
UIDValidity? uidvalidity = null;
|
||||
int unseen = -1;
|
||||
|
||||
for (int ctr = 0; ctr < values.get_count(); ctr += 2) {
|
||||
|
|
@ -76,7 +76,7 @@ public class Geary.Imap.StatusResults : Geary.Imap.CommandResults {
|
|||
break;
|
||||
|
||||
case StatusDataType.UIDVALIDITY:
|
||||
uidvalidity = new UID(valuep.as_int());
|
||||
uidvalidity = new UIDValidity(valuep.as_int());
|
||||
break;
|
||||
|
||||
case StatusDataType.UNSEEN:
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@ public class Geary.Imap.UID : Geary.Common.Int64MessageData, Geary.Imap.MessageD
|
|||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.UIDValidity : Geary.Common.Int64MessageData, Geary.Imap.MessageData {
|
||||
public UIDValidity(int64 value) {
|
||||
base (value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.MessageNumber : Geary.Common.IntMessageData, Geary.Imap.MessageData {
|
||||
public MessageNumber(int value) {
|
||||
base (value);
|
||||
|
|
@ -32,6 +38,8 @@ public class Geary.Imap.MessageNumber : Geary.Common.IntMessageData, Geary.Imap.
|
|||
}
|
||||
|
||||
public abstract class Geary.Imap.Flags : Geary.Common.MessageData, Geary.Imap.MessageData {
|
||||
public int size { get { return list.size; } }
|
||||
|
||||
private Gee.Set<Flag> list;
|
||||
|
||||
public Flags(Gee.Collection<Flag> flags) {
|
||||
|
|
@ -47,6 +55,14 @@ public abstract class Geary.Imap.Flags : Geary.Common.MessageData, Geary.Imap.Me
|
|||
return list.read_only_view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flags in serialized form, which is each flag separated by a space (legal in
|
||||
* IMAP, as flags must be atoms and atoms prohibit spaces).
|
||||
*/
|
||||
public virtual string serialize() {
|
||||
return to_string();
|
||||
}
|
||||
|
||||
public override string to_string() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
foreach (Flag flag in list) {
|
||||
|
|
@ -79,12 +95,32 @@ public class Geary.Imap.MessageFlags : Geary.Imap.Flags {
|
|||
|
||||
return new MessageFlags(list);
|
||||
}
|
||||
|
||||
public static MessageFlags deserialize(string str) {
|
||||
string[] tokens = str.split(" ");
|
||||
|
||||
Gee.Collection<MessageFlag> flags = new Gee.ArrayList<MessageFlag>();
|
||||
foreach (string token in tokens)
|
||||
flags.add(new MessageFlag(token));
|
||||
|
||||
return new MessageFlags(flags);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.MailboxAttributes : Geary.Imap.Flags {
|
||||
public MailboxAttributes(Gee.Collection<MailboxAttribute> attrs) {
|
||||
base (attrs);
|
||||
}
|
||||
|
||||
public static MailboxAttributes deserialize(string str) {
|
||||
string[] tokens = str.split(" ");
|
||||
|
||||
Gee.Collection<MailboxAttribute> attrs = new Gee.ArrayList<MailboxAttribute>();
|
||||
foreach (string token in tokens)
|
||||
attrs.add(new MailboxAttribute(token));
|
||||
|
||||
return new MailboxAttributes(attrs);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.InternalDate : Geary.RFC822.Date, Geary.Imap.MessageData {
|
||||
|
|
|
|||
|
|
@ -98,6 +98,19 @@ public class Geary.Imap.ClientSessionManager {
|
|||
return (results.get_count() > 0) ? results.get_all()[0] : null;
|
||||
}
|
||||
|
||||
public async Geary.Imap.StatusResults status_async(string path, StatusDataType[] types,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
ClientSession session = yield get_authorized_session(cancellable);
|
||||
|
||||
StatusResults results = StatusResults.decode(yield session.send_command_async(
|
||||
new StatusCommand(session.generate_tag(), path, types), cancellable));
|
||||
|
||||
if (results.status_response.status != Status.OK)
|
||||
throw new ImapError.SERVER_ERROR("Server error: %s", results.to_string());
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public async Mailbox select_mailbox(string path, Cancellable? cancellable = null) throws Error {
|
||||
return yield select_examine_mailbox(path, true, cancellable);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ public class Geary.Imap.Mailbox : Geary.SmartReference {
|
|||
public string name { get; private set; }
|
||||
public int count { get; private set; }
|
||||
public bool is_readonly { get; private set; }
|
||||
public UID uid_validity { get; private set; }
|
||||
public UIDValidity uid_validity { get; private set; }
|
||||
|
||||
private SelectedContext context;
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ internal class Geary.Imap.SelectedContext : Object, Geary.ReferenceSemantics {
|
|||
public int exists { get; protected set; }
|
||||
public int recent { get; protected set; }
|
||||
public bool is_readonly { get; protected set; }
|
||||
public UID uid_validity { get; protected set; }
|
||||
public UIDValidity uid_validity { get; protected set; }
|
||||
|
||||
public signal void exists_changed(int exists);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,14 @@ public abstract class Geary.Sqlite.Table {
|
|||
return table.field_name(col);
|
||||
}
|
||||
|
||||
protected inline static int bool_to_int(bool b) {
|
||||
return b ? 1 : 0;
|
||||
}
|
||||
|
||||
protected inline static bool int_to_bool(int i) {
|
||||
return !(i == 0);
|
||||
}
|
||||
|
||||
public string to_string() {
|
||||
return table.name;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
Geary.Imap.Folder imap_folder = (Geary.Imap.Folder) folder;
|
||||
Geary.Imap.FolderProperties? imap_folder_properties = (Geary.Imap.FolderProperties?)
|
||||
imap_folder.get_properties();
|
||||
|
||||
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));
|
||||
}
|
||||
// properties *must* be available to perform a clone
|
||||
assert(imap_folder_properties != null);
|
||||
|
||||
yield folder_table.create_many_async(rows, cancellable);
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,20 +33,11 @@ public class Geary.Sqlite.FolderTable : Geary.Sqlite.Table {
|
|||
query.bind_null(1);
|
||||
}
|
||||
|
||||
public async void create_async(FolderRow row, Cancellable? cancellable = null) throws Error {
|
||||
public async int64 create_async(FolderRow row, Cancellable? cancellable = null) throws Error {
|
||||
SQLHeavy.Query query = create_query();
|
||||
create_binding(query, row);
|
||||
|
||||
yield query.execute_insert_async(cancellable);
|
||||
}
|
||||
|
||||
public async void create_many_async(Gee.Collection<FolderRow> rows, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
SQLHeavy.Query query = create_query();
|
||||
foreach (FolderRow row in rows) {
|
||||
create_binding(query, row);
|
||||
query.execute_insert();
|
||||
}
|
||||
return yield query.execute_insert_async(cancellable);
|
||||
}
|
||||
|
||||
public async Gee.List<FolderRow> list_async(int64 parent_id, Cancellable? cancellable = null)
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ public class Geary.Sqlite.MailDatabase : Geary.Sqlite.Database {
|
|||
: (MessageLocationTable) add_table(new MessageLocationTable(this, heavy_table));
|
||||
}
|
||||
|
||||
// TODO: This belongs in a subclass.
|
||||
public Geary.Sqlite.ImapMessageLocationPropertiesTable get_imap_message_location_table() {
|
||||
SQLHeavy.Table heavy_table;
|
||||
ImapMessageLocationPropertiesTable? imap_location_table = get_table(
|
||||
|
|
@ -49,5 +50,25 @@ public class Geary.Sqlite.MailDatabase : Geary.Sqlite.Database {
|
|||
? imap_location_table
|
||||
: (ImapMessageLocationPropertiesTable) add_table(new ImapMessageLocationPropertiesTable(this, heavy_table));
|
||||
}
|
||||
|
||||
// TODO: This belongs in a subclass.
|
||||
public Geary.Sqlite.ImapFolderPropertiesTable get_imap_folder_properties_table() {
|
||||
SQLHeavy.Table heavy_table;
|
||||
ImapFolderPropertiesTable? imap_folder_properties_table = get_table(
|
||||
"ImapFolderPropertiesTable", out heavy_table) as ImapFolderPropertiesTable;
|
||||
|
||||
return imap_folder_properties_table
|
||||
?? (ImapFolderPropertiesTable) add_table(new ImapFolderPropertiesTable(this, heavy_table));
|
||||
}
|
||||
|
||||
// TODO: This belongs in a subclass.
|
||||
public Geary.Sqlite.ImapMessagePropertiesTable get_imap_message_properties_table() {
|
||||
SQLHeavy.Table heavy_table;
|
||||
ImapMessagePropertiesTable? imap_message_properties_table = get_table(
|
||||
"ImapMessagePropertiesTable", out heavy_table) as ImapMessagePropertiesTable;
|
||||
|
||||
return imap_message_properties_table
|
||||
?? (ImapMessagePropertiesTable) add_table(new ImapMessagePropertiesTable(this, heavy_table));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,18 +29,29 @@ public class Geary.Sqlite.MessageLocationTable : Geary.Sqlite.Table {
|
|||
}
|
||||
|
||||
/**
|
||||
* low is one-based.
|
||||
* low is one-based. If count is -1, all messages starting at low are returned.
|
||||
*/
|
||||
public async Gee.List<MessageLocationRow>? list_async(int64 folder_id, int low, int count,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
assert(low >= 1);
|
||||
assert(count >= 0 || count == -1);
|
||||
|
||||
SQLHeavy.Query query = db.prepare(
|
||||
SQLHeavy.Query query;
|
||||
if (count >= 0) {
|
||||
query = db.prepare(
|
||||
"SELECT id, message_id, position FROM MessageLocationTable WHERE folder_id = ? "
|
||||
+ "ORDER BY position LIMIT ? OFFSET ?");
|
||||
query.bind_int64(0, folder_id);
|
||||
query.bind_int(1, count);
|
||||
query.bind_int(2, low - 1);
|
||||
} else {
|
||||
// count == -1
|
||||
query = db.prepare(
|
||||
"SELECT id, message_id, position FROM MessageLocationTable WHERE folder_id = ? "
|
||||
+ "ORDER BY position OFFSET ?");
|
||||
query.bind_int64(0, folder_id);
|
||||
query.bind_int(1, low - 1);
|
||||
}
|
||||
|
||||
SQLHeavy.QueryResult results = yield query.execute_async(cancellable);
|
||||
if (results.finished)
|
||||
|
|
@ -103,7 +114,7 @@ public class Geary.Sqlite.MessageLocationTable : Geary.Sqlite.Table {
|
|||
"SELECT id, message_id, position FROM MessageLocationTable WHERE folder_id = ? "
|
||||
+ "AND position = ?");
|
||||
query.bind_int64(0, folder_id);
|
||||
query.bind_int64(1, position);
|
||||
query.bind_int(1, position);
|
||||
|
||||
SQLHeavy.QueryResult results = yield query.execute_async(cancellable);
|
||||
if (results.finished)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
public class Geary.Sqlite.ImapFolderPropertiesRow : Geary.Sqlite.Row {
|
||||
public int64 id { get; private set; }
|
||||
public int64 folder_id { get; private set; }
|
||||
public Geary.Imap.UIDValidity? uid_validity { get; private set; }
|
||||
public string attributes { get; private set; }
|
||||
|
||||
public ImapFolderPropertiesRow(ImapFolderPropertiesTable table, int64 id, int64 folder_id,
|
||||
Geary.Imap.UIDValidity? uid_validity, string attributes) {
|
||||
base (table);
|
||||
|
||||
this.id = id;
|
||||
this.folder_id = folder_id;
|
||||
this.uid_validity = uid_validity;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public ImapFolderPropertiesRow.from_imap_properties(ImapFolderPropertiesTable table,
|
||||
int64 folder_id, Geary.Imap.FolderProperties properties) {
|
||||
base (table);
|
||||
|
||||
id = Row.INVALID_ID;
|
||||
this.folder_id = folder_id;
|
||||
uid_validity = properties.uid_validity;
|
||||
attributes = properties.attrs.serialize();
|
||||
}
|
||||
|
||||
public Geary.Imap.FolderProperties get_imap_folder_properties() {
|
||||
return new Geary.Imap.FolderProperties(uid_validity,
|
||||
Geary.Imap.MailboxAttributes.deserialize(attributes));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
public class Geary.Sqlite.ImapFolderPropertiesTable : Geary.Sqlite.Table {
|
||||
// This *must* be in the same order as the schema.
|
||||
public enum Column {
|
||||
ID,
|
||||
FOLDER_ID,
|
||||
UID_VALIDITY,
|
||||
FLAGS
|
||||
}
|
||||
|
||||
public ImapFolderPropertiesTable(Geary.Sqlite.Database gdb, SQLHeavy.Table table) {
|
||||
base (gdb, table);
|
||||
}
|
||||
|
||||
public async int64 create_async(ImapFolderPropertiesRow row, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
SQLHeavy.Query query = db.prepare(
|
||||
"INSERT INTO ImapFolderPropertiesTable (folder_id, uid_validity, attributes) VALUES (?, ?, ?)");
|
||||
query.bind_int64(0, row.folder_id);
|
||||
query.bind_int64(1, (row.uid_validity != null) ? row.uid_validity.value : -1);
|
||||
query.bind_string(2, row.attributes);
|
||||
|
||||
return yield query.execute_insert_async(cancellable);
|
||||
}
|
||||
|
||||
public async ImapFolderPropertiesRow? fetch_async(int64 folder_id, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
SQLHeavy.Query query = db.prepare(
|
||||
"SELECT id, uid_validity, attributes FROM ImapFolderPropertiesTable WHERE folder_id = ?");
|
||||
query.bind_int64(0, folder_id);
|
||||
|
||||
SQLHeavy.QueryResult result = yield query.execute_async(cancellable);
|
||||
if (result.finished)
|
||||
return null;
|
||||
|
||||
Geary.Imap.UIDValidity? uid_validity = null;
|
||||
if (result.fetch_int64(1) >= 0)
|
||||
uid_validity = new Geary.Imap.UIDValidity(result.fetch_int64(1));
|
||||
|
||||
return new ImapFolderPropertiesRow(this, result.fetch_int64(0), folder_id, uid_validity,
|
||||
result.fetch_string(2));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
public class Geary.Sqlite.ImapMessagePropertiesRow : Geary.Sqlite.Row {
|
||||
public int64 id { get; private set; }
|
||||
public int64 message_id { get; private set; }
|
||||
public string flags { get; private set; }
|
||||
|
||||
public ImapMessagePropertiesRow(ImapMessagePropertiesTable table, int64 id, int64 message_id,
|
||||
string flags) {
|
||||
base (table);
|
||||
|
||||
this.id = id;
|
||||
this.message_id = message_id;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
public ImapMessagePropertiesRow.from_imap_properties(ImapMessagePropertiesTable table,
|
||||
int64 message_id, Geary.Imap.EmailProperties properties) {
|
||||
base (table);
|
||||
|
||||
id = Row.INVALID_ID;
|
||||
this.message_id = message_id;
|
||||
flags = properties.flags.serialize();
|
||||
}
|
||||
|
||||
public Geary.Imap.EmailProperties get_imap_email_properties() {
|
||||
return new Geary.Imap.EmailProperties(Geary.Imap.MessageFlags.deserialize(flags));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
public class Geary.Sqlite.ImapMessagePropertiesTable : Geary.Sqlite.Table {
|
||||
// This *must* be in the same order as the schema.
|
||||
public enum Column {
|
||||
ID,
|
||||
FLAGS
|
||||
}
|
||||
|
||||
public ImapMessagePropertiesTable(Geary.Sqlite.Database gdb, SQLHeavy.Table table) {
|
||||
base (gdb, table);
|
||||
}
|
||||
|
||||
public async int64 create_async(ImapMessagePropertiesRow row, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
SQLHeavy.Query query = db.prepare(
|
||||
"INSERT INTO ImapMessagePropertiesTable (message_id, flags) VALUES (?, ?)");
|
||||
query.bind_int64(0, row.message_id);
|
||||
query.bind_string(1, row.flags);
|
||||
|
||||
return yield query.execute_insert_async(cancellable);
|
||||
}
|
||||
|
||||
public async ImapMessagePropertiesRow? fetch_async(int64 message_id, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
SQLHeavy.Query query = db.prepare(
|
||||
"SELECT id, flags FROM ImapMessagePropertiesTable WHERE message_id = ?");
|
||||
query.bind_int64(0, message_id);
|
||||
|
||||
SQLHeavy.QueryResult result = yield query.execute_async(cancellable);
|
||||
if (result.finished)
|
||||
return null;
|
||||
|
||||
return new ImapMessagePropertiesRow(this, result.fetch_int64(0), message_id,
|
||||
result.fetch_string(1));
|
||||
}
|
||||
|
||||
public async void update_async(int64 message_id, string flags, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
SQLHeavy.Query query = db.prepare(
|
||||
"UPDATE ImapMessagePropertiesTable SET flags = ? WHERE message_id = ?");
|
||||
query.bind_string(0, flags);
|
||||
query.bind_int64(1, message_id);
|
||||
|
||||
yield query.execute_async(cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
public enum Geary.Trillian {
|
||||
// DO NOT MODIFY unless you know what you're doing. These values are persisted.
|
||||
UNKNOWN = -1,
|
||||
FALSE = 0,
|
||||
TRUE = 1;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ def build(bld):
|
|||
'../engine/api/geary-folder-properties.vala',
|
||||
'../engine/api/geary-folder.vala',
|
||||
'../engine/api/geary-generic-imap-account.vala',
|
||||
'../engine/api/geary-generic-imap-folder.vala',
|
||||
'../engine/api/geary-gmail-account.vala',
|
||||
'../engine/api/geary-local-interfaces.vala',
|
||||
'../engine/api/geary-personality.vala',
|
||||
|
|
@ -99,8 +100,12 @@ def build(bld):
|
|||
'../engine/sqlite/email/sqlite-message-location-table.vala',
|
||||
'../engine/sqlite/email/sqlite-message-row.vala',
|
||||
'../engine/sqlite/email/sqlite-message-table.vala',
|
||||
'../engine/sqlite/imap/sqlite-imap-folder-properties-row.vala',
|
||||
'../engine/sqlite/imap/sqlite-imap-folder-properties-table.vala',
|
||||
'../engine/sqlite/imap/sqlite-imap-message-location-properties-row.vala',
|
||||
'../engine/sqlite/imap/sqlite-imap-message-location-properties-table.vala',
|
||||
'../engine/sqlite/imap/sqlite-imap-message-properties-row.vala',
|
||||
'../engine/sqlite/imap/sqlite-imap-message-properties-table.vala',
|
||||
|
||||
'../engine/state/state-machine-descriptor.vala',
|
||||
'../engine/state/state-machine.vala',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue