Addition of MessageTable and MessageLocationTable toward fixing #3742.
Much of the API between the local and net stores had to be reworked for consistency as well as planning ahead for how messages will be retrieved and stored efficiently. This work also attempts to keep in mind that other mail sources (POP, etc.) may be required in the future, and hopefully can be added without major rework.
This commit is contained in:
parent
ac41e9269a
commit
9221937e95
38 changed files with 1239 additions and 460 deletions
11
Makefile
11
Makefile
|
|
@ -10,10 +10,14 @@ APPS := geary console watchmbox
|
|||
|
||||
ENGINE_SRC := \
|
||||
src/engine/Engine.vala \
|
||||
src/engine/ImapEngine.vala \
|
||||
src/engine/EngineFolder.vala \
|
||||
src/engine/api/Account.vala \
|
||||
src/engine/api/Email.vala \
|
||||
src/engine/api/EmailProperties.vala \
|
||||
src/engine/api/EmailOrdering.vala \
|
||||
src/engine/api/Folder.vala \
|
||||
src/engine/api/FolderProperties.vala \
|
||||
src/engine/api/Credentials.vala \
|
||||
src/engine/api/EngineError.vala \
|
||||
src/engine/sqlite/Database.vala \
|
||||
|
|
@ -22,6 +26,10 @@ ENGINE_SRC := \
|
|||
src/engine/sqlite/MailDatabase.vala \
|
||||
src/engine/sqlite/FolderTable.vala \
|
||||
src/engine/sqlite/FolderRow.vala \
|
||||
src/engine/sqlite/MessageRow.vala \
|
||||
src/engine/sqlite/MessageTable.vala \
|
||||
src/engine/sqlite/MessageLocationRow.vala \
|
||||
src/engine/sqlite/MessageLocationTable.vala \
|
||||
src/engine/sqlite/api/Account.vala \
|
||||
src/engine/sqlite/api/Folder.vala \
|
||||
src/engine/state/Machine.vala \
|
||||
|
|
@ -33,7 +41,6 @@ ENGINE_SRC := \
|
|||
src/engine/imap/ClientSessionManager.vala \
|
||||
src/engine/imap/DataFormat.vala \
|
||||
src/engine/imap/Mailbox.vala \
|
||||
src/engine/imap/Email.vala \
|
||||
src/engine/imap/Parameter.vala \
|
||||
src/engine/imap/Tag.vala \
|
||||
src/engine/imap/Command.vala \
|
||||
|
|
@ -63,7 +70,9 @@ ENGINE_SRC := \
|
|||
src/engine/imap/decoders/SelectExamineResults.vala \
|
||||
src/engine/imap/decoders/StatusResults.vala \
|
||||
src/engine/imap/api/Account.vala \
|
||||
src/engine/imap/api/EmailProperties.vala \
|
||||
src/engine/imap/api/Folder.vala \
|
||||
src/engine/imap/api/FolderProperties.vala \
|
||||
src/engine/rfc822/MailboxAddress.vala \
|
||||
src/engine/rfc822/MessageData.vala \
|
||||
src/engine/util/Memory.vala \
|
||||
|
|
|
|||
|
|
@ -1,12 +1,94 @@
|
|||
|
||||
--
|
||||
-- FolderTable
|
||||
--
|
||||
|
||||
CREATE TABLE FolderTable (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
supports_children INTEGER,
|
||||
is_openable INTEGER,
|
||||
parent_id INTEGER
|
||||
parent_id INTEGER REFERENCES FolderTable ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
CREATE INDEX FolderTableNameIndex ON FolderTable (name);
|
||||
CREATE INDEX FolderTableParentIndex ON FolderTable (parent_id);
|
||||
CREATE INDEX FolderTableNameIndex ON FolderTable(name);
|
||||
CREATE INDEX FolderTableParentIndex ON FolderTable(parent_id);
|
||||
|
||||
--
|
||||
-- MessageTable
|
||||
--
|
||||
|
||||
CREATE TABLE MessageTable (
|
||||
id INTEGER PRIMARY KEY,
|
||||
|
||||
date_field TEXT,
|
||||
date_time_t INTEGER,
|
||||
|
||||
from_field TEXT,
|
||||
sender TEXT,
|
||||
reply_to TEXT,
|
||||
|
||||
to_field TEXT,
|
||||
cc TEXT,
|
||||
bcc TEXT,
|
||||
|
||||
message_id TEXT,
|
||||
in_reply_to TEXT,
|
||||
|
||||
subject TEXT,
|
||||
|
||||
header TEXT,
|
||||
|
||||
body TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX MessageTableMessageIDIndex ON MessageTable(message_id);
|
||||
|
||||
--
|
||||
-- MessageLocationTable
|
||||
--
|
||||
|
||||
CREATE TABLE MessageLocationTable (
|
||||
id INTEGER PRIMARY KEY,
|
||||
message_id INTEGER REFERENCES MessageTable ON DELETE CASCADE,
|
||||
folder_id INTEGER REFERENCES FolderTable ON DELETE CASCADE,
|
||||
ordering INTEGER
|
||||
);
|
||||
|
||||
CREATE INDEX MessageLocationTableMessageIDIndex ON MessageLocationTable(message_id);
|
||||
CREATE INDEX MessageLocationTableFolderIDIndex ON MessageLocationTable(folder_id);
|
||||
|
||||
--
|
||||
-- IMAP-specific tables
|
||||
--
|
||||
|
||||
--
|
||||
-- ImapFolderPropertiesTable
|
||||
--
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
CREATE INDEX ImapFolderPropertiesTableFolderIDIndex ON ImapFolderPropertiesTable(folder_id);
|
||||
|
||||
--
|
||||
-- ImapMessagePropertiesTable
|
||||
--
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
CREATE INDEX ImapMessagePropertiesTableMessageIDIndex ON ImapMessagePropertiesTable(message_id);
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ public class MainWindow : Gtk.Window {
|
|||
private async void do_start() {
|
||||
try {
|
||||
// pull down the root-level folders
|
||||
Gee.Collection<Geary.Folder> folders = yield account.list_async(null);
|
||||
Gee.Collection<Geary.Folder> folders = yield account.list_folders_async(null);
|
||||
if (folders != null)
|
||||
on_folders_added_removed(folders, null);
|
||||
else
|
||||
|
|
@ -203,10 +203,11 @@ public class MainWindow : Gtk.Window {
|
|||
|
||||
yield current_folder.open_async(true);
|
||||
|
||||
Gee.List<Geary.EmailHeader>? headers = yield current_folder.read_async(1, 100);
|
||||
if (headers != null && headers.size > 0) {
|
||||
foreach (Geary.EmailHeader header in headers)
|
||||
message_list_store.append_header(header);
|
||||
Gee.List<Geary.Email>? email = yield current_folder.list_email_async(1, 100,
|
||||
Geary.Email.Field.ENVELOPE);
|
||||
if (email != null && email.size > 0) {
|
||||
foreach (Geary.Email envelope in email)
|
||||
message_list_store.append_header(envelope);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -218,25 +219,25 @@ public class MainWindow : Gtk.Window {
|
|||
}
|
||||
}
|
||||
|
||||
private void on_message_selected(Geary.EmailHeader? header) {
|
||||
if (header == null) {
|
||||
private void on_message_selected(Geary.Email? email) {
|
||||
if (email == null) {
|
||||
message_buffer.set_text("");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
do_select_message.begin(header, on_select_message_completed);
|
||||
do_select_message.begin(email, on_select_message_completed);
|
||||
}
|
||||
|
||||
private async void do_select_message(Geary.EmailHeader header) throws Error {
|
||||
private async void do_select_message(Geary.Email email) throws Error {
|
||||
if (current_folder == null) {
|
||||
debug("Message %s selected with no folder selected", header.to_string());
|
||||
debug("Message %s selected with no folder selected", email.to_string());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Geary.Email email = yield current_folder.fetch_async(header);
|
||||
message_buffer.set_text(email.full);
|
||||
Geary.Email text = yield current_folder.fetch_email_async(email.msg_num, Geary.Email.Field.BODY);
|
||||
message_buffer.set_text(text.body.buffer.to_ascii_string());
|
||||
}
|
||||
|
||||
private void on_select_message_completed(Object? source, AsyncResult result) {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public class MessageListStore : Gtk.TreeStore {
|
|||
typeof (string), // DATE
|
||||
typeof (string), // FROM
|
||||
typeof (string), // SUBJECT
|
||||
typeof (Geary.EmailHeader) // MESSAGE_OBJECT
|
||||
typeof (Geary.Email) // MESSAGE_OBJECT
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -54,27 +54,27 @@ public class MessageListStore : Gtk.TreeStore {
|
|||
set_column_types(Column.get_types());
|
||||
}
|
||||
|
||||
public void append_header(Geary.EmailHeader header) {
|
||||
public void append_header(Geary.Email envelope) {
|
||||
Gtk.TreeIter iter;
|
||||
append(out iter, null);
|
||||
|
||||
set(iter,
|
||||
Column.DATE, Date.pretty_print(header.sent.value),
|
||||
Column.FROM, header.from.get_at(0).get_short_address(),
|
||||
Column.SUBJECT, header.subject.value,
|
||||
Column.MESSAGE_OBJECT, header
|
||||
Column.DATE, Date.pretty_print(envelope.date.value),
|
||||
Column.FROM, envelope.from[0].get_short_address(),
|
||||
Column.SUBJECT, envelope.subject.value,
|
||||
Column.MESSAGE_OBJECT, envelope
|
||||
);
|
||||
}
|
||||
|
||||
public Geary.EmailHeader? get_message_at(Gtk.TreePath path) {
|
||||
public Geary.Email? get_message_at(Gtk.TreePath path) {
|
||||
Gtk.TreeIter iter;
|
||||
if (!get_iter(out iter, path))
|
||||
return null;
|
||||
|
||||
Geary.EmailHeader header;
|
||||
get(iter, Column.MESSAGE_OBJECT, out header);
|
||||
Geary.Email email;
|
||||
get(iter, Column.MESSAGE_OBJECT, out email);
|
||||
|
||||
return header;
|
||||
return email;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
public class MessageListView : Gtk.TreeView {
|
||||
public signal void message_selected(Geary.EmailHeader? email);
|
||||
public signal void message_selected(Geary.Email? email);
|
||||
|
||||
public MessageListView(MessageListStore store) {
|
||||
set_model(store);
|
||||
|
|
@ -48,9 +48,9 @@ public class MessageListView : Gtk.TreeView {
|
|||
return;
|
||||
}
|
||||
|
||||
Geary.EmailHeader? header = get_store().get_message_at(path);
|
||||
if (header != null)
|
||||
message_selected(header);
|
||||
Geary.Email? email = get_store().get_message_at(path);
|
||||
if (email != null)
|
||||
message_selected(email);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,124 +4,12 @@
|
|||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
public class Geary.Engine : Object, Geary.Account {
|
||||
private NetworkAccount net;
|
||||
private LocalAccount local;
|
||||
|
||||
private Engine(NetworkAccount net, LocalAccount local) {
|
||||
this.net = net;
|
||||
this.local = local;
|
||||
}
|
||||
|
||||
public static Account open(Geary.Credentials cred) throws Error {
|
||||
return new Engine(
|
||||
public class Geary.Engine {
|
||||
public static Geary.Account open(Geary.Credentials cred) throws Error {
|
||||
// Only ImapEngine today
|
||||
return new ImapEngine(
|
||||
new Geary.Imap.Account(cred, Imap.ClientConnection.DEFAULT_PORT_TLS),
|
||||
new Geary.Sqlite.Account(cred));
|
||||
}
|
||||
|
||||
public async Gee.Collection<Geary.Folder> list_async(string? parent_folder,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
Gee.Collection<Geary.Folder> local_list = yield local.list_async(parent_folder, cancellable);
|
||||
|
||||
Gee.Collection<Geary.Folder> engine_list = new Gee.ArrayList<Geary.Folder>();
|
||||
foreach (Geary.Folder local_folder in local_list)
|
||||
engine_list.add(new EngineFolder(net, local, local_folder));
|
||||
|
||||
background_update_folders.begin(parent_folder, engine_list);
|
||||
|
||||
debug("Reporting %d folders", engine_list.size);
|
||||
|
||||
return engine_list;
|
||||
}
|
||||
|
||||
public async Geary.Folder fetch_async(string? parent_folder, string folder_name,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
Geary.Folder local_folder = yield local.fetch_async(parent_folder, folder_name, cancellable);
|
||||
Geary.Folder engine_folder = new EngineFolder(net, local, local_folder);
|
||||
|
||||
return engine_folder;
|
||||
}
|
||||
|
||||
private Gee.Set<string> get_folder_names(Gee.Collection<Geary.Folder> folders) {
|
||||
Gee.Set<string> names = new Gee.HashSet<string>();
|
||||
foreach (Geary.Folder folder in folders)
|
||||
names.add(folder.get_name());
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
private Gee.List<Geary.Folder> get_excluded_folders(Gee.Collection<Geary.Folder> folders,
|
||||
Gee.Set<string> names) {
|
||||
Gee.List<Geary.Folder> excluded = new Gee.ArrayList<Geary.Folder>();
|
||||
foreach (Geary.Folder folder in folders) {
|
||||
if (!names.contains(folder.get_name()))
|
||||
excluded.add(folder);
|
||||
}
|
||||
|
||||
return excluded;
|
||||
}
|
||||
|
||||
private async void background_update_folders(string? parent_folder,
|
||||
Gee.Collection<Geary.Folder> engine_folders) {
|
||||
Gee.Collection<Geary.Folder> net_folders;
|
||||
try {
|
||||
net_folders = yield net.list_async(parent_folder);
|
||||
} catch (Error neterror) {
|
||||
error("Unable to retrieve folder list from server: %s", neterror.message);
|
||||
}
|
||||
|
||||
Gee.Set<string> local_names = get_folder_names(engine_folders);
|
||||
Gee.Set<string> net_names = get_folder_names(net_folders);
|
||||
|
||||
debug("%d local names, %d net names", local_names.size, net_names.size);
|
||||
|
||||
Gee.List<Geary.Folder>? to_add = get_excluded_folders(net_folders, local_names);
|
||||
Gee.List<Geary.Folder>? to_remove = get_excluded_folders(engine_folders, net_names);
|
||||
|
||||
debug("Adding %d, removing %d to/from local store", to_add.size, to_remove.size);
|
||||
|
||||
if (to_add.size == 0)
|
||||
to_add = null;
|
||||
|
||||
if (to_remove.size == 0)
|
||||
to_remove = null;
|
||||
|
||||
try {
|
||||
if (to_add != null)
|
||||
yield local.create_many_async(to_add);
|
||||
} catch (Error err) {
|
||||
error("Unable to add/remove folders: %s", err.message);
|
||||
}
|
||||
|
||||
Gee.Collection<Geary.Folder> engine_added = null;
|
||||
if (to_add != null) {
|
||||
engine_added = new Gee.ArrayList<Geary.Folder>();
|
||||
foreach (Geary.Folder net_folder in to_add) {
|
||||
try {
|
||||
engine_added.add(new EngineFolder(net, local,
|
||||
yield local.fetch_async(parent_folder, net_folder.get_name())));
|
||||
} catch (Error convert_err) {
|
||||
error("Unable to fetch local folder: %s", convert_err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (engine_added != null)
|
||||
notify_folders_added_removed(engine_added, null);
|
||||
}
|
||||
|
||||
public async void create_async(Geary.Folder folder, Cancellable? cancellable = null) throws Error {
|
||||
}
|
||||
|
||||
public async void create_many_async(Gee.Collection<Geary.Folder> folders,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
}
|
||||
|
||||
public async void remove_async(string folder, Cancellable? cancellable = null) throws Error {
|
||||
}
|
||||
|
||||
public async void remove_many_async(Gee.Set<string> folders, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,25 +26,18 @@ private class Geary.EngineFolder : Object, Geary.Folder {
|
|||
return local_folder.get_name();
|
||||
}
|
||||
|
||||
public Trillian is_readonly() {
|
||||
return local_folder.is_readonly();
|
||||
public Geary.FolderProperties? get_properties() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Trillian does_support_children() {
|
||||
return local_folder.does_support_children();
|
||||
}
|
||||
|
||||
public Trillian has_children() {
|
||||
return local_folder.has_children();
|
||||
}
|
||||
|
||||
public Trillian is_openable() {
|
||||
return local_folder.is_openable();
|
||||
public async void create_email_async(Geary.Email email, Geary.EmailOrdering ordering,
|
||||
Cancellable? cancellable) throws Error {
|
||||
throw new EngineError.READONLY("Engine currently read-only");
|
||||
}
|
||||
|
||||
public async void open_async(bool readonly, Cancellable? cancellable = null) throws Error {
|
||||
if (net_folder == null) {
|
||||
net_folder = yield net.fetch_async(null, local_folder.get_name(), cancellable);
|
||||
net_folder = yield net.fetch_folder_async(null, local_folder.get_name(), cancellable);
|
||||
net_folder.updated.connect(on_net_updated);
|
||||
}
|
||||
|
||||
|
|
@ -64,20 +57,17 @@ private class Geary.EngineFolder : Object, Geary.Folder {
|
|||
return 0;
|
||||
}
|
||||
|
||||
public async Gee.List<Geary.EmailHeader>? read_async(int low, int count,
|
||||
public async Gee.List<Geary.Email> list_email_async(int low, int count, Geary.Email.Field fields,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
if (net_folder == null)
|
||||
throw new EngineError.OPEN_REQUIRED("Folder %s not opened", get_name());
|
||||
|
||||
return yield net_folder.read_async(low, count, cancellable);
|
||||
return yield net_folder.list_email_async(low, count, fields, cancellable);
|
||||
}
|
||||
|
||||
public async Geary.Email fetch_async(Geary.EmailHeader header,
|
||||
public async Geary.Email fetch_email_async(int num, Geary.Email.Field fields,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
if (net_folder == null)
|
||||
throw new EngineError.OPEN_REQUIRED("Folder %s not opened", get_name());
|
||||
|
||||
return yield net_folder.fetch_async(header, cancellable);
|
||||
return yield net_folder.fetch_email_async(num, fields, cancellable);
|
||||
}
|
||||
|
||||
private void on_local_updated() {
|
||||
|
|
|
|||
123
src/engine/ImapEngine.vala
Normal file
123
src/engine/ImapEngine.vala
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/* 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.ImapEngine : Object, Geary.Account {
|
||||
private NetworkAccount net;
|
||||
private LocalAccount local;
|
||||
|
||||
public ImapEngine(NetworkAccount net, LocalAccount local) {
|
||||
this.net = net;
|
||||
this.local = local;
|
||||
}
|
||||
|
||||
public async void create_folder_async(Geary.Folder? parent, Geary.Folder folder,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
}
|
||||
|
||||
public async void create_many_folders_async(Geary.Folder? parent,
|
||||
Gee.Collection<Geary.Folder> folders, Cancellable? cancellable = null) throws Error {
|
||||
}
|
||||
|
||||
public async Gee.Collection<Geary.Folder> list_folders_async(Geary.Folder? parent,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
Gee.Collection<Geary.Folder> local_list = yield local.list_folders_async(parent, cancellable);
|
||||
|
||||
Gee.Collection<Geary.Folder> engine_list = new Gee.ArrayList<Geary.Folder>();
|
||||
foreach (Geary.Folder local_folder in local_list)
|
||||
engine_list.add(new EngineFolder(net, local, local_folder));
|
||||
|
||||
background_update_folders.begin(parent, engine_list);
|
||||
|
||||
debug("Reporting %d folders", engine_list.size);
|
||||
|
||||
return engine_list;
|
||||
}
|
||||
|
||||
public async Geary.Folder fetch_folder_async(Geary.Folder? parent, string folder_name,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
Geary.Folder local_folder = yield local.fetch_folder_async(parent, folder_name, cancellable);
|
||||
Geary.Folder engine_folder = new EngineFolder(net, local, local_folder);
|
||||
|
||||
return engine_folder;
|
||||
}
|
||||
|
||||
private Gee.Set<string> get_folder_names(Gee.Collection<Geary.Folder> folders) {
|
||||
Gee.Set<string> names = new Gee.HashSet<string>();
|
||||
foreach (Geary.Folder folder in folders)
|
||||
names.add(folder.get_name());
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
private Gee.List<Geary.Folder> get_excluded_folders(Gee.Collection<Geary.Folder> folders,
|
||||
Gee.Set<string> names) {
|
||||
Gee.List<Geary.Folder> excluded = new Gee.ArrayList<Geary.Folder>();
|
||||
foreach (Geary.Folder folder in folders) {
|
||||
if (!names.contains(folder.get_name()))
|
||||
excluded.add(folder);
|
||||
}
|
||||
|
||||
return excluded;
|
||||
}
|
||||
|
||||
private async void background_update_folders(Geary.Folder? parent,
|
||||
Gee.Collection<Geary.Folder> engine_folders) {
|
||||
Gee.Collection<Geary.Folder> net_folders;
|
||||
try {
|
||||
net_folders = yield net.list_folders_async(parent);
|
||||
} catch (Error neterror) {
|
||||
error("Unable to retrieve folder list from server: %s", neterror.message);
|
||||
}
|
||||
|
||||
Gee.Set<string> local_names = get_folder_names(engine_folders);
|
||||
Gee.Set<string> net_names = get_folder_names(net_folders);
|
||||
|
||||
debug("%d local names, %d net names", local_names.size, net_names.size);
|
||||
|
||||
Gee.List<Geary.Folder>? to_add = get_excluded_folders(net_folders, local_names);
|
||||
Gee.List<Geary.Folder>? to_remove = get_excluded_folders(engine_folders, net_names);
|
||||
|
||||
debug("Adding %d, removing %d to/from local store", to_add.size, to_remove.size);
|
||||
|
||||
if (to_add.size == 0)
|
||||
to_add = null;
|
||||
|
||||
if (to_remove.size == 0)
|
||||
to_remove = null;
|
||||
|
||||
try {
|
||||
if (to_add != null)
|
||||
yield local.create_many_folders_async(parent, to_add);
|
||||
} catch (Error err) {
|
||||
error("Unable to add/remove folders: %s", err.message);
|
||||
}
|
||||
|
||||
Gee.Collection<Geary.Folder> engine_added = null;
|
||||
if (to_add != null) {
|
||||
engine_added = new Gee.ArrayList<Geary.Folder>();
|
||||
foreach (Geary.Folder net_folder in to_add) {
|
||||
try {
|
||||
engine_added.add(new EngineFolder(net, local,
|
||||
yield local.fetch_folder_async(parent, net_folder.get_name())));
|
||||
} catch (Error convert_err) {
|
||||
error("Unable to fetch local folder: %s", convert_err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (engine_added != null)
|
||||
notify_folders_added_removed(engine_added, null);
|
||||
}
|
||||
|
||||
public async void remove_folder_async(Geary.Folder folder, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
}
|
||||
|
||||
public async void remove_many_folders_async(Gee.Set<Geary.Folder> folders,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -13,29 +13,26 @@ public interface Geary.Account : Object {
|
|||
folders_added_removed(added, removed);
|
||||
}
|
||||
|
||||
public abstract async Gee.Collection<Geary.Folder> list_async(string? parent_folder,
|
||||
public abstract async void create_folder_async(Geary.Folder? parent, Geary.Folder folder,
|
||||
Cancellable? cancellable = null) throws Error;
|
||||
|
||||
public abstract async Geary.Folder fetch_async(string? parent_folder, string folder_name,
|
||||
public abstract async void create_many_folders_async(Geary.Folder? parent,
|
||||
Gee.Collection<Geary.Folder> folders, Cancellable? cancellable = null) throws Error;
|
||||
|
||||
public abstract async Gee.Collection<Geary.Folder> list_folders_async(Geary.Folder? parent,
|
||||
Cancellable? cancellable = null) throws Error;
|
||||
|
||||
public abstract async void create_async(Geary.Folder folder, Cancellable? cancellable = null)
|
||||
throws Error;
|
||||
|
||||
public abstract async void create_many_async(Gee.Collection<Geary.Folder> folders,
|
||||
public abstract async Geary.Folder fetch_folder_async(Geary.Folder? parent, string folder_name,
|
||||
Cancellable? cancellable = null) throws Error;
|
||||
|
||||
public abstract async void remove_async(string folder, Cancellable? cancellable = null)
|
||||
public abstract async void remove_folder_async(Geary.Folder folder, Cancellable? cancellable = null)
|
||||
throws Error;
|
||||
|
||||
public abstract async void remove_many_async(Gee.Set<string> folders, Cancellable? cancellable = null)
|
||||
throws Error;
|
||||
public abstract async void remove_many_folders_async(Gee.Set<Geary.Folder> folders,
|
||||
Cancellable? cancellable = null) throws Error;
|
||||
}
|
||||
|
||||
public interface Geary.NetworkAccount : Object, Geary.Account {
|
||||
public signal void connectivity_changed(bool online);
|
||||
|
||||
public abstract bool is_online();
|
||||
}
|
||||
|
||||
public interface Geary.LocalAccount : Object, Geary.Account {
|
||||
|
|
|
|||
|
|
@ -4,39 +4,92 @@
|
|||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
public class Geary.EmailHeader : Object {
|
||||
public int msg_num { get; private set; }
|
||||
public Geary.RFC822.MailboxAddresses from { get; private set; }
|
||||
public Geary.RFC822.Subject subject { get; private set; }
|
||||
public Geary.RFC822.Date sent { get; private set; }
|
||||
|
||||
public EmailHeader(int msg_num, Geary.RFC822.MailboxAddresses from, Geary.RFC822.Subject subject,
|
||||
Geary.RFC822.Date sent) {
|
||||
this.msg_num = msg_num;
|
||||
this.from = from;
|
||||
this.subject = subject;
|
||||
this.sent = sent;
|
||||
}
|
||||
|
||||
public string to_string() {
|
||||
return "[%d] %s: %s (%s)".printf(msg_num, from.to_string(), subject.to_string(), sent.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Email : Object {
|
||||
public EmailHeader header { get; private set; }
|
||||
public string full { get; private set; }
|
||||
|
||||
public Email(EmailHeader header, string full) {
|
||||
this.header = header;
|
||||
this.full = full;
|
||||
[Flags]
|
||||
public enum Field {
|
||||
NONE = 0,
|
||||
DATE,
|
||||
ORIGINATORS,
|
||||
RECEIVERS,
|
||||
REFERENCES,
|
||||
SUBJECT,
|
||||
HEADER,
|
||||
BODY,
|
||||
PROPERTIES,
|
||||
ENVELOPE = DATE | ORIGINATORS | RECEIVERS | REFERENCES | SUBJECT,
|
||||
ALL = 0xFFFFFFFF;
|
||||
|
||||
public static Field[] all() {
|
||||
return {
|
||||
DATE,
|
||||
ORIGINATORS,
|
||||
RECEIVERS,
|
||||
REFERENCES,
|
||||
SUBJECT,
|
||||
HEADER,
|
||||
BODY,
|
||||
PROPERTIES
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public int msg_num { get; private set; }
|
||||
|
||||
// DATE
|
||||
public Geary.RFC822.Date? date = null;
|
||||
|
||||
// ORIGINATORS
|
||||
public Geary.RFC822.MailboxAddresses? from = null;
|
||||
public Geary.RFC822.MailboxAddresses? sender = null;
|
||||
public Geary.RFC822.MailboxAddresses? reply_to = null;
|
||||
|
||||
// RECEIVERS
|
||||
public Geary.RFC822.MailboxAddresses? to = null;
|
||||
public Geary.RFC822.MailboxAddresses? cc = null;
|
||||
public Geary.RFC822.MailboxAddresses? bcc = null;
|
||||
|
||||
// REFERENCES
|
||||
public Geary.RFC822.MessageID? message_id = null;
|
||||
public Geary.RFC822.MessageID? in_reply_to = null;
|
||||
|
||||
// SUBJECT
|
||||
public Geary.RFC822.Subject? subject = null;
|
||||
|
||||
// HEADER
|
||||
public RFC822.Header? header = null;
|
||||
|
||||
// BODY
|
||||
public RFC822.Text? body = null;
|
||||
|
||||
// PROPERTIES
|
||||
public Geary.EmailProperties? properties = null;
|
||||
|
||||
public Email(int msg_num) {
|
||||
this.msg_num = msg_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* This does not return the full body or any portion of it. It's intended only for debugging.
|
||||
*/
|
||||
public string to_string() {
|
||||
return "%s (%d bytes)".printf(header.to_string(), full.data.length);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (date != null)
|
||||
builder.append_printf("[%s]", date.to_string());
|
||||
|
||||
if (from != null)
|
||||
builder.append_printf("[From: %s]", from.to_string());
|
||||
|
||||
if (to != null)
|
||||
builder.append_printf("[To: %s]", to.to_string());
|
||||
|
||||
if (subject != null)
|
||||
builder.append_printf("[Subj: %s]", subject.to_string());
|
||||
|
||||
if (header != null)
|
||||
builder.append_printf("[Header: %lub]", header.buffer.get_size());
|
||||
|
||||
if (body != null)
|
||||
builder.append_printf("[Body: %lub]", body.buffer.get_size());
|
||||
|
||||
return builder.str;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
14
src/engine/api/EmailOrdering.vala
Normal file
14
src/engine/api/EmailOrdering.vala
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* 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.EmailOrdering {
|
||||
public int64 ordinal { get; private set; }
|
||||
|
||||
public EmailOrdering(int64 ordinal) {
|
||||
this.ordinal = ordinal;
|
||||
}
|
||||
}
|
||||
|
||||
9
src/engine/api/EmailProperties.vala
Normal file
9
src/engine/api/EmailProperties.vala
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/* 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 abstract class Geary.EmailProperties : Object {
|
||||
}
|
||||
|
||||
|
|
@ -7,6 +7,8 @@
|
|||
public errordomain Geary.EngineError {
|
||||
OPEN_REQUIRED,
|
||||
ALREADY_OPEN,
|
||||
NOT_FOUND
|
||||
NOT_FOUND,
|
||||
READONLY,
|
||||
BAD_PARAMETERS
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,16 +17,21 @@ public interface Geary.Folder : Object {
|
|||
|
||||
public signal void updated();
|
||||
|
||||
public virtual void notify_opened() {
|
||||
opened();
|
||||
}
|
||||
|
||||
public virtual void notify_closed(CloseReason reason) {
|
||||
closed(reason);
|
||||
}
|
||||
|
||||
public virtual void notify_updated() {
|
||||
updated();
|
||||
}
|
||||
|
||||
public abstract string get_name();
|
||||
|
||||
// This is only for when a context has been selected
|
||||
public abstract Trillian is_readonly();
|
||||
|
||||
public abstract Trillian does_support_children();
|
||||
|
||||
public abstract Trillian has_children();
|
||||
|
||||
public abstract Trillian is_openable();
|
||||
public abstract Geary.FolderProperties? get_properties();
|
||||
|
||||
public abstract async void open_async(bool readonly, Cancellable? cancellable = null) throws Error;
|
||||
|
||||
|
|
@ -34,10 +39,16 @@ public interface Geary.Folder : Object {
|
|||
|
||||
public abstract int get_message_count() throws Error;
|
||||
|
||||
public abstract async Gee.List<Geary.EmailHeader>? read_async(int low, int count,
|
||||
public abstract async void create_email_async(Geary.Email email, Geary.EmailOrdering ordering,
|
||||
Cancellable? cancellable = null) throws Error;
|
||||
|
||||
public abstract async Geary.Email fetch_async(Geary.EmailHeader header,
|
||||
/**
|
||||
* low is one-based.
|
||||
*/
|
||||
public abstract async Gee.List<Geary.Email> list_email_async(int low, int count,
|
||||
Geary.Email.Field fields, Cancellable? cancellable = null) throws Error;
|
||||
|
||||
public abstract async Geary.Email fetch_email_async(int msg_num, Geary.Email.Field fields,
|
||||
Cancellable? cancellable = null) throws Error;
|
||||
}
|
||||
|
||||
|
|
|
|||
9
src/engine/api/FolderProperties.vala
Normal file
9
src/engine/api/FolderProperties.vala
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/* 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 abstract class Geary.FolderProperties : Object {
|
||||
}
|
||||
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
/* 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.Imap.EmailHeader : Geary.EmailHeader {
|
||||
public EmailHeader(int msg_num, Envelope envelope) {
|
||||
base (msg_num, envelope.from, envelope.subject, envelope.sent);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.Email : Geary.Email {
|
||||
public Email(EmailHeader header, string full) {
|
||||
base (header, full);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -8,6 +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; }
|
||||
|
||||
private SelectedContext context;
|
||||
|
||||
|
|
@ -26,42 +27,44 @@ public class Geary.Imap.Mailbox : Geary.SmartReference {
|
|||
name = context.name;
|
||||
count = context.exists;
|
||||
is_readonly = context.is_readonly;
|
||||
uid_validity = context.uid_validity;
|
||||
}
|
||||
|
||||
public async Gee.List<EmailHeader>? read(int low, int count, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
public async Gee.List<Geary.Email>? list_async(int low, int count, Geary.Email.Field fields,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
if (context.is_closed())
|
||||
throw new ImapError.NOT_SELECTED("Mailbox %s closed", name);
|
||||
|
||||
if (fields == Geary.Email.Field.NONE)
|
||||
throw new EngineError.BAD_PARAMETERS("No email fields specify for list");
|
||||
|
||||
CommandResponse resp = yield context.session.send_command_async(
|
||||
new FetchCommand(context.session.generate_tag(), new MessageSet.range(low, count),
|
||||
{ FetchDataType.ENVELOPE }), cancellable);
|
||||
fields_to_fetch_data_types(fields)), cancellable);
|
||||
|
||||
if (resp.status_response.status != Status.OK)
|
||||
throw new ImapError.SERVER_ERROR("Server error: %s", resp.to_string());
|
||||
|
||||
Gee.List<EmailHeader> msgs = new Gee.ArrayList<EmailHeader>();
|
||||
Gee.List<Geary.Email> msgs = new Gee.ArrayList<Geary.Email>();
|
||||
|
||||
FetchResults[] results = FetchResults.decode(resp);
|
||||
foreach (FetchResults res in results) {
|
||||
Envelope envelope = (Envelope) res.get_data(FetchDataType.ENVELOPE);
|
||||
msgs.add(new EmailHeader(res.msg_num, envelope));
|
||||
Geary.Email email = new Geary.Email(res.msg_num);
|
||||
fetch_results_to_email(res, fields, email);
|
||||
msgs.add(email);
|
||||
}
|
||||
|
||||
return msgs;
|
||||
}
|
||||
|
||||
public async Geary.Email fetch(Geary.EmailHeader hdr, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
Geary.Imap.EmailHeader? header = hdr as Geary.Imap.EmailHeader;
|
||||
assert(header != null);
|
||||
|
||||
public async Geary.Email fetch_async(int msg_num, Geary.Email.Field fields,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
if (context.is_closed())
|
||||
throw new ImapError.NOT_SELECTED("Mailbox %s closed", name);
|
||||
|
||||
CommandResponse resp = yield context.session.send_command_async(
|
||||
new FetchCommand(context.session.generate_tag(), new MessageSet(hdr.msg_num),
|
||||
{ FetchDataType.RFC822_TEXT }), cancellable);
|
||||
new FetchCommand(context.session.generate_tag(), new MessageSet(msg_num),
|
||||
fields_to_fetch_data_types(fields)), cancellable);
|
||||
|
||||
if (resp.status_response.status != Status.OK)
|
||||
throw new ImapError.SERVER_ERROR("Server error: %s", resp.to_string());
|
||||
|
|
@ -70,9 +73,15 @@ public class Geary.Imap.Mailbox : Geary.SmartReference {
|
|||
if (results.length != 1)
|
||||
throw new ImapError.SERVER_ERROR("Too many responses from server: %d", results.length);
|
||||
|
||||
Geary.RFC822.Text text = (Geary.RFC822.Text) results[0].get_data(FetchDataType.RFC822_TEXT);
|
||||
if (results[0].msg_num != msg_num) {
|
||||
throw new ImapError.SERVER_ERROR("Server returns message #%d, requested %d",
|
||||
results[0].msg_num, msg_num);
|
||||
}
|
||||
|
||||
return new Email(header, text.buffer.to_ascii_string());
|
||||
Geary.Email email = new Geary.Email(msg_num);
|
||||
fetch_results_to_email(results[0], fields, email);
|
||||
|
||||
return email;
|
||||
}
|
||||
|
||||
private void on_exists_changed(int exists) {
|
||||
|
|
@ -86,6 +95,102 @@ public class Geary.Imap.Mailbox : Geary.SmartReference {
|
|||
private void on_disconnected(bool local) {
|
||||
disconnected(local);
|
||||
}
|
||||
|
||||
private static FetchDataType[] fields_to_fetch_data_types(Geary.Email.Field fields) {
|
||||
Gee.HashSet<FetchDataType> data_type_set = new Gee.HashSet<FetchDataType>();
|
||||
foreach (Geary.Email.Field field in Geary.Email.Field.all()) {
|
||||
switch (fields & field) {
|
||||
case Geary.Email.Field.DATE:
|
||||
case Geary.Email.Field.ORIGINATORS:
|
||||
case Geary.Email.Field.RECEIVERS:
|
||||
case Geary.Email.Field.REFERENCES:
|
||||
case Geary.Email.Field.SUBJECT:
|
||||
data_type_set.add(FetchDataType.ENVELOPE);
|
||||
break;
|
||||
|
||||
case Geary.Email.Field.HEADER:
|
||||
data_type_set.add(FetchDataType.RFC822_HEADER);
|
||||
break;
|
||||
|
||||
case Geary.Email.Field.BODY:
|
||||
data_type_set.add(FetchDataType.RFC822_TEXT);
|
||||
break;
|
||||
|
||||
case Geary.Email.Field.PROPERTIES:
|
||||
data_type_set.add(FetchDataType.FLAGS);
|
||||
break;
|
||||
|
||||
case Geary.Email.Field.NONE:
|
||||
// not set
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
assert(data_type_set.size > 0);
|
||||
FetchDataType[] data_types = new FetchDataType[data_type_set.size];
|
||||
int ctr = 0;
|
||||
foreach (FetchDataType data_type in data_type_set)
|
||||
data_types[ctr++] = data_type;
|
||||
|
||||
return data_types;
|
||||
}
|
||||
|
||||
private static void fetch_results_to_email(FetchResults res, Geary.Email.Field fields,
|
||||
Geary.Email email) {
|
||||
foreach (FetchDataType data_type in res.get_all_types()) {
|
||||
MessageData? data = res.get_data(data_type);
|
||||
if (data == null)
|
||||
continue;
|
||||
|
||||
switch (data_type) {
|
||||
case FetchDataType.ENVELOPE:
|
||||
Envelope envelope = (Envelope) data;
|
||||
|
||||
if ((fields & Geary.Email.Field.DATE) != 0)
|
||||
email.date = envelope.sent;
|
||||
|
||||
if ((fields & Geary.Email.Field.SUBJECT) != 0)
|
||||
email.subject = envelope.subject;
|
||||
|
||||
if ((fields & Geary.Email.Field.ORIGINATORS) != 0) {
|
||||
email.from = envelope.from;
|
||||
email.sender = envelope.sender;
|
||||
email.reply_to = envelope.reply_to;
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.RECEIVERS) != 0) {
|
||||
email.to = envelope.to;
|
||||
email.cc = envelope.cc;
|
||||
email.bcc = envelope.bcc;
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.REFERENCES) != 0) {
|
||||
email.in_reply_to = envelope.in_reply_to;
|
||||
email.message_id = envelope.message_id;
|
||||
}
|
||||
break;
|
||||
|
||||
case FetchDataType.RFC822_HEADER:
|
||||
email.header = (RFC822.Header) data;
|
||||
break;
|
||||
|
||||
case FetchDataType.RFC822_TEXT:
|
||||
email.body = (RFC822.Text) data;
|
||||
break;
|
||||
|
||||
case FetchDataType.FLAGS:
|
||||
email.properties = new Imap.EmailProperties((MessageFlags) data);
|
||||
break;
|
||||
|
||||
default:
|
||||
// everything else dropped on the floor (not applicable to Geary.Email)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class Geary.Imap.SelectedContext : Object, Geary.ReferenceSemantics {
|
||||
|
|
@ -97,6 +202,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 signal void exists_changed(int exists);
|
||||
|
||||
|
|
@ -113,6 +219,7 @@ internal class Geary.Imap.SelectedContext : Object, Geary.ReferenceSemantics {
|
|||
is_readonly = results.readonly;
|
||||
exists = results.exists;
|
||||
recent = results.recent;
|
||||
uid_validity = results.uid_validity;
|
||||
|
||||
session.current_mailbox_changed.connect(on_session_mailbox_changed);
|
||||
session.unsolicited_exists.connect(on_unsolicited_exists);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@
|
|||
*
|
||||
* Note that IMAP specifies that Flags and Attributes are *always* returned as a list, even if only
|
||||
* one is present, which is why these elements are MessageData but not the elements within the
|
||||
* lists (Flag, Attribute). Obviously these classes are closely related, hence their presence
|
||||
* here.
|
||||
* lists (Flag, Attribute).
|
||||
*
|
||||
* Also note that Imap.MessageData inherits from Common.MessageData.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -11,13 +11,20 @@ public class Geary.Imap.Account : Object, Geary.Account, Geary.NetworkAccount {
|
|||
session_mgr = new ClientSessionManager(cred, default_port);
|
||||
}
|
||||
|
||||
public bool is_online() {
|
||||
return true;
|
||||
public async void create_folder_async(Geary.Folder? parent, Geary.Folder folder,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
throw new EngineError.READONLY("IMAP readonly");
|
||||
}
|
||||
|
||||
public async Gee.Collection<Geary.Folder> list_async(string? parent_folder,
|
||||
public async void create_many_folders_async(Geary.Folder? parent, Gee.Collection<Geary.Folder> folders,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
Gee.Collection<MailboxInformation> mboxes = yield session_mgr.list(parent_folder, cancellable);
|
||||
throw new EngineError.READONLY("IMAP readonly");
|
||||
}
|
||||
|
||||
public async Gee.Collection<Geary.Folder> list_folders_async(Geary.Folder? parent,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
Gee.Collection<MailboxInformation> mboxes = yield session_mgr.list(
|
||||
(parent != null) ? parent.get_name() : null, cancellable);
|
||||
|
||||
Gee.Collection<Geary.Folder> folders = new Gee.ArrayList<Geary.Folder>();
|
||||
foreach (MailboxInformation mbox in mboxes)
|
||||
|
|
@ -26,32 +33,24 @@ public class Geary.Imap.Account : Object, Geary.Account, Geary.NetworkAccount {
|
|||
return folders;
|
||||
}
|
||||
|
||||
public async Geary.Folder fetch_async(string? parent_folder, string folder_name,
|
||||
public async Geary.Folder fetch_folder_async(Geary.Folder? parent, string folder_name,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
MailboxInformation? mbox = yield session_mgr.fetch_async(parent_folder, folder_name,
|
||||
cancellable);
|
||||
MailboxInformation? mbox = yield session_mgr.fetch_async(
|
||||
(parent != null) ? parent.get_name() : null, folder_name, cancellable);
|
||||
if (mbox == null)
|
||||
throw new EngineError.NOT_FOUND("Folder %s not found on server", folder_name);
|
||||
|
||||
return new Geary.Imap.Folder(session_mgr, mbox);
|
||||
}
|
||||
|
||||
public async void create_async(Geary.Folder folder, Cancellable? cancellable = null) throws Error {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public async void create_many_async(Gee.Collection<Geary.Folder> folders,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public async void remove_async(string folder, Cancellable? cancellable = null) throws Error {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public async void remove_many_async(Gee.Set<string> folders, Cancellable? cancellable = null)
|
||||
public async void remove_folder_async(Geary.Folder folder, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
// TODO
|
||||
throw new EngineError.READONLY("IMAP readonly");
|
||||
}
|
||||
|
||||
public async void remove_many_folders_async(Gee.Set<Geary.Folder> folders,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
throw new EngineError.READONLY("IMAP readonly");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
14
src/engine/imap/api/EmailProperties.vala
Normal file
14
src/engine/imap/api/EmailProperties.vala
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* 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.Imap.EmailProperties : Geary.EmailProperties {
|
||||
public MessageFlags flags { get; private set; }
|
||||
|
||||
public EmailProperties(MessageFlags flags) {
|
||||
this.flags = flags;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -9,9 +9,7 @@ public class Geary.Imap.Folder : Object, Geary.Folder {
|
|||
private MailboxInformation info;
|
||||
private string name;
|
||||
private Trillian readonly;
|
||||
private Trillian supports_children;
|
||||
private Trillian children;
|
||||
private Trillian openable;
|
||||
private Imap.FolderProperties properties;
|
||||
private Mailbox? mailbox = null;
|
||||
|
||||
internal Folder(ClientSessionManager session_mgr, MailboxInformation info) {
|
||||
|
|
@ -20,11 +18,7 @@ public class Geary.Imap.Folder : Object, Geary.Folder {
|
|||
|
||||
name = info.name;
|
||||
readonly = Trillian.UNKNOWN;
|
||||
supports_children = Trillian.from_boolean(!info.attrs.contains(MailboxAttribute.NO_INFERIORS));
|
||||
// \HasNoChildren is an optional attribute and lack of presence doesn't indiciate anything
|
||||
children = info.attrs.contains(MailboxAttribute.HAS_NO_CHILDREN) ? Trillian.TRUE
|
||||
: Trillian.UNKNOWN;
|
||||
openable = Trillian.from_boolean(!info.attrs.contains(MailboxAttribute.NO_SELECT));
|
||||
properties = new Imap.FolderProperties(null, info.attrs);
|
||||
}
|
||||
|
||||
public string get_name() {
|
||||
|
|
@ -35,16 +29,8 @@ public class Geary.Imap.Folder : Object, Geary.Folder {
|
|||
return readonly;
|
||||
}
|
||||
|
||||
public Trillian does_support_children() {
|
||||
return supports_children;
|
||||
}
|
||||
|
||||
public Trillian has_children() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public Trillian is_openable() {
|
||||
return openable;
|
||||
public Geary.FolderProperties? get_properties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public async void open_async(bool readonly, Cancellable? cancellable = null) throws Error {
|
||||
|
|
@ -55,11 +41,13 @@ public class Geary.Imap.Folder : Object, Geary.Folder {
|
|||
// hook up signals
|
||||
|
||||
this.readonly = Trillian.from_boolean(readonly);
|
||||
properties.uid_validity = mailbox.uid_validity;
|
||||
}
|
||||
|
||||
public async void close_async(Cancellable? cancellable = null) throws Error {
|
||||
mailbox = null;
|
||||
readonly = Trillian.UNKNOWN;
|
||||
properties.uid_validity = null;
|
||||
}
|
||||
|
||||
public int get_message_count() throws Error {
|
||||
|
|
@ -69,20 +57,25 @@ public class Geary.Imap.Folder : Object, Geary.Folder {
|
|||
return mailbox.count;
|
||||
}
|
||||
|
||||
public async Gee.List<Geary.EmailHeader>? read_async(int low, int count,
|
||||
public async void create_email_async(Geary.Email email, Geary.EmailOrdering ordring,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
if (mailbox == null)
|
||||
throw new EngineError.OPEN_REQUIRED("%s not opened", to_string());
|
||||
|
||||
return yield mailbox.read(low, count, cancellable);
|
||||
throw new EngineError.READONLY("IMAP currently read-only");
|
||||
}
|
||||
|
||||
public async Geary.Email fetch_async(Geary.EmailHeader header,
|
||||
public async Gee.List<Geary.Email> list_email_async(int low, int count, Geary.Email.Field fields,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
if (mailbox == null)
|
||||
throw new EngineError.OPEN_REQUIRED("%s not opened", to_string());
|
||||
|
||||
return yield mailbox.fetch(header, cancellable);
|
||||
return yield mailbox.list_async(low, count, fields, cancellable);
|
||||
}
|
||||
|
||||
public async Geary.Email fetch_email_async(int msg_num, Geary.Email.Field fields,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
if (mailbox == null)
|
||||
throw new EngineError.OPEN_REQUIRED("%s not opened", to_string());
|
||||
|
||||
return yield mailbox.fetch_async(msg_num, fields, cancellable);
|
||||
}
|
||||
|
||||
public string to_string() {
|
||||
|
|
|
|||
25
src/engine/imap/api/FolderProperties.vala
Normal file
25
src/engine/imap/api/FolderProperties.vala
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* Copyright 2011 Yorba Foundation
|
||||
*
|
||||
* This software is licensed under the GNU Lesser General Public License
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
public class Geary.Imap.FolderProperties : Geary.FolderProperties {
|
||||
public UID? 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) {
|
||||
this.uid_validity = uid_validity;
|
||||
this.attrs = attrs;
|
||||
|
||||
supports_children = Trillian.from_boolean(!attrs.contains(MailboxAttribute.NO_INFERIORS));
|
||||
// \HasNoChildren is an optional attribute and lack of presence doesn't indiciate anything
|
||||
supports_children = attrs.contains(MailboxAttribute.HAS_NO_CHILDREN) ? Trillian.TRUE
|
||||
: Trillian.UNKNOWN;
|
||||
is_openable = Trillian.from_boolean(!attrs.contains(MailboxAttribute.NO_SELECT));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -73,6 +73,10 @@ public class Geary.Imap.FetchResults : Geary.Imap.CommandResults {
|
|||
return array;
|
||||
}
|
||||
|
||||
public Gee.Set<FetchDataType> get_all_types() {
|
||||
return map.keys;
|
||||
}
|
||||
|
||||
public void set_data(FetchDataType data_item, MessageData primitive) {
|
||||
map.set(data_item, primitive);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public class Geary.Imap.SelectExamineResults : Geary.Imap.CommandResults {
|
|||
* -1 if not specified.
|
||||
*/
|
||||
public int unseen { get; private set; }
|
||||
public UID? uidvalidity { get; private set; }
|
||||
public UID? uid_validity { get; private set; }
|
||||
public Flags? flags { get; private set; }
|
||||
public Flags? permanentflags { get; private set; }
|
||||
public bool readonly { get; private set; }
|
||||
|
|
@ -29,7 +29,7 @@ public class Geary.Imap.SelectExamineResults : Geary.Imap.CommandResults {
|
|||
this.exists = exists;
|
||||
this.recent = recent;
|
||||
this.unseen = unseen;
|
||||
this.uidvalidity = uidvalidity;
|
||||
this.uid_validity = uid_validity;
|
||||
this.flags = flags;
|
||||
this.permanentflags = permanentflags;
|
||||
this.readonly = readonly;
|
||||
|
|
|
|||
|
|
@ -22,13 +22,14 @@ public class Geary.RFC822.MessageID : Geary.Common.StringMessageData, Geary.RFC8
|
|||
public class Geary.RFC822.Date : Geary.RFC822.MessageData, Geary.Common.MessageData {
|
||||
public string original { get; private set; }
|
||||
public DateTime value { get; private set; }
|
||||
public time_t as_time_t { get; private set; }
|
||||
|
||||
public Date(string iso8601) throws ImapError {
|
||||
time_t tm = GMime.utils_header_decode_date(iso8601, null);
|
||||
if (tm == 0)
|
||||
as_time_t = GMime.utils_header_decode_date(iso8601, null);
|
||||
if (as_time_t == 0)
|
||||
throw new ImapError.PARSE_ERROR("Unable to parse \"%s\": not ISO-8601 date", iso8601);
|
||||
|
||||
value = new DateTime.from_unix_local(tm);
|
||||
value = new DateTime.from_unix_local(as_time_t);
|
||||
original = iso8601;
|
||||
}
|
||||
|
||||
|
|
@ -50,18 +51,20 @@ public class Geary.RFC822.Subject : Geary.Common.StringMessageData, Geary.RFC822
|
|||
}
|
||||
|
||||
public class Geary.RFC822.MailboxAddresses : Geary.Common.MessageData, Geary.RFC822.MessageData {
|
||||
public int size { get { return addrs.size; } }
|
||||
|
||||
private Gee.List<MailboxAddress> addrs = new Gee.ArrayList<MailboxAddress>();
|
||||
|
||||
public MailboxAddresses(Gee.Collection<MailboxAddress> addrs) {
|
||||
this.addrs.add_all(addrs);
|
||||
}
|
||||
|
||||
public int get_count() {
|
||||
return addrs.size;
|
||||
public MailboxAddress? get(int index) {
|
||||
return addrs.get(index);
|
||||
}
|
||||
|
||||
public MailboxAddress? get_at(int index) {
|
||||
return addrs.get(index);
|
||||
public Gee.Iterator<MailboxAddress> iterator() {
|
||||
return addrs.iterator();
|
||||
}
|
||||
|
||||
public Gee.List<MailboxAddress> get_all() {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public abstract class Geary.Sqlite.Database {
|
|||
db_file.get_parent().make_directory_with_parents();
|
||||
|
||||
db = new SQLHeavy.VersionedDatabase(db_file.get_path(), schema_dir.get_path());
|
||||
db.foreign_keys = true;
|
||||
}
|
||||
|
||||
protected Geary.Sqlite.Table? get_table(string name, out SQLHeavy.Table heavy_table) {
|
||||
|
|
@ -27,8 +28,10 @@ public abstract class Geary.Sqlite.Database {
|
|||
return table_map.get(heavy_table);
|
||||
}
|
||||
|
||||
protected void add_table(Geary.Sqlite.Table table) {
|
||||
protected Geary.Sqlite.Table add_table(Geary.Sqlite.Table table) {
|
||||
table_map.set(table.table, table);
|
||||
|
||||
return table;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,27 +7,22 @@
|
|||
public class Geary.Sqlite.FolderRow : Geary.Sqlite.Row {
|
||||
public int64 id { get; private set; }
|
||||
public string name { get; private set; }
|
||||
public Trillian supports_children { get; private set; }
|
||||
public Trillian is_openable { get; private set; }
|
||||
public int64 parent_id { get; private set; }
|
||||
|
||||
public FolderRow(string name, Trillian supports_children, Trillian is_openable,
|
||||
int64 parent_id = INVALID_ID) {
|
||||
this.id = -1;
|
||||
public FolderRow(FolderTable table, string name, int64 parent_id) {
|
||||
base (table);
|
||||
|
||||
this.id = INVALID_ID;
|
||||
this.name = name;
|
||||
this.supports_children = supports_children;
|
||||
this.is_openable = is_openable;
|
||||
this.parent_id = parent_id;
|
||||
}
|
||||
|
||||
public FolderRow.from_query_result(SQLHeavy.QueryResult result) throws Error {
|
||||
id = fetch_int64_for(result, FolderTable.Column.ID.colname());
|
||||
name = fetch_string_for(result, FolderTable.Column.NAME.colname());
|
||||
supports_children = Trillian.from_int(fetch_int_for(result,
|
||||
FolderTable.Column.SUPPORTS_CHILDREN.colname()));
|
||||
is_openable = Trillian.from_int(fetch_int_for(result,
|
||||
FolderTable.Column.IS_OPENABLE.colname()));
|
||||
parent_id = fetch_int64_for(result, FolderTable.Column.PARENT_ID.colname());
|
||||
public FolderRow.from_query_result(FolderTable table, SQLHeavy.QueryResult result) throws Error {
|
||||
base (table);
|
||||
|
||||
id = fetch_int64_for(result, FolderTable.Column.ID);
|
||||
name = fetch_string_for(result, FolderTable.Column.NAME);
|
||||
parent_id = fetch_int64_for(result, FolderTable.Column.PARENT_ID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,69 +9,17 @@ public class Geary.Sqlite.FolderTable : Geary.Sqlite.Table {
|
|||
public enum Column {
|
||||
ID,
|
||||
NAME,
|
||||
SUPPORTS_CHILDREN,
|
||||
IS_OPENABLE,
|
||||
PARENT_ID;
|
||||
|
||||
public string colname() {
|
||||
switch (this) {
|
||||
case ID:
|
||||
return "id";
|
||||
|
||||
case NAME:
|
||||
return "name";
|
||||
|
||||
case SUPPORTS_CHILDREN:
|
||||
return "supports_children";
|
||||
|
||||
case IS_OPENABLE:
|
||||
return "is_openable";
|
||||
|
||||
case PARENT_ID:
|
||||
return "parent_id";
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
PARENT_ID
|
||||
}
|
||||
|
||||
internal FolderTable(Geary.Sqlite.Database gdb, SQLHeavy.Table table) {
|
||||
base (gdb, table);
|
||||
}
|
||||
|
||||
public async Gee.List<FolderRow> list_async(int64 parent_id, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
SQLHeavy.Query query = db.prepare("SELECT * FROM FolderTable WHERE parent_id=?");
|
||||
query.bind_int64(0, parent_id);
|
||||
|
||||
SQLHeavy.QueryResult result = yield query.execute_async(cancellable);
|
||||
|
||||
Gee.List<FolderRow> rows = new Gee.ArrayList<FolderRow>();
|
||||
while (!result.finished) {
|
||||
rows.add(new FolderRow.from_query_result(result));
|
||||
|
||||
yield result.next_async(cancellable);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
public async FolderRow? fetch_async(int64 parent_id, string name, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
SQLHeavy.Query query = db.prepare("SELECT * FROM FolderTable WHERE parent_id=? AND name=?");
|
||||
query.bind_int64(0, parent_id);
|
||||
query.bind_string(1, name);
|
||||
|
||||
SQLHeavy.QueryResult result = yield query.execute_async(cancellable);
|
||||
|
||||
return (!result.finished) ? new FolderRow.from_query_result(result) : null;
|
||||
}
|
||||
|
||||
private SQLHeavy.Query create_query(SQLHeavy.Queryable? queryable = null) throws SQLHeavy.Error {
|
||||
SQLHeavy.Queryable q = queryable ?? db;
|
||||
SQLHeavy.Query query = q.prepare(
|
||||
"INSERT INTO FolderTable (name, supports_children, is_openable, parent_id) VALUES (?, ?, ?, ?)");
|
||||
"INSERT INTO FolderTable (name, parent_id) VALUES (?, ?)");
|
||||
|
||||
return query;
|
||||
}
|
||||
|
|
@ -79,9 +27,10 @@ public class Geary.Sqlite.FolderTable : Geary.Sqlite.Table {
|
|||
private void create_binding(SQLHeavy.Query query, FolderRow row) throws SQLHeavy.Error {
|
||||
query.clear();
|
||||
query.bind_string(0, row.name);
|
||||
query.bind_int(1, row.supports_children.to_int());
|
||||
query.bind_int(2, row.is_openable.to_int());
|
||||
query.bind_int64(3, row.parent_id);
|
||||
if (row.parent_id != Row.INVALID_ID)
|
||||
query.bind_int64(1, row.parent_id);
|
||||
else
|
||||
query.bind_null(1);
|
||||
}
|
||||
|
||||
public async void create_async(FolderRow row, Cancellable? cancellable = null) throws Error {
|
||||
|
|
@ -93,16 +42,50 @@ public class Geary.Sqlite.FolderTable : Geary.Sqlite.Table {
|
|||
|
||||
public async void create_many_async(Gee.Collection<FolderRow> rows, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
SQLHeavy.Transaction transaction = db.begin_transaction();
|
||||
|
||||
SQLHeavy.Query query = create_query(transaction);
|
||||
SQLHeavy.Query query = create_query();
|
||||
foreach (FolderRow row in rows) {
|
||||
create_binding(query, row);
|
||||
query.execute_insert();
|
||||
}
|
||||
}
|
||||
|
||||
public async Gee.List<FolderRow> list_async(int64 parent_id, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
SQLHeavy.Query query;
|
||||
if (parent_id != Row.INVALID_ID) {
|
||||
query = db.prepare("SELECT * FROM FolderTable WHERE parent_id=?");
|
||||
query.bind_int64(0, parent_id);
|
||||
} else {
|
||||
query = db.prepare("SELECT * FROM FolderTable WHERE parent_id IS NULL");
|
||||
}
|
||||
|
||||
// TODO: Need an async transaction commit
|
||||
transaction.commit();
|
||||
SQLHeavy.QueryResult result = yield query.execute_async(cancellable);
|
||||
|
||||
Gee.List<FolderRow> rows = new Gee.ArrayList<FolderRow>();
|
||||
while (!result.finished) {
|
||||
rows.add(new FolderRow.from_query_result(this, result));
|
||||
|
||||
yield result.next_async(cancellable);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
public async FolderRow? fetch_async(int64 parent_id, string name, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
SQLHeavy.Query query;
|
||||
if (parent_id != Row.INVALID_ID) {
|
||||
query = db.prepare("SELECT * FROM FolderTable WHERE parent_id=? AND name=?");
|
||||
query.bind_int64(0, parent_id);
|
||||
query.bind_string(1, name);
|
||||
} else {
|
||||
query = db.prepare("SELECT * FROM FolderTable WHERE name=? AND parent_id IS NULL");
|
||||
query.bind_string(0, name);
|
||||
}
|
||||
|
||||
SQLHeavy.QueryResult result = yield query.execute_async(cancellable);
|
||||
|
||||
return (!result.finished) ? new FolderRow.from_query_result(this, result) : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,13 +15,29 @@ public class Geary.Sqlite.MailDatabase : Geary.Sqlite.Database {
|
|||
public Geary.Sqlite.FolderTable get_folder_table() {
|
||||
SQLHeavy.Table heavy_table;
|
||||
FolderTable? folder_table = get_table("FolderTable", out heavy_table) as FolderTable;
|
||||
if (folder_table != null)
|
||||
return folder_table;
|
||||
|
||||
folder_table = new FolderTable(this, heavy_table);
|
||||
add_table(folder_table);
|
||||
return (folder_table != null)
|
||||
? folder_table
|
||||
: (FolderTable) add_table(new FolderTable(this, heavy_table));
|
||||
}
|
||||
|
||||
public Geary.Sqlite.MessageTable get_message_table() {
|
||||
SQLHeavy.Table heavy_table;
|
||||
MessageTable? message_table = get_table("MessageTable", out heavy_table) as MessageTable;
|
||||
|
||||
return folder_table;
|
||||
return (message_table != null)
|
||||
? message_table
|
||||
: (MessageTable) add_table(new MessageTable(this, heavy_table));
|
||||
}
|
||||
|
||||
public Geary.Sqlite.MessageLocationTable get_message_location_table() {
|
||||
SQLHeavy.Table heavy_table;
|
||||
MessageLocationTable? location_table = get_table("MessageLocationTable", out heavy_table)
|
||||
as MessageLocationTable;
|
||||
|
||||
return (location_table != null)
|
||||
? location_table
|
||||
: (MessageLocationTable) add_table(new MessageLocationTable(this, heavy_table));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
33
src/engine/sqlite/MessageLocationRow.vala
Normal file
33
src/engine/sqlite/MessageLocationRow.vala
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/* 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.MessageLocationRow : Geary.Sqlite.Row {
|
||||
public int64 id { get; private set; }
|
||||
public int64 message_id { get; private set; }
|
||||
public int64 folder_id { get; private set; }
|
||||
public int64 ordering { get; private set; }
|
||||
|
||||
public MessageLocationRow(MessageLocationTable table, int64 id, int64 message_id, int64 folder_id,
|
||||
int64 ordering) {
|
||||
base (table);
|
||||
|
||||
this.id = id;
|
||||
this.message_id = message_id;
|
||||
this.folder_id = folder_id;
|
||||
this.ordering = ordering;
|
||||
}
|
||||
|
||||
public MessageLocationRow.from_query_result(MessageLocationTable table,
|
||||
SQLHeavy.QueryResult result) throws Error {
|
||||
base (table);
|
||||
|
||||
id = fetch_int64_for(result, MessageLocationTable.Column.ID);
|
||||
message_id = fetch_int64_for(result, MessageLocationTable.Column.MESSAGE_ID);
|
||||
folder_id = fetch_int64_for(result, MessageLocationTable.Column.FOLDER_ID);
|
||||
ordering = fetch_int64_for(result, MessageLocationTable.Column.ORDERING);
|
||||
}
|
||||
}
|
||||
|
||||
80
src/engine/sqlite/MessageLocationTable.vala
Normal file
80
src/engine/sqlite/MessageLocationTable.vala
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/* 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.MessageLocationTable : Geary.Sqlite.Table {
|
||||
// This row *must* match the order in the schema
|
||||
public enum Column {
|
||||
ID,
|
||||
MESSAGE_ID,
|
||||
FOLDER_ID,
|
||||
ORDERING
|
||||
}
|
||||
|
||||
public MessageLocationTable(Geary.Sqlite.Database db, SQLHeavy.Table table) {
|
||||
base (db, table);
|
||||
}
|
||||
|
||||
public async int64 create_async(MessageLocationRow row, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
SQLHeavy.Query query = db.prepare(
|
||||
"INSERT INTO MessageLocationTable (message_id, folder_id, ordering) VALUES (?, ?, ?)");
|
||||
query.bind_int64(0, row.message_id);
|
||||
query.bind_int64(1, row.folder_id);
|
||||
query.bind_int64(2, row.ordering);
|
||||
|
||||
return yield query.execute_insert_async(cancellable);
|
||||
}
|
||||
|
||||
/**
|
||||
* low is zero-based.
|
||||
*/
|
||||
public async Gee.List<MessageLocationRow>? list_async(int64 folder_id, int low, int count,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
assert(low >= 0);
|
||||
|
||||
SQLHeavy.Query query = db.prepare(
|
||||
"SELECT id, message_id, ordering FROM MessageLocationTable WHERE folder_id = ? "
|
||||
+ "LIMIT ? OFFSET ? ORDER BY ordering");
|
||||
query.bind_int64(0, folder_id);
|
||||
query.bind_int(1, count);
|
||||
query.bind_int(2, low);
|
||||
|
||||
SQLHeavy.QueryResult results = yield query.execute_async(cancellable);
|
||||
if (results.finished)
|
||||
return null;
|
||||
|
||||
Gee.List<MessageLocationRow> list = new Gee.ArrayList<MessageLocationRow>();
|
||||
do {
|
||||
list.add(new MessageLocationRow(this, results.fetch_int64(0), results.fetch_int64(1),
|
||||
folder_id, results.fetch_int64(2)));
|
||||
yield results.next_async(cancellable);
|
||||
} while (!results.finished);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* num is zero-based.
|
||||
*/
|
||||
public async MessageLocationRow? fetch_async(int64 folder_id, int num,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
assert(num >= 0);
|
||||
|
||||
SQLHeavy.Query query = db.prepare(
|
||||
"SELECT id, message_id, ordering FROM MessageLocationTable WHERE folder_id = ? "
|
||||
+ "LIMIT 1 OFFSET ? ORDER BY ordering");
|
||||
query.bind_int64(0, folder_id);
|
||||
query.bind_int64(1, num);
|
||||
|
||||
SQLHeavy.QueryResult results = yield query.execute_async(cancellable);
|
||||
if (results.finished)
|
||||
return null;
|
||||
|
||||
return new MessageLocationRow(this, results.fetch_int64(0), results.fetch_int64(1), folder_id,
|
||||
results.fetch_int64(2));
|
||||
}
|
||||
}
|
||||
|
||||
154
src/engine/sqlite/MessageRow.vala
Normal file
154
src/engine/sqlite/MessageRow.vala
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/* 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.MessageRow : Geary.Sqlite.Row {
|
||||
public int64 id { get; set; default = INVALID_ID; }
|
||||
|
||||
public string? date { get; set; }
|
||||
public time_t date_time_t { get; set; default = -1; }
|
||||
|
||||
public string? from { get; set; }
|
||||
public string? sender { get; set; }
|
||||
public string? reply_to { get; set; }
|
||||
|
||||
public string? to { get; set; }
|
||||
public string? cc { get; set; }
|
||||
public string? bcc { get; set; }
|
||||
|
||||
public string? message_id { get; set; }
|
||||
public string? in_reply_to { get; set; }
|
||||
|
||||
public string? subject { get; set; }
|
||||
|
||||
public string? header { get; set; }
|
||||
|
||||
public string? body { get; set; }
|
||||
|
||||
public MessageRow(Table table) {
|
||||
base (table);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public MessageRow.from_query_result(Table table, Geary.Email.Field fields, SQLHeavy.QueryResult result)
|
||||
throws Error {
|
||||
base (table);
|
||||
|
||||
id = fetch_int64_for(result, MessageTable.Column.ID);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.ORIGINATORS) != 0) {
|
||||
from = fetch_string_for(result, MessageTable.Column.FROM_FIELD);
|
||||
sender = fetch_string_for(result, MessageTable.Column.SENDER);
|
||||
reply_to = fetch_string_for(result, MessageTable.Column.REPLY_TO);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.RECEIVERS) != 0) {
|
||||
to = fetch_string_for(result, MessageTable.Column.TO_FIELD);
|
||||
cc = fetch_string_for(result, MessageTable.Column.CC);
|
||||
bcc = fetch_string_for(result, MessageTable.Column.BCC);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.REFERENCES) != 0) {
|
||||
message_id = fetch_string_for(result, MessageTable.Column.MESSAGE_ID);
|
||||
in_reply_to = fetch_string_for(result, MessageTable.Column.IN_REPLY_TO);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.SUBJECT) != 0)
|
||||
subject = fetch_string_for(result, MessageTable.Column.SUBJECT);
|
||||
|
||||
if ((fields & Geary.Email.Field.HEADER) != 0)
|
||||
header = fetch_string_for(result, MessageTable.Column.HEADER);
|
||||
|
||||
if ((fields & Geary.Email.Field.BODY) != 0)
|
||||
body = fetch_string_for(result, MessageTable.Column.BODY);
|
||||
}
|
||||
|
||||
public Geary.Email to_email(int msg_num) throws Error {
|
||||
Geary.Email email = new Geary.Email(msg_num);
|
||||
|
||||
email.date = (date != null) ? new RFC822.Date(date) : null;
|
||||
|
||||
email.from = unflatten_addresses(from);
|
||||
email.sender = unflatten_addresses(sender);
|
||||
email.reply_to = unflatten_addresses(reply_to);
|
||||
|
||||
email.to = unflatten_addresses(to);
|
||||
email.cc = unflatten_addresses(cc);
|
||||
email.bcc = 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;
|
||||
|
||||
email.subject = (subject != null) ? new RFC822.Subject(subject) : null;
|
||||
|
||||
email.header = (header != null) ? new RFC822.Header(new Geary.Memory.StringBuffer(header))
|
||||
: null;
|
||||
|
||||
email.body = (body != null) ? new RFC822.Text(new Geary.Memory.StringBuffer(body))
|
||||
: null;
|
||||
|
||||
return email;
|
||||
}
|
||||
|
||||
public string? flatten_addresses(RFC822.MailboxAddresses? addrs) {
|
||||
if (addrs == null)
|
||||
return null;
|
||||
|
||||
switch (addrs.size) {
|
||||
case 0:
|
||||
return null;
|
||||
|
||||
case 1:
|
||||
return addrs[0].get_full_address();
|
||||
|
||||
default:
|
||||
StringBuilder builder = new StringBuilder();
|
||||
foreach (RFC822.MailboxAddress addr in addrs) {
|
||||
if (!String.is_empty(builder.str))
|
||||
builder.append(", ");
|
||||
|
||||
builder.append(addr.get_full_address());
|
||||
}
|
||||
|
||||
return builder.str;
|
||||
}
|
||||
}
|
||||
|
||||
public RFC822.MailboxAddresses? unflatten_addresses(string? str) {
|
||||
if (str == null)
|
||||
return null;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
143
src/engine/sqlite/MessageTable.vala
Normal file
143
src/engine/sqlite/MessageTable.vala
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/* 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.MessageTable : Geary.Sqlite.Table {
|
||||
// This *must* match the column order in the database
|
||||
public enum Column {
|
||||
ID,
|
||||
|
||||
DATE_FIELD,
|
||||
DATE_INT64,
|
||||
|
||||
FROM_FIELD,
|
||||
SENDER,
|
||||
REPLY_TO,
|
||||
|
||||
TO_FIELD,
|
||||
CC,
|
||||
BCC,
|
||||
|
||||
MESSAGE_ID,
|
||||
IN_REPLY_TO,
|
||||
|
||||
SUBJECT,
|
||||
|
||||
HEADER,
|
||||
|
||||
BODY;
|
||||
}
|
||||
|
||||
internal MessageTable(Geary.Sqlite.Database gdb, SQLHeavy.Table table) {
|
||||
base (gdb, table);
|
||||
}
|
||||
|
||||
public async int64 create_async(MessageRow row, Cancellable? cancellable) throws Error {
|
||||
SQLHeavy.Query query = db.prepare(
|
||||
"INSERT INTO MessageTable "
|
||||
+ "(date_field, date_time_t, from_field, sender, reply_to, to_field, cc, bcc, message_id, in_reply_to, subject, header, body) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
query.bind_string(0, row.date);
|
||||
query.bind_int64(1, row.date_time_t);
|
||||
query.bind_string(2, row.from);
|
||||
query.bind_string(3, row.sender);
|
||||
query.bind_string(4, row.reply_to);
|
||||
query.bind_string(5, row.to);
|
||||
query.bind_string(6, row.cc);
|
||||
query.bind_string(7, row.bcc);
|
||||
query.bind_string(8, row.message_id);
|
||||
query.bind_string(9, row.in_reply_to);
|
||||
query.bind_string(10, row.subject);
|
||||
query.bind_string(11, row.header);
|
||||
query.bind_string(12, row.body);
|
||||
|
||||
return yield query.execute_insert_async(cancellable);
|
||||
}
|
||||
|
||||
public async Gee.List<MessageRow>? list_by_message_id_async(Geary.RFC822.MessageID message_id,
|
||||
Geary.Email.Field fields, Cancellable? cancellable) throws Error {
|
||||
assert(fields != Geary.Email.Field.NONE);
|
||||
|
||||
SQLHeavy.Query query = db.prepare(
|
||||
"SELECT %s FROM MessageTable WHERE message_id = ?".printf(fields_to_columns(fields)));
|
||||
query.bind_string(0, message_id.value);
|
||||
|
||||
SQLHeavy.QueryResult results = yield query.execute_async(cancellable);
|
||||
if (results.finished)
|
||||
return null;
|
||||
|
||||
Gee.List<MessageRow> list = new Gee.ArrayList<MessageRow>();
|
||||
do {
|
||||
list.add(new MessageRow.from_query_result(this, fields, results));
|
||||
yield results.next_async(cancellable);
|
||||
} while (!results.finished);
|
||||
|
||||
return (list.size > 0) ? list : null;
|
||||
}
|
||||
|
||||
public async MessageRow? fetch_async(int64 id, Geary.Email.Field fields,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
assert(fields != Geary.Email.Field.NONE);
|
||||
|
||||
SQLHeavy.Query query = db.prepare(
|
||||
"SELECT %s FROM MessageTable WHERE id = ?".printf(fields_to_columns(fields)));
|
||||
query.bind_int64(0, id);
|
||||
|
||||
SQLHeavy.QueryResult results = yield query.execute_async(cancellable);
|
||||
if (results.finished)
|
||||
return null;
|
||||
|
||||
MessageRow row = new MessageRow.from_query_result(this, fields, results);
|
||||
row.id = id;
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
private static string fields_to_columns(Geary.Email.Field fields) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
foreach (Geary.Email.Field field in Geary.Email.Field.all()) {
|
||||
string? append = null;
|
||||
switch (field) {
|
||||
case Geary.Email.Field.DATE:
|
||||
append = "date_field, date_time_t";
|
||||
break;
|
||||
|
||||
case Geary.Email.Field.ORIGINATORS:
|
||||
append = "from_field, sender, reply_to";
|
||||
break;
|
||||
|
||||
case Geary.Email.Field.RECEIVERS:
|
||||
append = "to_field, cc, bcc";
|
||||
break;
|
||||
|
||||
case Geary.Email.Field.REFERENCES:
|
||||
append = "message_id, in_reply_to";
|
||||
break;
|
||||
|
||||
case Geary.Email.Field.SUBJECT:
|
||||
append = "subject";
|
||||
break;
|
||||
|
||||
case Geary.Email.Field.HEADER:
|
||||
append = "header";
|
||||
break;
|
||||
|
||||
case Geary.Email.Field.BODY:
|
||||
append = "body";
|
||||
break;
|
||||
}
|
||||
|
||||
if (append != null) {
|
||||
if (!String.is_empty(builder.str))
|
||||
builder.append(", ");
|
||||
|
||||
builder.append(append);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.str;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7,19 +7,22 @@
|
|||
public abstract class Geary.Sqlite.Row {
|
||||
public const int64 INVALID_ID = -1;
|
||||
|
||||
public static int fetch_int_for(SQLHeavy.QueryResult result, string name)
|
||||
throws SQLHeavy.Error {
|
||||
return result.fetch_int(result.field_index(name));
|
||||
private Table table;
|
||||
|
||||
public Row(Table table) {
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
public static int64 fetch_int64_for(SQLHeavy.QueryResult result, string name)
|
||||
throws SQLHeavy.Error {
|
||||
return result.fetch_int64(result.field_index(name));
|
||||
public int fetch_int_for(SQLHeavy.QueryResult result, int col) throws SQLHeavy.Error {
|
||||
return result.fetch_int(result.field_index(table.get_field_name(col)));
|
||||
}
|
||||
|
||||
public static string fetch_string_for(SQLHeavy.QueryResult result, string name)
|
||||
throws SQLHeavy.Error {
|
||||
return result.fetch_string(result.field_index(name));
|
||||
public int64 fetch_int64_for(SQLHeavy.QueryResult result, int col) throws SQLHeavy.Error {
|
||||
return result.fetch_int64(result.field_index(table.get_field_name(col)));
|
||||
}
|
||||
|
||||
public string fetch_string_for(SQLHeavy.QueryResult result, int col) throws SQLHeavy.Error {
|
||||
return result.fetch_string(result.field_index(table.get_field_name(col)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,5 +18,9 @@ public abstract class Geary.Sqlite.Table {
|
|||
this.gdb = gdb;
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
public string get_field_name(int col) throws SQLHeavy.Error {
|
||||
return table.field_name(col);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,49 +18,48 @@ public class Geary.Sqlite.Account : Object, Geary.Account, Geary.LocalAccount {
|
|||
folder_table = db.get_folder_table();
|
||||
}
|
||||
|
||||
public async Gee.Collection<Geary.Folder> list_async(string? parent_folder,
|
||||
public async void create_folder_async(Geary.Folder? parent, Geary.Folder folder,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
yield folder_table.create_async(new FolderRow(folder_table, folder.get_name(), Row.INVALID_ID),
|
||||
cancellable);
|
||||
}
|
||||
|
||||
public async void create_many_folders_async(Geary.Folder? parent, Gee.Collection<Geary.Folder> folders,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
Gee.List<FolderRow> rows = new Gee.ArrayList<FolderRow>();
|
||||
foreach (Geary.Folder folder in folders)
|
||||
rows.add(new FolderRow(db.get_folder_table(), folder.get_name(), Row.INVALID_ID));
|
||||
|
||||
yield folder_table.create_many_async(rows, cancellable);
|
||||
}
|
||||
|
||||
public async Gee.Collection<Geary.Folder> list_folders_async(Geary.Folder? parent,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
Gee.List<FolderRow> rows = yield folder_table.list_async(Row.INVALID_ID, cancellable);
|
||||
|
||||
Gee.Collection<Geary.Folder> folders = new Gee.ArrayList<Geary.Sqlite.Folder>();
|
||||
foreach (FolderRow row in rows)
|
||||
folders.add(new Geary.Sqlite.Folder(row));
|
||||
folders.add(new Geary.Sqlite.Folder(db, row));
|
||||
|
||||
return folders;
|
||||
}
|
||||
|
||||
public async Geary.Folder fetch_async(string? parent_folder, string folder_name,
|
||||
public async Geary.Folder fetch_folder_async(Geary.Folder? parent, string folder_name,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
FolderRow? row = yield folder_table.fetch_async(Row.INVALID_ID, folder_name, cancellable);
|
||||
if (row == null)
|
||||
throw new EngineError.NOT_FOUND("%s not found in local database", folder_name);
|
||||
throw new EngineError.NOT_FOUND("\"%s\" not found in local database", folder_name);
|
||||
|
||||
return new Geary.Sqlite.Folder(row);
|
||||
return new Geary.Sqlite.Folder(db, row);
|
||||
}
|
||||
|
||||
public async void create_async(Geary.Folder folder, Cancellable? cancellable = null) throws Error {
|
||||
yield folder_table.create_async(
|
||||
new FolderRow(folder.get_name(), folder.does_support_children(), folder.is_openable()),
|
||||
cancellable);
|
||||
}
|
||||
|
||||
public async void create_many_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) {
|
||||
rows.add(new FolderRow(folder.get_name(), folder.does_support_children(),
|
||||
folder.is_openable()));
|
||||
}
|
||||
|
||||
yield folder_table.create_many_async(rows, cancellable);
|
||||
}
|
||||
|
||||
public async void remove_async(string folder, Cancellable? cancellable = null) throws Error {
|
||||
public async void remove_folder_async(Geary.Folder folder, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public async void remove_many_async(Gee.Set<string> folders, Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
public async void remove_many_folders_async(Gee.Set<Geary.Folder> folders,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,63 +5,91 @@
|
|||
*/
|
||||
|
||||
public class Geary.Sqlite.Folder : Object, Geary.Folder {
|
||||
private FolderRow row;
|
||||
private MailDatabase db;
|
||||
private FolderRow folder_row;
|
||||
private MessageTable message_table;
|
||||
private MessageLocationTable location_table;
|
||||
private string name;
|
||||
private Trillian readonly;
|
||||
private Trillian supports_children;
|
||||
private Trillian children;
|
||||
private Trillian openable;
|
||||
|
||||
internal Folder(FolderRow row) throws Error {
|
||||
this.row = row;
|
||||
internal Folder(MailDatabase db, FolderRow folder_row) throws Error {
|
||||
this.db = db;
|
||||
this.folder_row = folder_row;
|
||||
|
||||
name = row.name;
|
||||
readonly = Trillian.UNKNOWN;
|
||||
supports_children = row.supports_children;
|
||||
children = Trillian.UNKNOWN;
|
||||
openable = row.is_openable;
|
||||
name = folder_row.name;
|
||||
|
||||
message_table = db.get_message_table();
|
||||
location_table = db.get_message_location_table();
|
||||
}
|
||||
|
||||
public string get_name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Trillian is_readonly() {
|
||||
return readonly;
|
||||
}
|
||||
|
||||
public Trillian does_support_children() {
|
||||
return supports_children;
|
||||
}
|
||||
|
||||
public Trillian has_children() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public Trillian is_openable() {
|
||||
return openable;
|
||||
public Geary.FolderProperties? get_properties() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public async void open_async(bool readonly, Cancellable? cancellable = null) throws Error {
|
||||
this.readonly = Trillian.TRUE;
|
||||
}
|
||||
|
||||
public async void close_async(Cancellable? cancellable = null) throws Error {
|
||||
this.readonly = Trillian.UNKNOWN;
|
||||
}
|
||||
|
||||
public int get_message_count() throws Error {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public async Gee.List<Geary.EmailHeader>? read_async(int low, int count,
|
||||
public async void create_email_async(Geary.Email email, Geary.EmailOrdering ordering,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
return null;
|
||||
int64 message_id = yield message_table.create_async(
|
||||
new MessageRow.from_email(message_table, email),
|
||||
cancellable);
|
||||
|
||||
MessageLocationRow location_row = new MessageLocationRow(location_table, Row.INVALID_ID,
|
||||
message_id, folder_row.id, ordering.ordinal);
|
||||
yield location_table.create_async(location_row, cancellable);
|
||||
}
|
||||
|
||||
public async Geary.Email fetch_async(Geary.EmailHeader header,
|
||||
public async Gee.List<Geary.Email> list_email_async(int low, int count, Geary.Email.Field fields,
|
||||
Cancellable? cancellable) throws Error {
|
||||
assert(low >= 1);
|
||||
assert(count >= 1);
|
||||
|
||||
// low is zero-based in the database.
|
||||
Gee.List<MessageLocationRow>? list = yield location_table.list_async(folder_row.id, low - 1,
|
||||
count, cancellable);
|
||||
if (list == null || list.size == 0)
|
||||
throw new EngineError.NOT_FOUND("No messages found at position %d in %s", low, name);
|
||||
|
||||
Gee.List<Geary.Email> emails = new Gee.ArrayList<Geary.Email>();
|
||||
int msg_num = 1;
|
||||
foreach (MessageLocationRow location_row in list) {
|
||||
MessageRow? message_row = yield message_table.fetch_async(location_row.message_id,
|
||||
fields, cancellable);
|
||||
assert(message_row != null);
|
||||
|
||||
emails.add(message_row.to_email(msg_num++));
|
||||
}
|
||||
|
||||
return (emails.size > 0) ? emails : null;
|
||||
}
|
||||
|
||||
public async Geary.Email fetch_email_async(int num, Geary.Email.Field fields,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
throw new EngineError.OPEN_REQUIRED("Not implemented");
|
||||
assert(num >= 0);
|
||||
|
||||
// num is zero-based in the database.
|
||||
MessageLocationRow? location_row = yield location_table.fetch_async(folder_row.id, num - 1,
|
||||
cancellable);
|
||||
if (location_row == null)
|
||||
throw new EngineError.NOT_FOUND("No message number %s in folder %s", num, name);
|
||||
|
||||
MessageRow? message_row = yield message_table.fetch_async(location_row.message_id, fields,
|
||||
cancellable);
|
||||
if (message_row == null)
|
||||
throw new EngineError.NOT_FOUND("No message number %s in folde %s", num, name);
|
||||
|
||||
return message_row.to_email(num);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,30 @@ public abstract class Geary.Memory.AbstractBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
public class Geary.Memory.StringBuffer : Geary.Memory.AbstractBuffer {
|
||||
private string str;
|
||||
|
||||
public StringBuffer(string str) {
|
||||
this.str = str;
|
||||
}
|
||||
|
||||
public override size_t get_size() {
|
||||
return str.data.length;
|
||||
}
|
||||
|
||||
public override size_t get_actual_size() {
|
||||
return str.data.length;
|
||||
}
|
||||
|
||||
public override uint8[] get_buffer() {
|
||||
return str.data;
|
||||
}
|
||||
|
||||
public override InputStream get_input_stream() {
|
||||
return new MemoryInputStream.from_data(str.data, null);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Memory.Buffer : Geary.Memory.AbstractBuffer {
|
||||
private uint8[] buffer;
|
||||
private size_t filled;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue