Expanded and improved FETCH response decoding.
Library now uses an extensible system for decoding the various structured data that FETCH returns. This system may be expanded later for decoding other CommandResponses (although FETCH is the command most in need of this). Also created an RFC822 directory for data structures and code to deal with mail messages and a common directory for code that is used throughout the library but doesn't belong to util as they're "core" classes.
This commit is contained in:
parent
4778771c3d
commit
e2d339dc4f
17 changed files with 764 additions and 45 deletions
6
Makefile
6
Makefile
|
|
@ -12,6 +12,7 @@ ENGINE_SRC := \
|
|||
src/engine/state/Machine.vala \
|
||||
src/engine/state/MachineDescriptor.vala \
|
||||
src/engine/state/Mapping.vala \
|
||||
src/engine/common/MessageData.vala \
|
||||
src/engine/imap/ClientConnection.vala \
|
||||
src/engine/imap/ClientSession.vala \
|
||||
src/engine/imap/Mailbox.vala \
|
||||
|
|
@ -26,10 +27,15 @@ ENGINE_SRC := \
|
|||
src/engine/imap/ServerData.vala \
|
||||
src/engine/imap/Status.vala \
|
||||
src/engine/imap/CommandResponse.vala \
|
||||
src/engine/imap/FetchResults.vala \
|
||||
src/engine/imap/FetchDataDecoder.vala \
|
||||
src/engine/imap/MessageData.vala \
|
||||
src/engine/imap/Serializable.vala \
|
||||
src/engine/imap/Serializer.vala \
|
||||
src/engine/imap/Deserializer.vala \
|
||||
src/engine/imap/Error.vala \
|
||||
src/engine/rfc822/MailboxAddress.vala \
|
||||
src/engine/rfc822/MessageData.vala \
|
||||
src/engine/util/string.vala
|
||||
|
||||
CONSOLE_SRC := \
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ class ImapConsole : Gtk.Window {
|
|||
string[] args = new string[0];
|
||||
for (int ctr = 1; ctr < tokens.length; ctr++) {
|
||||
string arg = tokens[ctr].strip();
|
||||
if (!is_empty_string(arg))
|
||||
if (!String.is_empty(arg))
|
||||
args += arg;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,12 @@
|
|||
|
||||
public class Geary.Message {
|
||||
public int msg_num { get; private set; }
|
||||
public string from { get; private set; }
|
||||
public string subject { get; private set; }
|
||||
public string sent { 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 Message(int msg_num, string from, string subject, string sent) {
|
||||
public Message(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;
|
||||
|
|
@ -18,7 +19,7 @@ public class Geary.Message {
|
|||
}
|
||||
|
||||
public string to_string() {
|
||||
return "[%d] %s: %s (%s)".printf(msg_num, from, subject, sent);
|
||||
return "[%d] %s: %s (%s)".printf(msg_num, from.to_string(), subject.to_string(), sent.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
73
src/engine/common/MessageData.vala
Normal file
73
src/engine/common/MessageData.vala
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Common.MessageData is an abstract base class to unify the various message-related data and
|
||||
* metadata that may be associated with a mail message, whether it's embedded in its MIME
|
||||
* structure, its RFC822 header, IMAP metadata, or details from a POP server.
|
||||
*/
|
||||
|
||||
public abstract class Geary.Common.MessageData {
|
||||
/**
|
||||
* to_string() is intended for debugging and logging purposes, not user-visible text or
|
||||
* serialization.
|
||||
*/
|
||||
public abstract string to_string();
|
||||
|
||||
/**
|
||||
* emit() is intended for sending the data on the wire in a textual format.
|
||||
*/
|
||||
public abstract string emit();
|
||||
}
|
||||
|
||||
public abstract class Geary.Common.StringMessageData : Geary.Common.MessageData {
|
||||
public string value { get; private set; }
|
||||
|
||||
public StringMessageData(string value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public override string to_string() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public override string emit() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class Geary.Common.IntMessageData : Geary.Common.MessageData {
|
||||
public int value { get; private set; }
|
||||
|
||||
public IntMessageData(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public override string to_string() {
|
||||
return value.to_string();
|
||||
}
|
||||
|
||||
public override string emit() {
|
||||
return value.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class Geary.Common.LongMessageData : Geary.Common.MessageData {
|
||||
public long value { get; private set; }
|
||||
|
||||
public LongMessageData(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public override string to_string() {
|
||||
return value.to_string();
|
||||
}
|
||||
|
||||
public override string emit() {
|
||||
return value.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -279,7 +279,7 @@ public class Geary.Imap.ClientSession : Object, Geary.Account {
|
|||
};
|
||||
|
||||
fsm = new Geary.State.Machine(machine_desc, mappings, on_ignored_transition);
|
||||
fsm.set_logging(true);
|
||||
fsm.set_logging(false);
|
||||
}
|
||||
|
||||
public Tag? generate_tag() {
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ public class Geary.Imap.Deserializer {
|
|||
}
|
||||
|
||||
private bool is_current_string_empty() {
|
||||
return (current_string == null) || is_empty_string(current_string.str);
|
||||
return (current_string == null) || String.is_empty(current_string.str);
|
||||
}
|
||||
|
||||
private void append_to_string(unichar ch) {
|
||||
|
|
@ -279,7 +279,11 @@ public class Geary.Imap.Deserializer {
|
|||
if (is_current_string_empty())
|
||||
return;
|
||||
|
||||
save_parameter(new StringParameter(current_string.str));
|
||||
if (NilParameter.is_nil(current_string.str))
|
||||
save_parameter(NilParameter.instance);
|
||||
else
|
||||
save_parameter(new StringParameter(current_string.str));
|
||||
|
||||
current_string = null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
// TODO: Support body[section]<partial> and body.peek[section]<partial> forms
|
||||
public enum Geary.Imap.FetchDataItem {
|
||||
UID,
|
||||
FLAGS,
|
||||
|
|
@ -118,6 +119,29 @@ public enum Geary.Imap.FetchDataItem {
|
|||
public static FetchDataItem from_parameter(StringParameter strparam) throws ImapError {
|
||||
return decode(strparam.value);
|
||||
}
|
||||
|
||||
public FetchDataDecoder? get_decoder() {
|
||||
switch (this) {
|
||||
case UID:
|
||||
return new UIDDecoder();
|
||||
|
||||
case FLAGS:
|
||||
return new FlagsDecoder();
|
||||
|
||||
case ENVELOPE:
|
||||
return new EnvelopeDecoder();
|
||||
|
||||
case INTERNALDATE:
|
||||
return new InternalDateDecoder();
|
||||
|
||||
case RFC822_SIZE:
|
||||
return new RFC822SizeDecoder();
|
||||
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.FetchCommand : Command {
|
||||
|
|
|
|||
149
src/engine/imap/FetchDataDecoder.vala
Normal file
149
src/engine/imap/FetchDataDecoder.vala
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* FetchDataDecoder accept the result line of a FETCH response and decodes it into MessageData.
|
||||
* While they can be used standalone, they're intended to be used by FetchResults to process
|
||||
* a CommandResponse.
|
||||
*
|
||||
* Note that FetchDataDecoders are keyed off of FetchDataItem; new implementations should add
|
||||
* themselves to FetchDataItem.get_decoder().
|
||||
*
|
||||
* In the future FetchDataDecoder may be used to decode MessageData stored in other formats, such
|
||||
* as in a database.
|
||||
*/
|
||||
|
||||
public abstract class Geary.Imap.FetchDataDecoder {
|
||||
public FetchDataItem data_item { get; private set; }
|
||||
|
||||
public FetchDataDecoder(FetchDataItem data_item) {
|
||||
this.data_item = data_item;
|
||||
}
|
||||
|
||||
// The default implementation determines the type of the parameter and calls the appropriate
|
||||
// virtual function; most implementations of a FetchResponseDecoder shouldn't need to override
|
||||
// this method.
|
||||
public virtual MessageData decode(Parameter param) throws ImapError {
|
||||
StringParameter? stringp = param as StringParameter;
|
||||
if (stringp != null)
|
||||
return decode_string(stringp);
|
||||
|
||||
ListParameter? listp = param as ListParameter;
|
||||
if (listp != null)
|
||||
return decode_list(listp);
|
||||
|
||||
LiteralParameter? literalp = param as LiteralParameter;
|
||||
if (literalp != null)
|
||||
return decode_literal(literalp);
|
||||
|
||||
throw new ImapError.TYPE_ERROR("Data item \"%s\" of unknown type", param.to_string());
|
||||
}
|
||||
|
||||
public virtual MessageData decode_string(StringParameter param) throws ImapError {
|
||||
throw new ImapError.TYPE_ERROR("%s does not accept a string parameter", data_item.to_string());
|
||||
}
|
||||
|
||||
public virtual MessageData decode_list(ListParameter list) throws ImapError {
|
||||
throw new ImapError.TYPE_ERROR("%s does not accept a list parameter", data_item.to_string());
|
||||
}
|
||||
|
||||
public virtual MessageData decode_literal(LiteralParameter literal) throws ImapError {
|
||||
throw new ImapError.TYPE_ERROR("%s does not accept a literal parameter", data_item.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.UIDDecoder : Geary.Imap.FetchDataDecoder {
|
||||
public UIDDecoder() {
|
||||
base (FetchDataItem.UID);
|
||||
}
|
||||
|
||||
public override MessageData decode_string(StringParameter stringp) throws ImapError {
|
||||
return new UID(stringp.as_int());
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.FlagsDecoder : Geary.Imap.FetchDataDecoder {
|
||||
public FlagsDecoder() {
|
||||
base (FetchDataItem.FLAGS);
|
||||
}
|
||||
|
||||
public override MessageData decode_list(ListParameter listp) throws ImapError {
|
||||
Gee.List<Flag> flags = new Gee.ArrayList<Flag>();
|
||||
for (int ctr = 0; ctr < listp.get_count(); ctr++)
|
||||
flags.add(new Flag(listp.get_as_string(ctr).value));
|
||||
|
||||
return new Flags(flags);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.InternalDateDecoder : Geary.Imap.FetchDataDecoder {
|
||||
public InternalDateDecoder() {
|
||||
base (FetchDataItem.INTERNALDATE);
|
||||
}
|
||||
|
||||
public override MessageData decode_string(StringParameter stringp) throws ImapError {
|
||||
return new InternalDate(stringp.value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.RFC822SizeDecoder : Geary.Imap.FetchDataDecoder {
|
||||
public RFC822SizeDecoder() {
|
||||
base (FetchDataItem.RFC822_SIZE);
|
||||
}
|
||||
|
||||
public override MessageData decode_string(StringParameter stringp) throws ImapError {
|
||||
return new RFC822Size(stringp.as_long());
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
|
||||
public EnvelopeDecoder() {
|
||||
base (FetchDataItem.ENVELOPE);
|
||||
}
|
||||
|
||||
// TODO: This doesn't handle group lists (see Johnson, p.268)
|
||||
public override MessageData decode_list(ListParameter listp) throws ImapError {
|
||||
StringParameter sent = listp.get_as_string(0);
|
||||
StringParameter subject = listp.get_as_string(1);
|
||||
ListParameter from = listp.get_as_list(2);
|
||||
ListParameter sender = listp.get_as_list(3);
|
||||
ListParameter? reply_to = listp.get_as_list(4);
|
||||
ListParameter? to = listp.get_as_nullable_list(5);
|
||||
ListParameter? cc = listp.get_as_nullable_list(6);
|
||||
ListParameter? bcc = listp.get_as_nullable_list(7);
|
||||
StringParameter? in_reply_to = listp.get_as_nullable_string(8);
|
||||
StringParameter message_id = listp.get_as_string(9);
|
||||
|
||||
return new Envelope(new Geary.RFC822.Date(sent.value), new Geary.RFC822.Subject(subject.value),
|
||||
parse_addresses(from), parse_addresses(sender), parse_addresses(reply_to),
|
||||
(to != null) ? parse_addresses(to) : null,
|
||||
(cc != null) ? parse_addresses(cc) : null,
|
||||
(bcc != null) ? parse_addresses(bcc) : null,
|
||||
(in_reply_to != null) ? new Geary.RFC822.MessageID(in_reply_to.value) : null,
|
||||
new Geary.RFC822.MessageID(message_id.value));
|
||||
}
|
||||
|
||||
private Geary.RFC822.MailboxAddresses parse_addresses(ListParameter listp) throws ImapError {
|
||||
Gee.List<Geary.RFC822.MailboxAddress> list = new Gee.ArrayList<Geary.RFC822.MailboxAddress>();
|
||||
for (int ctr = 0; ctr < listp.get_count(); ctr++) {
|
||||
ListParameter fields = listp.get_as_list(ctr);
|
||||
StringParameter? name = fields.get_as_nullable_string(0);
|
||||
StringParameter? source_route = fields.get_as_nullable_string(1);
|
||||
StringParameter mailbox = fields.get_as_string(2);
|
||||
StringParameter domain = fields.get_as_string(3);
|
||||
|
||||
Geary.RFC822.MailboxAddress addr = new Geary.RFC822.MailboxAddress(
|
||||
(name != null) ? name.nullable_value : null,
|
||||
(source_route != null) ? source_route.nullable_value : null,
|
||||
mailbox.value,
|
||||
domain.value);
|
||||
list.add(addr);
|
||||
}
|
||||
|
||||
return new Geary.RFC822.MailboxAddresses(list);
|
||||
}
|
||||
}
|
||||
|
||||
76
src/engine/imap/FetchResults.vala
Normal file
76
src/engine/imap/FetchResults.vala
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* FetchResults represents the data returned from a FETCH response for each message. Since
|
||||
* FETCH allows for multiple FetchDataItems to be requested, this object can hold all of them.
|
||||
*
|
||||
* decode_command_response() will take a CommandResponse for a FETCH command and return all
|
||||
* results for all messages specified.
|
||||
*/
|
||||
|
||||
public class Geary.Imap.FetchResults {
|
||||
public int msg_num { get; private set; }
|
||||
|
||||
private Gee.Map<FetchDataItem, MessageData> map = new Gee.HashMap<FetchDataItem, MessageData>();
|
||||
|
||||
public FetchResults(int msg_num) {
|
||||
this.msg_num = msg_num;
|
||||
}
|
||||
|
||||
public static FetchResults decode(ServerData data) throws ImapError {
|
||||
StringParameter msg_num = (StringParameter) data.get_as(1, typeof(StringParameter));
|
||||
StringParameter cmd = (StringParameter) data.get_as(2, typeof(StringParameter));
|
||||
ListParameter list = (ListParameter) data.get_as(3, typeof(ListParameter));
|
||||
|
||||
// verify this is a FETCH response
|
||||
if (!cmd.equals_ci("fetch")) {
|
||||
throw new ImapError.TYPE_ERROR("Unable to decode fetch response \"%s\": Not marked as fetch response",
|
||||
data.to_string());
|
||||
}
|
||||
|
||||
FetchResults results = new FetchResults(msg_num.as_int());
|
||||
|
||||
// walk the list for each returned fetch data item, which is paired by its data item name
|
||||
// and the structured data itself
|
||||
for (int ctr = 0; ctr < list.get_count(); ctr += 2) {
|
||||
StringParameter data_item_param = (StringParameter) list.get_as(ctr, typeof(StringParameter));
|
||||
FetchDataItem data_item = FetchDataItem.decode(data_item_param.value);
|
||||
FetchDataDecoder? decoder = data_item.get_decoder();
|
||||
if (decoder == null) {
|
||||
debug("Unable to decode fetch response for \"%s\": No decoder available",
|
||||
data_item.to_string());
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
results.set_data(data_item, decoder.decode(list.get_required(ctr + 1)));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public static FetchResults[] decode_command_response(CommandResponse response) throws ImapError {
|
||||
FetchResults[] array = new FetchResults[0];
|
||||
foreach (ServerData data in response.server_data)
|
||||
array += decode(data);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public void set_data(FetchDataItem data_item, MessageData primitive) {
|
||||
map.set(data_item, primitive);
|
||||
}
|
||||
|
||||
public MessageData? get_data(FetchDataItem data_item) {
|
||||
return map.get(data_item);
|
||||
}
|
||||
|
||||
public int get_count() {
|
||||
return map.size;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -38,31 +38,11 @@ private class Geary.Imap.MessageStreamImpl : Object, Geary.MessageStream {
|
|||
throw new ImapError.SERVER_ERROR(resp.status_response.text);
|
||||
|
||||
Gee.List<Message> msgs = new Gee.ArrayList<Message>();
|
||||
foreach (ServerData data in resp.server_data) {
|
||||
StringParameter? label = data.get(2) as StringParameter;
|
||||
if (label == null || label.value.down() != "fetch") {
|
||||
debug("Not fetch data: %s", (label == null) ? "(null)" : label.value);
|
||||
continue;
|
||||
}
|
||||
|
||||
StringParameter msg_num = (StringParameter) data.get_as(1, typeof(StringParameter));
|
||||
ListParameter envelope = (ListParameter) data.get_as(3, typeof(ListParameter));
|
||||
|
||||
ListParameter fields = (ListParameter) envelope.get_as(1, typeof(ListParameter));
|
||||
|
||||
StringParameter date = (StringParameter) fields.get_as(0, typeof(StringParameter));
|
||||
StringParameter subject = (StringParameter) fields.get_as(1, typeof(StringParameter));
|
||||
|
||||
ListParameter from_fields = (ListParameter) fields.get_as(3, typeof(ListParameter));
|
||||
ListParameter first_from = (ListParameter) from_fields.get_as(0, typeof(ListParameter));
|
||||
StringParameter from_name = (StringParameter) first_from.get_as(0, typeof(StringParameter));
|
||||
StringParameter from_mailbox = (StringParameter) first_from.get_as(2, typeof(StringParameter));
|
||||
StringParameter from_domain = (StringParameter) first_from.get_as(3, typeof(StringParameter));
|
||||
|
||||
Message msg = new Message(int.parse(msg_num.value),
|
||||
"%s <%s@%s>".printf(from_name.value, from_mailbox.value, from_domain.value),
|
||||
subject.value, date.value);
|
||||
msgs.add(msg);
|
||||
|
||||
FetchResults[] results = FetchResults.decode_command_response(resp);
|
||||
foreach (FetchResults res in results) {
|
||||
Envelope envelope = (Envelope) res.get_data(FetchDataItem.ENVELOPE);
|
||||
msgs.add(new Message(res.msg_num, envelope.from, envelope.subject, envelope.sent));
|
||||
}
|
||||
|
||||
return msgs;
|
||||
|
|
|
|||
164
src/engine/imap/MessageData.vala
Normal file
164
src/engine/imap/MessageData.vala
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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). Obviously these classes are closely related, hence their presence
|
||||
* here.
|
||||
*
|
||||
* Also note that Imap.MessageData inherits from Common.MessageData.
|
||||
*/
|
||||
|
||||
public interface Geary.Imap.MessageData : Geary.Common.MessageData {
|
||||
}
|
||||
|
||||
public class Geary.Imap.UID : Geary.Common.IntMessageData, Geary.Imap.MessageData {
|
||||
public UID(int value) {
|
||||
base (value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.Flag {
|
||||
public static Flag ANSWERED = new Flag("\\answered");
|
||||
public static Flag DELETED = new Flag("\\deleted");
|
||||
public static Flag DRAFT = new Flag("\\draft");
|
||||
public static Flag FLAGGED = new Flag("\\flagged");
|
||||
public static Flag RECENT = new Flag("\\recent");
|
||||
public static Flag SEEN = new Flag("\\seen");
|
||||
|
||||
public string value { get; private set; }
|
||||
|
||||
public Flag(string value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public bool is_system() {
|
||||
return value[0] == '\\';
|
||||
}
|
||||
|
||||
public bool has_value(string value) {
|
||||
return this.value.down() == value.down();
|
||||
}
|
||||
|
||||
public bool equals(Flag flag) {
|
||||
return (flag == this) ? true : flag.has_value(value);
|
||||
}
|
||||
|
||||
public string to_string() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static uint hash_func(void *flag) {
|
||||
return str_hash(((Flag *) flag)->value);
|
||||
}
|
||||
|
||||
public static bool equal_func(void *a, void *b) {
|
||||
return ((Flag *) a)->equals((Flag *) b);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.Flags : Geary.Common.MessageData, Geary.Imap.MessageData {
|
||||
private Gee.Set<Flag> list;
|
||||
|
||||
public Flags(Gee.Collection<Flag> flags) {
|
||||
list = new Gee.HashSet<Flag>(Flag.hash_func, Flag.equal_func);
|
||||
list.add_all(flags);
|
||||
}
|
||||
|
||||
public bool contains(Flag flag) {
|
||||
return list.contains(flag);
|
||||
}
|
||||
|
||||
public Gee.Set<Flag> get_all() {
|
||||
return list.read_only_view;
|
||||
}
|
||||
|
||||
public override string to_string() {
|
||||
return emit();
|
||||
}
|
||||
|
||||
public override string emit() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
foreach (Flag flag in list) {
|
||||
if (!String.is_empty(builder.str))
|
||||
builder.append_c(' ');
|
||||
|
||||
builder.append(flag.value);
|
||||
}
|
||||
|
||||
return builder.str;
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.InternalDate : Geary.RFC822.Date, Geary.Imap.MessageData {
|
||||
public InternalDate(string value) {
|
||||
base (value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.RFC822Size : Geary.RFC822.Size, Geary.Imap.MessageData {
|
||||
public RFC822Size(long value) {
|
||||
base (value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.Envelope : Geary.Common.MessageData, Geary.Imap.MessageData {
|
||||
public Geary.RFC822.Date sent { get; private set; }
|
||||
public Geary.RFC822.Subject subject { get; private set; }
|
||||
public Geary.RFC822.MailboxAddresses from { get; private set; }
|
||||
public Geary.RFC822.MailboxAddresses sender { get; private set; }
|
||||
public Geary.RFC822.MailboxAddresses? reply_to { get; private set; }
|
||||
public Geary.RFC822.MailboxAddresses? to { get; private set; }
|
||||
public Geary.RFC822.MailboxAddresses? cc { get; private set; }
|
||||
public Geary.RFC822.MailboxAddresses? bcc { get; private set; }
|
||||
public Geary.RFC822.MessageID? in_reply_to { get; private set; }
|
||||
public Geary.RFC822.MessageID message_id { get; private set; }
|
||||
|
||||
public Envelope(Geary.RFC822.Date sent, Geary.RFC822.Subject subject,
|
||||
Geary.RFC822.MailboxAddresses from, Geary.RFC822.MailboxAddresses sender,
|
||||
Geary.RFC822.MailboxAddresses? reply_to, Geary.RFC822.MailboxAddresses? to,
|
||||
Geary.RFC822.MailboxAddresses? cc, Geary.RFC822.MailboxAddresses? bcc,
|
||||
Geary.RFC822.MessageID? in_reply_to, Geary.RFC822.MessageID message_id) {
|
||||
this.sent = sent;
|
||||
this.subject = subject;
|
||||
this.from = from;
|
||||
this.sender = sender;
|
||||
this.reply_to = reply_to;
|
||||
this.to = to;
|
||||
this.cc = cc;
|
||||
this.bcc = bcc;
|
||||
this.in_reply_to = in_reply_to;
|
||||
this.message_id = message_id;
|
||||
}
|
||||
|
||||
public override string to_string() {
|
||||
return "[%s] %s: \"%s\"".printf(sent.to_string(), from.to_string(), subject.to_string());
|
||||
}
|
||||
|
||||
public override string emit() {
|
||||
return to_string();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public class Geary.Imap.BodyStructure : Geary.Imap.Primitive {
|
||||
}
|
||||
|
||||
public class Geary.Imap.RFC822Header : Geary.Imap.Primitive {
|
||||
}
|
||||
|
||||
public class Geary.Imap.RFC822Text : Geary.Imap.Primitive {
|
||||
}
|
||||
|
||||
public class Geary.Imap.RFC822 : Geary.Imap.Primitive {
|
||||
}
|
||||
*/
|
||||
|
||||
|
|
@ -12,19 +12,65 @@ public abstract class Geary.Imap.Parameter : Object, Serializable {
|
|||
public abstract string to_string();
|
||||
}
|
||||
|
||||
public class Geary.Imap.NilParameter : Geary.Imap.Parameter {
|
||||
public const string VALUE = "NIL";
|
||||
|
||||
private static NilParameter? _instance = null;
|
||||
|
||||
public static NilParameter instance {
|
||||
get {
|
||||
if (_instance == null)
|
||||
_instance = new NilParameter();
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private NilParameter() {
|
||||
}
|
||||
|
||||
public static bool is_nil(string str) {
|
||||
return String.ascii_equali(VALUE, str);
|
||||
}
|
||||
|
||||
public override async void serialize(Serializer ser) throws Error {
|
||||
ser.push_nil();
|
||||
}
|
||||
|
||||
public override string to_string() {
|
||||
return VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.StringParameter : Geary.Imap.Parameter {
|
||||
public string value { get; private set; }
|
||||
public string? nullable_value {
|
||||
get {
|
||||
return String.is_empty(value) ? null : value;
|
||||
}
|
||||
}
|
||||
|
||||
public StringParameter(string value) requires (!is_empty_string(value)) {
|
||||
public StringParameter(string value) requires (!String.is_empty(value)) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public StringParameter.NIL() {
|
||||
this.value = "nil";
|
||||
public bool equals_cs(string value) {
|
||||
return this.value == value;
|
||||
}
|
||||
|
||||
public bool is_nil() {
|
||||
return value.down() == "nil";
|
||||
public bool equals_ci(string value) {
|
||||
return this.value.down() == value.down();
|
||||
}
|
||||
|
||||
// TODO: This does not check that the value is a properly-formed integer. This should be
|
||||
// added later.
|
||||
public int as_int() throws ImapError {
|
||||
return int.parse(value);
|
||||
}
|
||||
|
||||
// TODO: This does not check that the value is a properly-formed long.
|
||||
public long as_long() throws ImapError {
|
||||
return long.parse(value);
|
||||
}
|
||||
|
||||
public override string to_string() {
|
||||
|
|
@ -99,19 +145,61 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
|
|||
return list.get(index);
|
||||
}
|
||||
|
||||
public Parameter get_required(int index) throws ImapError {
|
||||
Parameter? param = list.get(index);
|
||||
if (param == null)
|
||||
throw new ImapError.TYPE_ERROR("No parameter at index %d", index);
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
public Parameter get_as(int index, Type type) throws ImapError {
|
||||
assert(type.is_a(typeof(Parameter)));
|
||||
|
||||
if (index >= list.size)
|
||||
throw new ImapError.TYPE_ERROR("No parameter at index %d", index);
|
||||
|
||||
Parameter param = list.get(index);
|
||||
Parameter param = get_required(index);
|
||||
if (!param.get_type().is_a(type))
|
||||
throw new ImapError.TYPE_ERROR("Parameter %d is not of type %s", index, type.name());
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
public Parameter? get_as_nullable(int index, Type type) throws ImapError {
|
||||
assert(type.is_a(typeof(Parameter)));
|
||||
|
||||
Parameter param = get_required(index);
|
||||
if (param is NilParameter)
|
||||
return null;
|
||||
|
||||
if (!param.get_type().is_a(type))
|
||||
throw new ImapError.TYPE_ERROR("Parameter %d is not of type %s", index, type.name());
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
public StringParameter get_as_string(int index) throws ImapError {
|
||||
return (StringParameter) get_as(index, typeof(StringParameter));
|
||||
}
|
||||
|
||||
public StringParameter? get_as_nullable_string(int index) throws ImapError {
|
||||
return (StringParameter?) get_as_nullable(index, typeof(StringParameter));
|
||||
}
|
||||
|
||||
public ListParameter get_as_list(int index) throws ImapError {
|
||||
return (ListParameter) get_as(index, typeof(ListParameter));
|
||||
}
|
||||
|
||||
public ListParameter? get_as_nullable_list(int index) throws ImapError {
|
||||
return (ListParameter?) get_as_nullable(index, typeof(ListParameter));
|
||||
}
|
||||
|
||||
public LiteralParameter get_as_literal(int index) throws ImapError {
|
||||
return (LiteralParameter) get_as(index, typeof(LiteralParameter));
|
||||
}
|
||||
|
||||
public LiteralParameter? get_as_nullable_literal(int index) throws ImapError {
|
||||
return (LiteralParameter?) get_as_nullable(index, typeof(LiteralParameter));
|
||||
}
|
||||
|
||||
public Gee.List<Parameter> get_all() {
|
||||
return list.read_only_view;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ public class Geary.Imap.Serializer {
|
|||
douts.put_byte(' ', null);
|
||||
}
|
||||
|
||||
public void push_nil() throws Error {
|
||||
douts.put_string(NilParameter.VALUE, null);
|
||||
}
|
||||
|
||||
public void push_eol() throws Error {
|
||||
douts.put_string("\r\n", null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public class Geary.Imap.StatusResponse : ServerResponse {
|
|||
start_index++;
|
||||
}
|
||||
|
||||
return !is_empty_string(builder.str) ? builder.str : null;
|
||||
return !String.is_empty(builder.str) ? builder.str : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
34
src/engine/rfc822/MailboxAddress.vala
Normal file
34
src/engine/rfc822/MailboxAddress.vala
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* Copyright 2011 Yorba Foundation
|
||||
*
|
||||
* This software is licensed under the GNU Lesser General Public License
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
public class Geary.RFC822.MailboxAddress {
|
||||
public string? name { get; private set; }
|
||||
public string? source_route { get; private set; }
|
||||
public string mailbox { get; private set; }
|
||||
public string domain { get; private set; }
|
||||
public string address { get; private set; }
|
||||
|
||||
public MailboxAddress(string? name, string? source_route, string mailbox, string domain) {
|
||||
this.name = name;
|
||||
this.source_route = source_route;
|
||||
this.mailbox = mailbox;
|
||||
this.domain = domain;
|
||||
this.address = "%s@%s".printf(mailbox, domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human-readable formatted address, showing the name (if available) and the email
|
||||
* address in angled brackets.
|
||||
*/
|
||||
public string get_full_address() {
|
||||
return String.is_empty(name) ? "<%s>".printf(address) : "%s <%s>".printf(name, address);
|
||||
}
|
||||
|
||||
public string to_string() {
|
||||
return get_full_address();
|
||||
}
|
||||
}
|
||||
|
||||
84
src/engine/rfc822/MessageData.vala
Normal file
84
src/engine/rfc822/MessageData.vala
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* RFC822.MessageData represents a base class for all the various elements that may be present in
|
||||
* an RFC822 message header. Note that some common elements (such as MailAccount) are not
|
||||
* MessageData because they exist in an RFC822 header in list (i.e. multiple email addresses) form.
|
||||
*/
|
||||
|
||||
public interface Geary.RFC822.MessageData : Geary.Common.MessageData {
|
||||
}
|
||||
|
||||
public class Geary.RFC822.MessageID : Geary.Common.StringMessageData, Geary.RFC822.MessageData {
|
||||
public MessageID(string value) {
|
||||
base (value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.RFC822.Date : Geary.Common.StringMessageData, Geary.RFC822.MessageData {
|
||||
public Date(string value) {
|
||||
base (value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.RFC822.Size : Geary.Common.LongMessageData, Geary.RFC822.MessageData {
|
||||
public Size(long value) {
|
||||
base (value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.RFC822.Subject : Geary.Common.StringMessageData, Geary.RFC822.MessageData {
|
||||
public Subject(string value) {
|
||||
base (value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.RFC822.MailboxAddresses : Geary.Common.MessageData, Geary.RFC822.MessageData {
|
||||
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 Gee.List<MailboxAddress> get_all() {
|
||||
return addrs.read_only_view;
|
||||
}
|
||||
|
||||
public override string to_string() {
|
||||
return addrs.size > 0 ? emit() : "(no addresses)";
|
||||
}
|
||||
|
||||
public override string emit() {
|
||||
switch (addrs.size) {
|
||||
case 0:
|
||||
return "";
|
||||
|
||||
case 1:
|
||||
return addrs[0].to_string();
|
||||
|
||||
default:
|
||||
StringBuilder builder = new StringBuilder();
|
||||
foreach (MailboxAddress addr in addrs) {
|
||||
if (!String.is_empty(builder.str))
|
||||
builder.append(", ");
|
||||
|
||||
builder.append(addr.to_string());
|
||||
}
|
||||
|
||||
return builder.str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4,7 +4,39 @@
|
|||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
public inline bool is_empty_string(string? str) {
|
||||
namespace String {
|
||||
|
||||
public inline bool is_empty(string? str) {
|
||||
return (str == null || str[0] == 0);
|
||||
}
|
||||
|
||||
public int ascii_cmp(string a, string b) {
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
||||
public int ascii_cmpi(string a, string b) {
|
||||
char *aptr = a;
|
||||
char *bptr = b;
|
||||
for (;;) {
|
||||
int diff = *aptr - *bptr;
|
||||
if (diff != 0)
|
||||
return diff;
|
||||
|
||||
if (*aptr == '\0')
|
||||
return 0;
|
||||
|
||||
aptr++;
|
||||
bptr++;
|
||||
}
|
||||
}
|
||||
|
||||
public inline bool ascii_equal(string a, string b) {
|
||||
return ascii_cmp(a, b) == 0;
|
||||
}
|
||||
|
||||
public inline bool ascii_equali(string a, string b) {
|
||||
return ascii_cmpi(a, b) == 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue