WIP
This commit is contained in:
parent
1dfcf240b4
commit
f2552d101f
21 changed files with 473 additions and 290 deletions
|
|
@ -86,9 +86,12 @@ engine/imap/message/imap-mailbox-parameter.vala
|
|||
engine/imap/message/imap-message-data.vala
|
||||
engine/imap/message/imap-message-set.vala
|
||||
engine/imap/message/imap-parameter.vala
|
||||
engine/imap/message/imap-sequence-number.vala
|
||||
engine/imap/message/imap-status-data.vala
|
||||
engine/imap/message/imap-status-data-type.vala
|
||||
engine/imap/message/imap-tag.vala
|
||||
engine/imap/message/imap-uid.vala
|
||||
engine/imap/message/imap-uid-validity.vala
|
||||
engine/imap/response/imap-coded-status-response.vala
|
||||
engine/imap/response/imap-completion-status-response.vala
|
||||
engine/imap/response/imap-continuation-response.vala
|
||||
|
|
|
|||
|
|
@ -689,8 +689,8 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
|
||||
// normalize starting at the message *after* the highest position of the local store,
|
||||
// which has now changed
|
||||
Imap.MessageSet msg_set = new Imap.MessageSet.range_by_first_last(remote_count + 1,
|
||||
new_remote_count);
|
||||
Imap.MessageSet msg_set = new Imap.MessageSet.range_by_first_last(
|
||||
new Imap.SequenceNumber(remote_count + 1), new Imap.SequenceNumber(new_remote_count));
|
||||
Gee.List<Geary.Email>? list = yield remote_folder.list_email_async(
|
||||
msg_set, ImapDB.Folder.REQUIRED_FOR_DUPLICATE_DETECTION, null);
|
||||
if (list != null && list.size > 0) {
|
||||
|
|
@ -1146,7 +1146,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
// Normalize the local folder by fetching EmailIdentifiers for all missing email as well
|
||||
// as fields for duplicate detection
|
||||
Gee.List<Geary.Email>? list = yield remote_folder.list_email_async(
|
||||
new Imap.MessageSet.range_by_count(high, prefetch_count),
|
||||
new Imap.MessageSet.range_by_count(new Imap.SequenceNumber(high), prefetch_count),
|
||||
ImapDB.Folder.REQUIRED_FOR_DUPLICATE_DETECTION, cancellable);
|
||||
if (list == null || list.size != prefetch_count) {
|
||||
throw new EngineError.BAD_PARAMETERS("Unable to prefetch %d email starting at %d in %s",
|
||||
|
|
|
|||
|
|
@ -318,9 +318,11 @@ private class Geary.ImapEngine.ListEmail : Geary.ImapEngine.SendReplayOperation
|
|||
list = needed_by_position;
|
||||
}
|
||||
|
||||
Imap.SequenceNumber[] seq_list = Imap.SequenceNumber.to_list(list);
|
||||
|
||||
// pull from server
|
||||
Gee.List<Geary.Email>? remote_list = yield engine.remote_folder.list_email_async(
|
||||
new Imap.MessageSet.sparse(list), required_fields, cancellable);
|
||||
new Imap.MessageSet.sparse(seq_list), required_fields, cancellable);
|
||||
if (remote_list == null || remote_list.size == 0)
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
private ClientSessionManager session_mgr;
|
||||
private ClientSession? session = null;
|
||||
private Nonblocking.Mutex fetch_mutex = new Nonblocking.Mutex();
|
||||
private Gee.HashMap<MessageNumber, FetchedData> fetch_accumulator = new Gee.HashMap<
|
||||
MessageNumber, FetchedData>();
|
||||
private Gee.HashMap<SequenceNumber, FetchedData> fetch_accumulator = new Gee.HashMap<
|
||||
SequenceNumber, FetchedData>();
|
||||
|
||||
public signal void exists(int total);
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
appended(total);
|
||||
}
|
||||
|
||||
private void on_expunge(MessageNumber pos) {
|
||||
private void on_expunge(SequenceNumber pos) {
|
||||
debug("%s EXPUNGE %s", to_string(), pos.to_string());
|
||||
|
||||
properties.set_select_examine_message_count(properties.select_examine_messages - 1);
|
||||
|
|
@ -143,8 +143,8 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
|
||||
private void on_fetch(FetchedData fetched_data) {
|
||||
// add if not found, merge if already received data for this email
|
||||
FetchedData? already_present = fetch_accumulator.get(fetched_data.msg_num);
|
||||
fetch_accumulator.set(fetched_data.msg_num,
|
||||
FetchedData? already_present = fetch_accumulator.get(fetched_data.seq_num);
|
||||
fetch_accumulator.set(fetched_data.seq_num,
|
||||
(already_present != null) ? fetched_data.combine(already_present) : fetched_data);
|
||||
|
||||
fetched(fetched_data);
|
||||
|
|
@ -196,7 +196,7 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
}
|
||||
|
||||
// For FETCH or STORE commands, both of which return FETCH results.
|
||||
private async Gee.HashMap<MessageNumber, FetchedData> store_fetch_commands_async(MessageSet msg_set,
|
||||
private async Gee.HashMap<SequenceNumber, FetchedData> store_fetch_commands_async(MessageSet msg_set,
|
||||
Gee.Collection<Command> store_fetch_cmds, bool lock_mutex, Cancellable? cancellable)
|
||||
throws Error {
|
||||
// watch for deadlock
|
||||
|
|
@ -216,8 +216,8 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
}
|
||||
|
||||
// swap out results and clear accumulator
|
||||
Gee.HashMap<MessageNumber, FetchedData> results = fetch_accumulator;
|
||||
fetch_accumulator = new Gee.HashMap<MessageNumber, FetchedData>();
|
||||
Gee.HashMap<SequenceNumber, FetchedData> results = fetch_accumulator;
|
||||
fetch_accumulator = new Gee.HashMap<SequenceNumber, FetchedData>();
|
||||
|
||||
// unlock after clearing accumulator
|
||||
if (token != Nonblocking.Mutex.INVALID_TOKEN)
|
||||
|
|
@ -326,8 +326,8 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
cmds.add(new FetchCommand(msg_set, data_types, null));
|
||||
}
|
||||
|
||||
Gee.HashMap<MessageNumber, UID>? pos_uid_map = null;
|
||||
Gee.HashMap<MessageNumber, FetchedData>? fetched = null;
|
||||
Gee.HashMap<SequenceNumber, UID>? pos_uid_map = null;
|
||||
Gee.HashMap<SequenceNumber, FetchedData>? fetched = null;
|
||||
Error? fetch_err = null;
|
||||
|
||||
// Commands prepped, do the actual fetching with the mutex in place
|
||||
|
|
@ -338,7 +338,7 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
// for responses to come back in any order, broken up in any way, with only positional
|
||||
// addressing, and there's no way to build Email's without the UID.
|
||||
if (!msg_set.is_uid) {
|
||||
pos_uid_map = new Gee.HashMap<MessageNumber, UID>();
|
||||
pos_uid_map = new Gee.HashMap<SequenceNumber, UID>();
|
||||
|
||||
FetchCommand cmd = new FetchCommand.data_type(msg_set, FetchDataType.UID);
|
||||
Gee.HashMap<MessageData, FetchedData> uids = yield store_fetch_commands_async(
|
||||
|
|
@ -346,10 +346,13 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
|
||||
// convert fetched UIDs into easy-lookup map
|
||||
foreach (FetchedData fetched_data in uids.values) {
|
||||
if (fetched_data.data_map.has_key(FetchDataType.UID))
|
||||
pos_uid_map.set(fetched_data.msg_num, (UID) fetched_data.data_map.get(FetchDataType.UID));
|
||||
else
|
||||
debug("No UID in FetchedData for %s on %s", fetched_data.msg_num.to_string(), to_string());
|
||||
if (fetched_data.data_map.has_key(FetchDataType.UID)) {
|
||||
pos_uid_map.set(fetched_data.seq_num,
|
||||
(UID) fetched_data.data_map.get(FetchDataType.UID));
|
||||
} else {
|
||||
debug("No UID in FetchedData for %s on %s", fetched_data.seq_num.to_string(),
|
||||
to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -368,14 +371,14 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
|
||||
// Convert fetched data into Geary.Email objects
|
||||
Gee.List<Geary.Email> email_list = new Gee.ArrayList<Geary.Email>();
|
||||
foreach (MessageNumber msg_num in fetched.keys) {
|
||||
FetchedData fetched_data = fetched.get(msg_num);
|
||||
foreach (SequenceNumber seq_num in fetched.keys) {
|
||||
FetchedData fetched_data = fetched.get(seq_num);
|
||||
|
||||
// the UID should either have been looked up (if using positional addressing) or should
|
||||
// have come back with the response (if using UID addressing)
|
||||
UID? uid = null;
|
||||
if (pos_uid_map != null)
|
||||
uid = pos_uid_map.get(msg_num);
|
||||
uid = pos_uid_map.get(seq_num);
|
||||
else
|
||||
uid = (UID) fetched_data.data_map.get(FetchDataType.UID);
|
||||
assert(uid != null);
|
||||
|
|
@ -534,7 +537,7 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
FetchBodyDataIdentifier? partial_header_identifier, FetchBodyDataIdentifier? body_identifier,
|
||||
FetchBodyDataIdentifier? preview_identifier, FetchBodyDataIdentifier? preview_charset_identifier)
|
||||
throws Error {
|
||||
Geary.Email email = new Geary.Email(fetched_data.msg_num.value,
|
||||
Geary.Email email = new Geary.Email(fetched_data.seq_num.value,
|
||||
new Imap.EmailIdentifier(uid, path));
|
||||
|
||||
// accumulate these to submit Imap.EmailProperties all at once
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
/* Copyright 2013 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.ServerDataNotifier : Object {
|
||||
//
|
||||
// ServerData (always untagged)
|
||||
//
|
||||
|
||||
public virtual signal void capability(Capabilities capabilities) {
|
||||
}
|
||||
|
||||
public virtual signal void exists(int count) {
|
||||
}
|
||||
|
||||
public virtual signal void expunge(MessageNumber msg_num) {
|
||||
}
|
||||
|
||||
public virtual signal void fetch(FetchedData fetched_data) {
|
||||
}
|
||||
|
||||
public virtual signal void flags(MailboxAttributes mailbox_attrs) {
|
||||
}
|
||||
|
||||
public virtual signal void list(MailboxInformation mailbox_info) {
|
||||
}
|
||||
|
||||
// TODO: LSUB results
|
||||
|
||||
public virtual signal void recent(int count) {
|
||||
}
|
||||
|
||||
// TODO: SEARCH results
|
||||
|
||||
// TODO: STATUS results
|
||||
|
||||
public ServerDataNotifier() {
|
||||
}
|
||||
|
||||
public bool notify(ServerData server_data) throws ImapError {
|
||||
switch (server_data.server_data_type) {
|
||||
case ServerDataType.CAPABILITY:
|
||||
capability(CapabilityDecoder.decode(server_data));
|
||||
break;
|
||||
|
||||
case ServerDataType.EXISTS:
|
||||
exists(ExistsDecoder.decode(server_data));
|
||||
break;
|
||||
|
||||
case ServerDataType.EXPUNGE:
|
||||
expunge(ExpungedDecoder.decode(server_data));
|
||||
break;
|
||||
|
||||
case ServerDataType.FETCH:
|
||||
fetch(FetchDecoder.decode(server_data));
|
||||
break;
|
||||
|
||||
case ServerDataType.FLAGS:
|
||||
flags(FlagsDecoder.decode(server_data));
|
||||
break;
|
||||
|
||||
case ServerDataType.LIST:
|
||||
list(ListDecoder.decode(server_data));
|
||||
break;
|
||||
|
||||
case ServerDataType.RECENT:
|
||||
recent(RecentDecoder.decode(server_data));
|
||||
break;
|
||||
|
||||
// TODO: LSUB, SEARCH, and STATUS
|
||||
case ServerDataType.STATUS:
|
||||
case ServerDataType.LSUB:
|
||||
case ServerDataType.SEARCH:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4,11 +4,50 @@
|
|||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A representation of an IMAP command (request).
|
||||
*
|
||||
* A Command is created by the caller and then submitted to a {@link ClientSession} or
|
||||
* {@link ClientConnection} for transmission to the server. In response, one or more
|
||||
* {@link ServerResponse}s are returned, generally zero or more {@link ServerData}s followed by
|
||||
* a {@link CompletionStatusResponse}. {@link ResponseCode}s, {@link StatusResponse}s, and/or
|
||||
* {@link CodedStatusResponse}s may also be returned, depending on the Command.
|
||||
*
|
||||
* See [[http://tools.ietf.org/html/rfc3501#section-6]]
|
||||
*/
|
||||
|
||||
public class Geary.Imap.Command : RootParameters {
|
||||
/**
|
||||
* All IMAP commands are tagged with an identifier assigned by the client.
|
||||
*
|
||||
* Note that this is not immutable. The general practice is to use an unassigned Tag
|
||||
* up until the {@link Command} is about to be transmitted, at which point a Tag is
|
||||
* assigned. This allows for all commands to be issued in Tag "order". This generally makes
|
||||
* tracing network traffic easier.
|
||||
*
|
||||
* @see Tag.get_unassigned
|
||||
* @see assign_tag
|
||||
*/
|
||||
public Tag tag { get; private set; }
|
||||
|
||||
/**
|
||||
* The name (or "verb") of the {@link Command}.
|
||||
*/
|
||||
public string name { get; private set; }
|
||||
|
||||
/**
|
||||
* Zero or more arguments for the {@link Command}.
|
||||
*
|
||||
* Note that some Commands have require args and others are optional. The format of the
|
||||
* arguments ({@link StringParameter}, {@link ListParameter}, etc.) is sometimes crucial.
|
||||
*/
|
||||
public string[]? args { get; private set; }
|
||||
|
||||
/**
|
||||
* Create a Command with an unassigned Tag.
|
||||
*
|
||||
* @see tag
|
||||
*/
|
||||
public Command(string name, string[]? args = null) {
|
||||
tag = Tag.get_unassigned();
|
||||
this.name = name;
|
||||
|
|
@ -17,6 +56,11 @@ public class Geary.Imap.Command : RootParameters {
|
|||
stock_params();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Command with an assigned Tag.
|
||||
*
|
||||
* @see tag
|
||||
*/
|
||||
public Command.assigned(Tag tag, string name, string[]? args = null)
|
||||
requires (tag.is_tagged() && tag.is_assigned()) {
|
||||
this.tag = tag;
|
||||
|
|
@ -36,6 +80,8 @@ public class Geary.Imap.Command : RootParameters {
|
|||
}
|
||||
|
||||
/**
|
||||
* Assign a {@link Tag} to a {@link Command} with an unassigned placeholder Tag.
|
||||
*
|
||||
* Can only be called on a Command that holds an unassigned Tag. Thus, this can only be called
|
||||
* once at most, and zero times if Command.assigned() was used to generate the Command.
|
||||
* Fires an assertion if either of these cases is true, or if the supplied Tag is unassigned.
|
||||
|
|
|
|||
|
|
@ -4,6 +4,18 @@
|
|||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A representation of the IMAP FETCH command.
|
||||
*
|
||||
* FETCH is easily the most complicated IMAP command. It has a number of parameters, some of which
|
||||
* have a number of variants, and the data that is returned requires involved decoding patterns.
|
||||
*
|
||||
* See [[http://tools.ietf.org/html/rfc3501#section-6.4.5]]
|
||||
*
|
||||
* @see FetchedData
|
||||
* @see StoreCommand
|
||||
*/
|
||||
|
||||
public class Geary.Imap.FetchCommand : Command {
|
||||
public const string NAME = "fetch";
|
||||
public const string UID_NAME = "uid fetch";
|
||||
|
|
|
|||
|
|
@ -10,13 +10,14 @@
|
|||
* See [[http://tools.ietf.org/html/rfc3501#section-7.4.2]]
|
||||
*
|
||||
* @see FetchCommand
|
||||
* @see StoreCommand
|
||||
*/
|
||||
public class Geary.Imap.FetchedData : Object {
|
||||
/**
|
||||
* The positional address of the email in the mailbox.
|
||||
*/
|
||||
public SequenceNumber seq_num { get; private set; }
|
||||
|
||||
public MessageNumber msg_num { get; private set; }
|
||||
/**
|
||||
* A Map of {@link FetchDataType}s to their {@link Imap.MessageData} for this email.
|
||||
*
|
||||
|
|
@ -36,15 +37,24 @@ public class Geary.Imap.FetchedData : Object {
|
|||
public Gee.Map<FetchBodyDataIdentifier, Memory.AbstractBuffer> body_data_map { get; private set;
|
||||
default = new Gee.HashMap<FetchBodyDataIdentifier, Memory.AbstractBuffer>(); }
|
||||
|
||||
public FetchedData(MessageNumber msg_num) {
|
||||
this.msg_num = msg_num;
|
||||
public FetchedData(SequenceNumber seq_num) {
|
||||
this.seq_num = seq_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes {@link ServerData} into a FetchedData representation.
|
||||
*
|
||||
* The ServerData must be the response to a FETCH or STORE command.
|
||||
*
|
||||
* @see FetchCommand
|
||||
* @see StoreCommand
|
||||
* @see ServerData.get_fetch
|
||||
*/
|
||||
public static FetchedData decode(ServerData server_data) throws ImapError {
|
||||
if (!server_data.get_as_string(2).equals_ci(FetchCommand.NAME))
|
||||
throw new ImapError.PARSE_ERROR("Not FETCH data: %s", server_data.to_string());
|
||||
|
||||
FetchedData fetched_data = new FetchedData(new MessageNumber(server_data.get_as_string(1).as_int()));
|
||||
FetchedData fetched_data = new FetchedData(new SequenceNumber(server_data.get_as_string(1).as_int()));
|
||||
|
||||
// walk the list for each returned fetch data item, which is paired by its data item name
|
||||
// and the structured data itself
|
||||
|
|
@ -94,13 +104,13 @@ public class Geary.Imap.FetchedData : Object {
|
|||
*
|
||||
* See warnings at {@link body_data_map} for dealing with multiple FetchBodyDataTypes.
|
||||
*
|
||||
* @return null if the FetchedData do not have the same {@link msg_num}.
|
||||
* @return null if the FetchedData do not have the same {@link seq_num}.
|
||||
*/
|
||||
public FetchedData? combine(FetchedData other) {
|
||||
if (!msg_num.equal_to(other.msg_num))
|
||||
if (!seq_num.equal_to(other.seq_num))
|
||||
return null;
|
||||
|
||||
FetchedData combined = new FetchedData(msg_num);
|
||||
FetchedData combined = new FetchedData(seq_num);
|
||||
Collection.map_set_all<FetchDataType, MessageData>(combined.data_map, data_map);
|
||||
Collection.map_set_all<FetchDataType, MessageData>(combined.data_map, other.data_map);
|
||||
Collection.map_set_all<FetchBodyDataIdentifier, Memory.AbstractBuffer>(combined.body_data_map,
|
||||
|
|
@ -114,7 +124,7 @@ public class Geary.Imap.FetchedData : Object {
|
|||
public string to_string() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append_printf("[%s] ", msg_num.to_string());
|
||||
builder.append_printf("[%s] ", seq_num.to_string());
|
||||
|
||||
foreach (FetchDataType data_type in data_map.keys)
|
||||
builder.append_printf("%s=%s ", data_type.to_string(), data_map.get(data_type).to_string());
|
||||
|
|
|
|||
|
|
@ -4,9 +4,32 @@
|
|||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The decoded response to a LIST command.
|
||||
*
|
||||
* This is also the response to an XLIST command.
|
||||
*
|
||||
* See [[http://tools.ietf.org/html/rfc3501#section-7.2.2]]
|
||||
*
|
||||
* @see ListCommand
|
||||
*/
|
||||
|
||||
public class Geary.Imap.MailboxInformation : Object {
|
||||
/**
|
||||
* The decoded mailbox name.
|
||||
*
|
||||
* See {@link MailboxParameter} for the encoded version of this string.
|
||||
*/
|
||||
public string name { get; private set; }
|
||||
|
||||
/**
|
||||
* The (optional) delimiter specified by the server.
|
||||
*/
|
||||
public string? delim { get; private set; }
|
||||
|
||||
/**
|
||||
* Folder attributes returned by the server.
|
||||
*/
|
||||
public MailboxAttributes attrs { get; private set; }
|
||||
|
||||
public MailboxInformation(string name, string? delim, MailboxAttributes attrs) {
|
||||
|
|
@ -37,6 +60,8 @@ public class Geary.Imap.MailboxInformation : Object {
|
|||
}
|
||||
|
||||
/**
|
||||
* The mailbox's name without parent folders.
|
||||
*
|
||||
* If name is non-empty, will return a non-empty value which is the final folder name (i.e.
|
||||
* the parent components are stripped). If no delimiter is specified, the name is returned.
|
||||
*/
|
||||
|
|
@ -53,11 +78,20 @@ public class Geary.Imap.MailboxInformation : Object {
|
|||
return !String.is_empty(basename) ? basename : name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes {@link ServerData} into a MailboxInformation representation.
|
||||
*
|
||||
* The ServerData must be the response to a LIST or XLIST command.
|
||||
*
|
||||
* @see ListCommand
|
||||
* @see ServerData.get_list
|
||||
*/
|
||||
public static MailboxInformation decode(ServerData server_data) throws ImapError {
|
||||
StringParameter cmd = server_data.get_as_string(1);
|
||||
if (!cmd.equals_ci(ListCommand.NAME) && !cmd.equals_ci(ListCommand.XLIST_NAME))
|
||||
throw new ImapError.PARSE_ERROR("Not LIST or XLIST data: %s", server_data.to_string());
|
||||
|
||||
// Build list of attributes
|
||||
ListParameter attrs = server_data.get_as_list(2);
|
||||
Gee.Collection<MailboxAttribute> attrlist = new Gee.ArrayList<MailboxAttribute>();
|
||||
foreach (Parameter attr in attrs.get_all()) {
|
||||
|
|
@ -72,16 +106,18 @@ public class Geary.Imap.MailboxInformation : Object {
|
|||
attrlist.add(new MailboxAttribute(stringp.value));
|
||||
}
|
||||
|
||||
// decode everything
|
||||
MailboxAttributes attributes = new MailboxAttributes(attrlist);
|
||||
StringParameter? delim = server_data.get_as_nullable_string(3);
|
||||
StringParameter mailbox = server_data.get_as_string(4);
|
||||
MailboxParameter mailbox = new MailboxParameter.from_string_parameter(
|
||||
server_data.get_as_string(4));
|
||||
|
||||
// Set \Inbox to standard path
|
||||
MailboxAttributes attributes = new MailboxAttributes(attrlist);
|
||||
if (Geary.Imap.MailboxAttribute.SPECIAL_FOLDER_INBOX in attributes) {
|
||||
return new MailboxInformation(Geary.Imap.Account.INBOX_NAME,
|
||||
(delim != null) ? delim.nullable_value : null, attributes);
|
||||
} else {
|
||||
return new MailboxInformation(mailbox.value,
|
||||
return new MailboxInformation(mailbox.decode(),
|
||||
(delim != null) ? delim.nullable_value : null, attributes);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,22 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* A StringParameter that holds a mailbox reference (can be wildcarded). Used
|
||||
* to juggle between our internal UTF-8 representation of mailboxes and IMAP's
|
||||
* A {@link StringParameter} that holds a mailbox reference (can be wildcarded).
|
||||
*
|
||||
* Used to juggle between our internal UTF-8 representation of mailboxes and IMAP's
|
||||
* odd "modified UTF-7" representation. The value is stored in IMAP's encoded
|
||||
* format since that's how it comes across the wire.
|
||||
*/
|
||||
|
||||
public class Geary.Imap.MailboxParameter : StringParameter {
|
||||
public MailboxParameter(string mailbox) {
|
||||
base (utf8_to_imap_utf7(mailbox));
|
||||
}
|
||||
|
||||
public MailboxParameter.from_string_parameter(StringParameter string_parameter) {
|
||||
base (string_parameter.value);
|
||||
}
|
||||
|
||||
private static string utf8_to_imap_utf7(string utf8) {
|
||||
try {
|
||||
return Geary.ImapUtf7.utf8_to_imap_utf7(utf8);
|
||||
|
|
@ -29,15 +39,8 @@ public class Geary.Imap.MailboxParameter : StringParameter {
|
|||
}
|
||||
}
|
||||
|
||||
public MailboxParameter(string mailbox) {
|
||||
base (utf8_to_imap_utf7(mailbox));
|
||||
}
|
||||
|
||||
public MailboxParameter.from_string_parameter(StringParameter string_parameter) {
|
||||
base (string_parameter.value);
|
||||
}
|
||||
|
||||
public string decode() {
|
||||
return imap_utf7_to_utf8(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@
|
|||
|
||||
/**
|
||||
* MessageData is an IMAP data structure delivered in some form by the server to the client.
|
||||
* Although the primary use of this object is for FETCH results, other commands return
|
||||
* similarly-structured data (in particulars Flags and Attributes).
|
||||
*
|
||||
*
|
||||
* 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).
|
||||
|
|
@ -19,91 +17,6 @@
|
|||
public interface Geary.Imap.MessageData : Geary.MessageData.AbstractMessageData {
|
||||
}
|
||||
|
||||
public class Geary.Imap.UID : Geary.MessageData.Int64MessageData, Geary.Imap.MessageData,
|
||||
Gee.Comparable<Geary.Imap.UID> {
|
||||
// Using statics because int32.MAX is static, not const (??)
|
||||
public static int64 MIN = 1;
|
||||
public static int64 MAX = int32.MAX;
|
||||
public static int64 INVALID = -1;
|
||||
|
||||
public UID(int64 value) {
|
||||
base (value);
|
||||
}
|
||||
|
||||
public bool is_valid() {
|
||||
return is_value_valid(value);
|
||||
}
|
||||
|
||||
public static bool is_value_valid(int64 val) {
|
||||
return Numeric.int64_in_range_inclusive(val, MIN, MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a valid UID, which means returning MIN or MAX if the value is out of range (either
|
||||
* direction) or MAX if this value is already MAX.
|
||||
*/
|
||||
public UID next() {
|
||||
if (value < MIN)
|
||||
return new UID(MIN);
|
||||
else if (value > MAX)
|
||||
return new UID(MAX);
|
||||
else
|
||||
return new UID(Numeric.int64_ceiling(value + 1, MAX));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a valid UID, which means returning MIN or MAX if the value is out of range (either
|
||||
* direction) or MIN if this value is already MIN.
|
||||
*/
|
||||
public UID previous() {
|
||||
if (value < MIN)
|
||||
return new UID(MIN);
|
||||
else if (value > MAX)
|
||||
return new UID(MAX);
|
||||
else
|
||||
return new UID(Numeric.int64_floor(value - 1, MIN));
|
||||
}
|
||||
|
||||
public virtual int compare_to(Geary.Imap.UID other) {
|
||||
if (value < other.value)
|
||||
return -1;
|
||||
else if (value > other.value)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
public string serialize() {
|
||||
return value.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.UIDValidity : Geary.MessageData.Int64MessageData, Geary.Imap.MessageData {
|
||||
// Using statics because int32.MAX is static, not const (??)
|
||||
public static int64 MIN = 1;
|
||||
public static int64 MAX = int32.MAX;
|
||||
public static int64 INVALID = -1;
|
||||
|
||||
public UIDValidity(int64 value) {
|
||||
base (value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.MessageNumber : Geary.MessageData.IntMessageData, Geary.Imap.MessageData,
|
||||
Gee.Comparable<MessageNumber> {
|
||||
public MessageNumber(int value) {
|
||||
base (value);
|
||||
}
|
||||
|
||||
public virtual int compare_to(MessageNumber other) {
|
||||
return value - other.value;
|
||||
}
|
||||
|
||||
public string serialize() {
|
||||
return value.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class Geary.Imap.Flags : Geary.MessageData.AbstractMessageData, Geary.Imap.MessageData,
|
||||
Gee.Hashable<Geary.Imap.Flags> {
|
||||
public int size { get { return list.size; } }
|
||||
|
|
|
|||
|
|
@ -6,15 +6,29 @@
|
|||
|
||||
extern void qsort(void *base, size_t num, size_t size, CompareFunc compare_func);
|
||||
|
||||
/**
|
||||
* A represenation of an IMAP message range specifier.
|
||||
*
|
||||
* A MessageSet can be for {@link SequenceNumber}s (which use positional addressing) or
|
||||
* {@link UID}s.
|
||||
*
|
||||
* See [[http://tools.ietf.org/html/rfc3501#section-9]], "sequence-set" and "seq-range".
|
||||
*/
|
||||
|
||||
public class Geary.Imap.MessageSet : BaseObject {
|
||||
/**
|
||||
* True if the {@link MessageSet} was created with a UID or a UID range.
|
||||
*
|
||||
* For {@link Command}s that accept MessageSets, they will use a UID variant
|
||||
*/
|
||||
public bool is_uid { get; private set; default = false; }
|
||||
|
||||
private string value { get; private set; }
|
||||
|
||||
public MessageSet(int msg_num) {
|
||||
assert(msg_num > 0);
|
||||
public MessageSet(SequenceNumber seq_num) {
|
||||
assert(seq_num.value > 0);
|
||||
|
||||
value = "%d".printf(msg_num);
|
||||
value = seq_num.serialize();
|
||||
}
|
||||
|
||||
public MessageSet.uid(UID uid) {
|
||||
|
|
@ -28,29 +42,29 @@ public class Geary.Imap.MessageSet : BaseObject {
|
|||
MessageSet.uid(((Geary.Imap.EmailIdentifier) email_id).uid);
|
||||
}
|
||||
|
||||
public MessageSet.range_by_count(int low_msg_num, int count) {
|
||||
assert(low_msg_num > 0);
|
||||
public MessageSet.range_by_count(SequenceNumber low_seq_num, int count) {
|
||||
assert(low_seq_num.value > 0);
|
||||
assert(count > 0);
|
||||
|
||||
value = (count > 1)
|
||||
? "%d:%d".printf(low_msg_num, low_msg_num + count - 1)
|
||||
: "%d".printf(low_msg_num);
|
||||
? "%d:%d".printf(low_seq_num.value, low_seq_num.value + count - 1)
|
||||
: low_seq_num.serialize();
|
||||
}
|
||||
|
||||
public MessageSet.range_by_first_last(int low_msg_num, int high_msg_num) {
|
||||
assert(low_msg_num > 0);
|
||||
assert(high_msg_num > 0);
|
||||
public MessageSet.range_by_first_last(SequenceNumber low_seq_num, SequenceNumber high_seq_num) {
|
||||
assert(low_seq_num.value > 0);
|
||||
assert(high_seq_num.value > 0);
|
||||
|
||||
// correct range problems (i.e. last before first)
|
||||
if (low_msg_num > high_msg_num) {
|
||||
int swap = low_msg_num;
|
||||
low_msg_num = high_msg_num;
|
||||
high_msg_num = swap;
|
||||
if (low_seq_num.value > high_seq_num.value) {
|
||||
SequenceNumber swap = low_seq_num;
|
||||
low_seq_num = high_seq_num;
|
||||
high_seq_num = swap;
|
||||
}
|
||||
|
||||
value = (low_msg_num != high_msg_num)
|
||||
? "%d:%d".printf(low_msg_num, high_msg_num)
|
||||
: "%d".printf(low_msg_num);
|
||||
value = (!low_seq_num.equal_to(high_seq_num))
|
||||
? "%s:%s".printf(low_seq_num.serialize(), high_seq_num.serialize())
|
||||
: low_seq_num.serialize();
|
||||
}
|
||||
|
||||
public MessageSet.uid_range(UID low, UID high) {
|
||||
|
|
@ -65,10 +79,10 @@ public class Geary.Imap.MessageSet : BaseObject {
|
|||
is_uid = true;
|
||||
}
|
||||
|
||||
public MessageSet.range_to_highest(int low_msg_num) {
|
||||
assert(low_msg_num > 0);
|
||||
public MessageSet.range_to_highest(SequenceNumber low_seq_num) {
|
||||
assert(low_seq_num.value > 0);
|
||||
|
||||
value = "%d:*".printf(low_msg_num);
|
||||
value = "%s:*".printf(low_seq_num.serialize());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -108,8 +122,8 @@ public class Geary.Imap.MessageSet : BaseObject {
|
|||
is_uid = true;
|
||||
}
|
||||
|
||||
public MessageSet.sparse(int[] msg_nums) {
|
||||
value = build_sparse_range(msg_array_to_int64(msg_nums));
|
||||
public MessageSet.sparse(SequenceNumber[] seq_nums) {
|
||||
value = build_sparse_range(seq_array_to_int64(seq_nums));
|
||||
}
|
||||
|
||||
public MessageSet.uid_sparse(UID[] msg_uids) {
|
||||
|
|
@ -124,8 +138,8 @@ public class Geary.Imap.MessageSet : BaseObject {
|
|||
is_uid = true;
|
||||
}
|
||||
|
||||
public MessageSet.sparse_to_highest(int[] msg_nums) {
|
||||
value = "%s:*".printf(build_sparse_range(msg_array_to_int64(msg_nums)));
|
||||
public MessageSet.sparse_to_highest(SequenceNumber[] seq_nums) {
|
||||
value = "%s:*".printf(build_sparse_range(seq_array_to_int64(seq_nums)));
|
||||
}
|
||||
|
||||
public MessageSet.multirange(MessageSet[] msg_sets) {
|
||||
|
|
@ -168,29 +182,29 @@ public class Geary.Imap.MessageSet : BaseObject {
|
|||
// Builds sparse range of either UID values or message numbers.
|
||||
// NOTE: This method assumes the supplied array is internally allocated, and so an in-place sort
|
||||
// is allowable
|
||||
private static string build_sparse_range(int64[] msg_nums) {
|
||||
assert(msg_nums.length > 0);
|
||||
private static string build_sparse_range(int64[] seq_nums) {
|
||||
assert(seq_nums.length > 0);
|
||||
|
||||
// sort array to search for spans
|
||||
qsort(msg_nums, msg_nums.length, sizeof(int64), Numeric.int64_compare);
|
||||
qsort(seq_nums, seq_nums.length, sizeof(int64), Numeric.int64_compare);
|
||||
|
||||
int64 start_of_span = -1;
|
||||
int64 last_msg_num = -1;
|
||||
int64 last_seq_num = -1;
|
||||
int span_count = 0;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
foreach (int64 msg_num in msg_nums) {
|
||||
assert(msg_num >= 0);
|
||||
foreach (int64 seq_num in seq_nums) {
|
||||
assert(seq_num >= 0);
|
||||
|
||||
// the first number is automatically the start of a span, although it may be a span of one
|
||||
// (start_of_span < 0 should only happen on first iteration; can't easily break out of
|
||||
// loop because foreach/Iterator would still require a special case to skip it)
|
||||
if (start_of_span < 0) {
|
||||
// start of first span
|
||||
builder.append(msg_num.to_string());
|
||||
builder.append(seq_num.to_string());
|
||||
|
||||
start_of_span = msg_num;
|
||||
start_of_span = seq_num;
|
||||
span_count = 1;
|
||||
} else if ((start_of_span + span_count) == msg_num) {
|
||||
} else if ((start_of_span + span_count) == seq_num) {
|
||||
// span continues
|
||||
span_count++;
|
||||
} else {
|
||||
|
|
@ -198,40 +212,40 @@ public class Geary.Imap.MessageSet : BaseObject {
|
|||
|
||||
// span ends, another begins
|
||||
if (span_count == 1) {
|
||||
builder.append_printf(",%s", msg_num.to_string());
|
||||
builder.append_printf(",%s", seq_num.to_string());
|
||||
} else if (span_count == 2) {
|
||||
builder.append_printf(",%s,%s", (start_of_span + 1).to_string(),
|
||||
msg_num.to_string());
|
||||
seq_num.to_string());
|
||||
} else {
|
||||
builder.append_printf(":%s,%s", (start_of_span + span_count - 1).to_string(),
|
||||
msg_num.to_string());
|
||||
seq_num.to_string());
|
||||
}
|
||||
|
||||
start_of_span = msg_num;
|
||||
start_of_span = seq_num;
|
||||
span_count = 1;
|
||||
}
|
||||
|
||||
last_msg_num = msg_num;
|
||||
last_seq_num = seq_num;
|
||||
}
|
||||
|
||||
// there should always be one msg_num in sorted, so the loop should exit with some state
|
||||
// there should always be one seq_num in sorted, so the loop should exit with some state
|
||||
assert(start_of_span >= 0);
|
||||
assert(span_count > 0);
|
||||
assert(last_msg_num >= 0);
|
||||
assert(last_seq_num >= 0);
|
||||
|
||||
// look for open-ended span
|
||||
if (span_count == 2)
|
||||
builder.append_printf(",%s", last_msg_num.to_string());
|
||||
builder.append_printf(",%s", last_seq_num.to_string());
|
||||
else
|
||||
builder.append_printf(":%s", last_msg_num.to_string());
|
||||
builder.append_printf(":%s", last_seq_num.to_string());
|
||||
|
||||
return builder.str;
|
||||
}
|
||||
|
||||
private static int64[] msg_array_to_int64(int[] msg_nums) {
|
||||
private static int64[] seq_array_to_int64(SequenceNumber[] seq_nums) {
|
||||
int64[] ret = new int64[0];
|
||||
foreach (int num in msg_nums)
|
||||
ret += (int64) num;
|
||||
foreach (SequenceNumber seq_num in seq_nums)
|
||||
ret += (int64) seq_num.value;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -252,6 +266,10 @@ public class Geary.Imap.MessageSet : BaseObject {
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link MessageSet} as a {@link Parameter} suitable for inclusion in a
|
||||
* {@link Command}.
|
||||
*/
|
||||
public Parameter to_parameter() {
|
||||
// Message sets are not quoted, even if they use an atom-special character (this *might*
|
||||
// be a Gmailism...)
|
||||
|
|
|
|||
40
src/engine/imap/message/imap-sequence-number.vala
Normal file
40
src/engine/imap/message/imap-sequence-number.vala
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* Copyright 2013 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A representation of IMAP's sequence number, i.e. positional addressing within a mailbox.
|
||||
*
|
||||
* See [[http://tools.ietf.org/html/rfc3501#section-2.3.1.2]]
|
||||
*
|
||||
* @see UID
|
||||
*/
|
||||
|
||||
public class Geary.Imap.SequenceNumber : Geary.MessageData.IntMessageData, Geary.Imap.MessageData,
|
||||
Gee.Comparable<SequenceNumber> {
|
||||
public SequenceNumber(int value) {
|
||||
base (value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array of ints into an array of {@link SequenceNumber}s.
|
||||
*/
|
||||
public static SequenceNumber[] to_list(int[] value_array) {
|
||||
SequenceNumber[] list = new SequenceNumber[0];
|
||||
foreach (int value in value_array)
|
||||
list += new SequenceNumber(value);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public virtual int compare_to(SequenceNumber other) {
|
||||
return value - other.value;
|
||||
}
|
||||
|
||||
public string serialize() {
|
||||
return value.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4,6 +4,14 @@
|
|||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A representation of the types of data to be found in a STATUS response.
|
||||
*
|
||||
* See [[http://tools.ietf.org/html/rfc3501#section-7.2.4]]
|
||||
*
|
||||
* @see StatusData
|
||||
*/
|
||||
|
||||
public enum Geary.Imap.StatusDataType {
|
||||
MESSAGES,
|
||||
RECENT,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,14 @@
|
|||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The decoded response to a STATUS command.
|
||||
*
|
||||
* See [[http://tools.ietf.org/html/rfc3501#section-7.2.4]]
|
||||
*
|
||||
* @see StatusCommand
|
||||
*/
|
||||
|
||||
public class Geary.Imap.StatusData : Object {
|
||||
// NOTE: This must be negative one; other values won't work well due to how the values are
|
||||
// decoded
|
||||
|
|
@ -15,18 +23,33 @@ public class Geary.Imap.StatusData : Object {
|
|||
* See {@link MailboxParameter} for the encoded version of this string.
|
||||
*/
|
||||
public string mailbox { get; private set; }
|
||||
|
||||
/**
|
||||
* UNSET if not set.
|
||||
* {@link UNSET} if not set.
|
||||
*/
|
||||
public int messages { get; private set; }
|
||||
|
||||
/**
|
||||
* UNSET if not set.
|
||||
* {@link UNSET} if not set.
|
||||
*/
|
||||
public int recent { get; private set; }
|
||||
public UID? uid_next { get; private set; }
|
||||
public UIDValidity? uid_validity { get; private set; }
|
||||
|
||||
/**
|
||||
* UNSET if not set.
|
||||
* The UIDNEXT of the mailbox, if returned.
|
||||
*
|
||||
* See [[http://tools.ietf.org/html/rfc3501#section-2.3.1.1]]
|
||||
*/
|
||||
public UID? uid_next { get; private set; }
|
||||
|
||||
/**
|
||||
* The UIDVALIDITY of the mailbox, if returned.
|
||||
*
|
||||
* See [[http://tools.ietf.org/html/rfc3501#section-2.3.1.1]]
|
||||
*/
|
||||
public UIDValidity? uid_validity { get; private set; }
|
||||
|
||||
/**
|
||||
* {@link UNSET} if not set.
|
||||
*/
|
||||
public int unseen { get; private set; }
|
||||
|
||||
|
|
@ -40,12 +63,23 @@ public class Geary.Imap.StatusData : Object {
|
|||
this.unseen = unseen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes {@link ServerData} into a StatusData representation.
|
||||
*
|
||||
* The ServerData must be the response to a STATUS command.
|
||||
*
|
||||
* @see StatusCommand
|
||||
* @see ServerData.get_status
|
||||
*/
|
||||
public static StatusData decode(ServerData server_data) throws ImapError {
|
||||
if (!server_data.get_as_string(1).equals_ci(StatusCommand.NAME)) {
|
||||
throw new ImapError.PARSE_ERROR("Bad STATUS command name in response \"%s\"",
|
||||
server_data.to_string());
|
||||
}
|
||||
|
||||
MailboxParameter mailbox_param = new MailboxParameter.from_string_parameter(
|
||||
server_data.get_as_string(2));
|
||||
|
||||
int messages = UNSET;
|
||||
int recent = UNSET;
|
||||
UID? uid_next = null;
|
||||
|
|
@ -92,8 +126,14 @@ public class Geary.Imap.StatusData : Object {
|
|||
}
|
||||
}
|
||||
|
||||
return new StatusData(server_data.get_as_string(2).value, messages, recent, uid_next,
|
||||
uid_validity, unseen);
|
||||
return new StatusData(mailbox_param.decode(), messages, recent, uid_next, uid_validity,
|
||||
unseen);
|
||||
}
|
||||
|
||||
public string to_string() {
|
||||
return "%s/%d/UIDNEXT=%s/UIDVALIDITY=%s".printf(mailbox, messages,
|
||||
(uid_next != null) ? uid_next.to_string() : "(none)",
|
||||
(uid_validity != null) ? uid_validity.to_string() : "(none)");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,17 @@
|
|||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A representation of an IMAP command tag.
|
||||
*
|
||||
* Tags are assigned by the client for each {@link Command} it sends to the server. Tags have
|
||||
* a general form of <a-z><000-999>, although that's only by convention and is not required.
|
||||
*
|
||||
* Special tags exist, namely to indicated an untagged response and continuations.
|
||||
*
|
||||
* See [[http://tools.ietf.org/html/rfc3501#section-2.2.1]]
|
||||
*/
|
||||
|
||||
public class Geary.Imap.Tag : StringParameter, Gee.Hashable<Geary.Imap.Tag> {
|
||||
public const string UNTAGGED_VALUE = "*";
|
||||
public const string CONTINUATION_VALUE = "+";
|
||||
|
|
|
|||
25
src/engine/imap/message/imap-uid-validity.vala
Normal file
25
src/engine/imap/message/imap-uid-validity.vala
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* Copyright 2013 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A representation of IMAP's UIDVALIDITY.
|
||||
*
|
||||
* See [[tools.ietf.org/html/rfc3501#section-2.3.1.1]]
|
||||
*
|
||||
* @see UID
|
||||
*/
|
||||
|
||||
public class Geary.Imap.UIDValidity : Geary.MessageData.Int64MessageData, Geary.Imap.MessageData {
|
||||
// Using statics because int32.MAX is static, not const (??)
|
||||
public static int64 MIN = 1;
|
||||
public static int64 MAX = int32.MAX;
|
||||
public static int64 INVALID = -1;
|
||||
|
||||
public UIDValidity(int64 value) {
|
||||
base (value);
|
||||
}
|
||||
}
|
||||
|
||||
73
src/engine/imap/message/imap-uid.vala
Normal file
73
src/engine/imap/message/imap-uid.vala
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/* Copyright 2013 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* An IMAP UID.
|
||||
*
|
||||
* See [[tools.ietf.org/html/rfc3501#section-2.3.1.1]]
|
||||
*
|
||||
* @see SequenceNumber
|
||||
*/
|
||||
|
||||
public class Geary.Imap.UID : Geary.MessageData.Int64MessageData, Geary.Imap.MessageData,
|
||||
Gee.Comparable<Geary.Imap.UID> {
|
||||
// Using statics because int32.MAX is static, not const (??)
|
||||
public static int64 MIN = 1;
|
||||
public static int64 MAX = int32.MAX;
|
||||
public static int64 INVALID = -1;
|
||||
|
||||
public UID(int64 value) {
|
||||
base (value);
|
||||
}
|
||||
|
||||
public bool is_valid() {
|
||||
return is_value_valid(value);
|
||||
}
|
||||
|
||||
public static bool is_value_valid(int64 val) {
|
||||
return Numeric.int64_in_range_inclusive(val, MIN, MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a valid UID, which means returning MIN or MAX if the value is out of range (either
|
||||
* direction) or MAX if this value is already MAX.
|
||||
*/
|
||||
public UID next() {
|
||||
if (value < MIN)
|
||||
return new UID(MIN);
|
||||
else if (value > MAX)
|
||||
return new UID(MAX);
|
||||
else
|
||||
return new UID(Numeric.int64_ceiling(value + 1, MAX));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a valid UID, which means returning MIN or MAX if the value is out of range (either
|
||||
* direction) or MIN if this value is already MIN.
|
||||
*/
|
||||
public UID previous() {
|
||||
if (value < MIN)
|
||||
return new UID(MIN);
|
||||
else if (value > MAX)
|
||||
return new UID(MAX);
|
||||
else
|
||||
return new UID(Numeric.int64_floor(value - 1, MIN));
|
||||
}
|
||||
|
||||
public virtual int compare_to(Geary.Imap.UID other) {
|
||||
if (value < other.value)
|
||||
return -1;
|
||||
else if (value > other.value)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
public string serialize() {
|
||||
return value.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -83,15 +83,15 @@ public class Geary.Imap.ServerData : ServerResponse {
|
|||
}
|
||||
|
||||
/**
|
||||
* Parses the {@link ServerData} into an expunged {@link MessageNumber}, if possible.
|
||||
* Parses the {@link ServerData} into an expunged {@link SequenceNumber}, if possible.
|
||||
*
|
||||
* @throws ImapError.INVALID if not an expunged MessageNumber.
|
||||
*/
|
||||
public MessageNumber get_expunge() throws ImapError {
|
||||
public SequenceNumber get_expunge() throws ImapError {
|
||||
if (server_data_type != ServerDataType.EXPUNGE)
|
||||
throw new ImapError.INVALID("Not EXPUNGE data: %s", to_string());
|
||||
|
||||
return new MessageNumber(get_as_string(1).as_int());
|
||||
return new SequenceNumber(get_as_string(1).as_int());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
|
||||
public signal void exists(int count);
|
||||
|
||||
public signal void expunge(MessageNumber msg_num);
|
||||
public signal void expunge(SequenceNumber seq_num);
|
||||
|
||||
public signal void fetch(FetchedData fetched_data);
|
||||
|
||||
|
|
@ -1304,9 +1304,6 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
|
||||
assert(completion_response != null);
|
||||
|
||||
if (completion_response.status != Status.OK)
|
||||
throw new ImapError.SERVER_ERROR("Command %s failed: %s", cmd.name, completion_response.to_string());
|
||||
|
||||
return completion_response;
|
||||
}
|
||||
|
||||
|
|
@ -1387,8 +1384,7 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
// update ClientSession capabilities before firing signal, so external signal
|
||||
// handlers that refer back to property aren't surprised
|
||||
capabilities = server_data.get_capabilities(ref next_capabilities_revision);
|
||||
debug("[%s] #%d %s", to_string(), next_capabilities_revision,
|
||||
capabilities.to_string());
|
||||
debug("[%s] %s", to_string(), capabilities.to_string());
|
||||
|
||||
capability(capabilities);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -6,13 +6,15 @@
|
|||
|
||||
/**
|
||||
* The Deserializer performs asynchronous I/O on a supplied input stream and transforms the raw
|
||||
* bytes into IMAP Parameters (which can then be converted into ServerResponses or ServerData).
|
||||
* The Deserializer will only begin reading from the stream when start_async() is called. Calling
|
||||
* stop_async() will halt reading without closing the stream itself. A Deserializer may not be
|
||||
* reused once stop_async() has been invoked.
|
||||
* bytes into IMAP {@link Parameter}s (which can then be converted into {@link ServerResponse}s or
|
||||
* {@link ServerData}).
|
||||
*
|
||||
* The Deserializer will only begin reading from the stream when {@link start_async} is called.
|
||||
* Calling {@link stop_async} will halt reading without closing the stream itself. A Deserializer
|
||||
* may not be reused once stop_async has been invoked.
|
||||
*
|
||||
* Since all results from the Deserializer are reported via signals, those signals should be
|
||||
* connected to prior to calling start_async(), or the caller risks missing early messages. (Note
|
||||
* connected to prior to calling start_async, or the caller risks missing early messages. (Note
|
||||
* that since Deserializer uses async I/O, this isn't technically possible unless the signals are
|
||||
* connected after the Idle loop has a chance to run; however, this is an implementation detail and
|
||||
* shouldn't be relied upon.)
|
||||
|
|
@ -81,25 +83,34 @@ public class Geary.Imap.Deserializer : BaseObject {
|
|||
private char[] atom_specials_exceptions = { ' ', ' ', '\0' };
|
||||
|
||||
/**
|
||||
* Fired when a complete set of Parameters have been received. Note that RootParameters may
|
||||
* contain StringParameters, ListParameters, NilParameters, and so on. One special Parameter
|
||||
* decoded by Deserializer is ResponseCode, which is structured internally as a ListParameter
|
||||
* subclass for convenience when decoding.
|
||||
* Fired when a complete set of IMAP {@link Parameter}s have been received.
|
||||
*
|
||||
* Note that {@link RootParameters} may contain {@link StringParameter}s, {@link ListParameter}s,
|
||||
* {@link NilParameter}s, and so on. One special Parameter decoded by Deserializer is
|
||||
* {@link ResponseCode}, which is structured internally as a ListParameter subclass for
|
||||
* convenience when decoding and can be deduced at the syntax level.
|
||||
*/
|
||||
public signal void parameters_ready(RootParameters root);
|
||||
|
||||
/**
|
||||
* "eos" is fired when the underlying InputStream is closed, whether due to normal EOS or input
|
||||
* error. Subscribe to "receive-failure" to be notified of errors.
|
||||
* Fired when the underlying InputStream is closed, whether due to normal EOS or input error.
|
||||
*
|
||||
* @see receive_failure
|
||||
*/
|
||||
public signal void eos();
|
||||
|
||||
/**
|
||||
* Fired when an Error is trapped on the input stream.
|
||||
*
|
||||
* This is nonrecoverable and means the stream should be closed and this Deserializer destroyed.
|
||||
*/
|
||||
public signal void receive_failure(Error err);
|
||||
|
||||
/**
|
||||
* "data-received" is fired as data blocks are received during download. The bytes themselves
|
||||
* may be partial and unusable out of context, so they're not provided, but their size is, to allow
|
||||
* monitoring of speed and such.
|
||||
* Fired as data blocks are received during download.
|
||||
*
|
||||
* The bytes themselves may be partial and unusable out of context, so they're not provided,
|
||||
* but their size is, to allow monitoring of speed and such.
|
||||
*
|
||||
* Note that this is fired for both line data (i.e. responses, status, etc.) and literal data
|
||||
* (block transfers).
|
||||
|
|
@ -109,6 +120,12 @@ public class Geary.Imap.Deserializer : BaseObject {
|
|||
*/
|
||||
public signal void bytes_received(size_t bytes);
|
||||
|
||||
/**
|
||||
* Fired when a syntax error has occurred.
|
||||
*
|
||||
* This generally means the data looks like garbage and further deserialization is unlikely
|
||||
* or impossible.
|
||||
*/
|
||||
public signal void deserialize_failure();
|
||||
|
||||
public Deserializer(InputStream ins) {
|
||||
|
|
@ -174,10 +191,20 @@ public class Geary.Imap.Deserializer : BaseObject {
|
|||
fsm = new Geary.State.Machine(machine_desc, mappings, on_bad_transition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a custom Converter into the input stream.
|
||||
*
|
||||
* Can be used for decompression, decryption, and so on.
|
||||
*/
|
||||
public bool install_converter(Converter converter) {
|
||||
return midstream.install(converter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin deserializing IMAP responses from the input stream.
|
||||
*
|
||||
* Subscribe to the various signals before starting to ensure that all responses are trapped.
|
||||
*/
|
||||
public async void start_async(int priority = GLib.Priority.DEFAULT) throws Error {
|
||||
if (cancellable != null)
|
||||
throw new EngineError.ALREADY_OPEN("Deserializer already open");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue