Commit 8059f0f3 authored by Eric Gregory's avatar Eric Gregory

Merge branch 'master' into feature/drafts

parents 7146ca47 a029b1fa
......@@ -16,6 +16,7 @@ Jens Georg <mail@jensge.org>
Michael George <mdgeorge@cs.cornell.edu>
Sven Hagemann <sven@rednose.nl>
Mathias Hasselmann <mathias@openismus.com>
Brendan Long <self@brendanlong.com>
Timo Kluck <tkluck@infty.nl>
Avi Levy <avi.w.levy@gmail.com>
Kai Mast <mail@kai-mast.de>
......
......@@ -10,8 +10,14 @@ public abstract class Geary.BaseObject : Object {
protected BaseObject() {
lock (refmap) {
if (refmap == null)
refmap = new Gee.HashMap<unowned string, int>(direct_hash, direct_equal);
if (refmap == null) {
// because strings are unowned and guaranteed to be
// unique by GType, use direct comparison functions,
// more efficient then string hash/equal
refmap = new Gee.HashMap<unowned string, int>(
Gee.Functions.get_hash_func_for(typeof(void*)),
Gee.Functions.get_equal_func_for(typeof(void*)));
}
unowned string classname = get_classname();
refmap.set(classname, refmap.get(classname) + 1);
......@@ -42,7 +48,7 @@ public abstract class Geary.BaseObject : Object {
Gee.ArrayList<unowned string> list = new Gee.ArrayList<unowned string>();
list.add_all(refmap.keys);
list.sort(strcmp);
list.sort();
foreach (unowned string classname in list)
outs.printf("%9d %s\n", refmap.get(classname), classname);
}
......
......@@ -369,6 +369,17 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
} else if (epoch_id != null) {
oldest_local_id = epoch_id;
}
// look for complete synchronization of UIDs (i.e. complete vector normalization)
// no need to keep searching once this happens
int local_count = yield folder.local_folder.get_email_count_async(ImapDB.Folder.ListFlags.NONE,
bg_cancellable);
if (local_count >= folder.properties.email_total) {
debug("Total vector normalization for %s: %d/%d emails", folder.to_string(), local_count,
folder.properties.email_total);
break;
}
} while (current_epoch.compare(epoch) > 0);
} else {
debug("No expansion necessary for %s, oldest local (%s) is before epoch (%s)",
......
......@@ -1028,7 +1028,11 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
check_open("expunge_email_async");
check_ids("expunge_email_async", email_ids);
replay_queue.schedule(new ExpungeEmail(this, email_ids, cancellable));
ExpungeEmail expunge = new ExpungeEmail(this, (Gee.List<ImapDB.EmailIdentifier>) email_ids,
cancellable);
replay_queue.schedule(expunge);
yield expunge.wait_for_ready_async(cancellable);
}
private void check_open(string method) throws EngineError {
......@@ -1058,22 +1062,29 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
Cancellable? cancellable = null) throws Error {
check_open("mark_email_async");
replay_queue.schedule(new MarkEmail(this, to_mark, flags_to_add, flags_to_remove,
cancellable));
MarkEmail mark = new MarkEmail(this, to_mark, flags_to_add, flags_to_remove, cancellable);
replay_queue.schedule(mark);
yield mark.wait_for_ready_async(cancellable);
}
public virtual async void copy_email_async(Gee.List<Geary.EmailIdentifier> to_copy,
Geary.FolderPath destination, Cancellable? cancellable = null) throws Error {
check_open("copy_email_async");
check_ids("copy_email_async", to_copy);
replay_queue.schedule(new CopyEmail(this, to_copy, destination));
CopyEmail copy = new CopyEmail(this, (Gee.List<ImapDB.EmailIdentifier>) to_copy, destination);
replay_queue.schedule(copy);
yield copy.wait_for_ready_async(cancellable);
}
public virtual async void move_email_async(Gee.List<Geary.EmailIdentifier> to_move,
Geary.FolderPath destination, Cancellable? cancellable = null) throws Error {
check_open("move_email_async");
check_ids("move_email_async", to_move);
replay_queue.schedule(new MoveEmail(this, to_move, destination));
MoveEmail move = new MoveEmail(this, (Gee.List<ImapDB.EmailIdentifier>) to_move, destination);
replay_queue.schedule(move);
yield move.wait_for_ready_async(cancellable);
}
private void on_email_flags_changed(Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> changed) {
......@@ -1113,7 +1124,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
// class. Magically generating EmailIdentifiers is a bad idea. (That's why this uses
// list_email_by_id_async() and not fetch_email_async(), and why OLDEST_TO_NEWEST is set.)
Gee.List<Geary.Email>? list = yield list_email_by_id_async(found_id, 1, Geary.Email.Field.NONE,
ListFlags.OLDEST_TO_NEWEST, cancellable);
ListFlags.OLDEST_TO_NEWEST | ListFlags.INCLUDING_ID, cancellable);
if (list == null || list.size == 0)
return null;
......
......@@ -45,26 +45,64 @@ public class Geary.RFC822.MessageIDList : Geary.MessageData.AbstractMessageData,
public MessageIDList.from_rfc822_string(string value) {
this ();
string[] ids = value.split_set(" \n\r\t");
// Have seen some mailers use commas between Message-IDs, meaning that the standard
// whitespace tokenizer is not sufficient; however, can't add the comma (or every other
// delimiter that mailers dream up) because it may be used within a Message-ID. The
// only guarantee made of a Message-ID is that it's surrounded by angle brackets, so
// mark anything not an angle bracket as a space and strip
//
// NOTE: Seen at least one spamfilter mailer that imaginatively uses parens instead of
// angle brackets for its Message-IDs; accounting for that as well here.
StringBuilder canonicalized = new StringBuilder();
int index = 0;
unichar ch;
bool in_message_id = false;
while (value.get_next_char(ref index, out ch)) {
switch (ch) {
case '<':
in_message_id = true;
break;
case '(':
if (!in_message_id) {
ch = '<';
in_message_id = true;
}
break;
case '>':
in_message_id = false;
break;
case ')':
if (in_message_id) {
ch = '>';
in_message_id = false;
}
break;
// anything not inside the message-id brackets is turned into spaces
default:
if (!in_message_id)
ch = ' ';
break;
}
canonicalized.append_unichar(ch);
}
if (value != canonicalized.str)
debug("Message-ID list corrected: \"%s\" -> \"%s\"", value, canonicalized.str);
// there's some additional paranoia here with getting the Message-ID sliced out of the
// strings, but it's worth it to get a valid Message-ID or none at all vs. a bogus one
string[] ids = canonicalized.str.split(" ");
foreach (string id in ids) {
if (String.is_empty(id))
continue;
// Have seen some mailers use commas between Message-IDs, meaning that the standard
// whitespace tokenizer is not sufficient; however, can't add the comma (or every other
// delimiter that mailers dream up) because it may be used within a Message-ID. The
// only guarantee made of a Message-ID is that it's surrounded by angle brackets, so
// mark anything not an angle bracket as a space and strip
//
// NOTE: Seen at least one spamfilter mailer that imaginatively uses parens instead of
// angle brackets for its Message-IDs; accounting for that as well here.
int start = id.index_of_char('<');
if (start < 0)
start = id.index_of_char('(');
int end = id.last_index_of_char('>');
if (end < 0)
end = id.last_index_of_char(')');
// if either end not found or the end comes before the beginning, invalid Message-ID
if (start < 0 || end < 0 || (start >= end)) {
......
......@@ -3,23 +3,38 @@
<!-- interface-requires gtk+ 3.0 -->
<object class="GtkActionGroup" id="compose actions">
<child>
<object class="GtkAction" id="undo"/>
<object class="GtkAction" id="undo">
<property name="label" translatable="yes">Undo</property>
<property name="icon-name">undo</property>
</object>
<accelerator key="z" modifiers="GDK_CONTROL_MASK"/>
</child>
<child>
<object class="GtkAction" id="redo"/>
<object class="GtkAction" id="redo">
<property name="label" translatable="yes">Redo</property>
<property name="icon-name">redo</property>
</object>
<accelerator key="z" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
</child>
<child>
<object class="GtkAction" id="cut"/>
<object class="GtkAction" id="cut">
<property name="label" translatable="yes">Cut</property>
<property name="icon-name">edit-cut</property>
</object>
<accelerator key="x" modifiers="GDK_CONTROL_MASK"/>
</child>
<child>
<object class="GtkAction" id="copy"/>
<object class="GtkAction" id="copy">
<property name="label" translatable="yes">Copy</property>
<property name="icon-name">edit-copy</property>
</object>
<accelerator key="c" modifiers="GDK_CONTROL_MASK"/>
</child>
<child>
<object class="GtkAction" id="paste"/>
<object class="GtkAction" id="paste">
<property name="label" translatable="yes">Paste</property>
<property name="icon-name">edit-paste</property>
</object>
<accelerator key="v" modifiers="GDK_CONTROL_MASK"/>
</child>
<child>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment