Broke out Parameters, misc. other fixes

This commit is contained in:
Jim Nelson 2013-06-05 18:24:42 -07:00
parent 1f71f02a9d
commit ce45d19b9a
27 changed files with 666 additions and 350 deletions

View file

@ -96,12 +96,20 @@ engine/imap/message/imap-flag.vala
engine/imap/message/imap-mailbox-specifier.vala
engine/imap/message/imap-mailbox-parameter.vala
engine/imap/message/imap-message-data.vala
engine/imap/message/imap-parameter.vala
engine/imap/message/imap-sequence-number.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/parameter/imap-atom-parameter.vala
engine/imap/parameter/imap-list-parameter.vala
engine/imap/parameter/imap-literal-parameter.vala
engine/imap/parameter/imap-nil-parameter.vala
engine/imap/parameter/imap-parameter.vala
engine/imap/parameter/imap-quoted-string-parameter.vala
engine/imap/parameter/imap-root-parameters.vala
engine/imap/parameter/imap-string-parameter.vala
engine/imap/parameter/imap-unquoted-string-parameter.vala
engine/imap/response/imap-capabilities.vala
engine/imap/response/imap-continuation-response.vala
engine/imap/response/imap-fetch-body-data-identifier.vala

View file

@ -290,8 +290,10 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
if (remote_children != null) {
foreach (Imap.Folder remote_child in remote_children) {
result.set(remote_child.path, remote_child);
Collection.map_set_all<FolderPath, Imap.Folder>(result,
yield enumerate_remote_folders_async(remote_child.path, cancellable));
if (remote_child.properties.has_children.is_possible()) {
Collection.map_set_all<FolderPath, Imap.Folder>(result,
yield enumerate_remote_folders_async(remote_child.path, cancellable));
}
}
}

View file

@ -117,8 +117,6 @@ public class Geary.Imap.FolderProperties : Geary.FolderProperties {
}
private void init_flags() {
supports_children = Trillian.from_boolean(!attrs.contains(MailboxAttribute.NO_INFERIORS));
// \HasNoChildren & \HasChildren are optional attributes (could check for CHILDREN extension,
// but unnecessary here)
if (attrs.contains(MailboxAttribute.HAS_NO_CHILDREN))
@ -128,6 +126,12 @@ public class Geary.Imap.FolderProperties : Geary.FolderProperties {
else
has_children = Trillian.UNKNOWN;
// has_children implies supports_children
if (has_children != Trillian.UNKNOWN)
supports_children = has_children;
else
supports_children = Trillian.from_boolean(!attrs.contains(MailboxAttribute.NO_INFERIORS));
is_openable = Trillian.from_boolean(!attrs.contains(MailboxAttribute.NO_SELECT));
}

View file

@ -72,24 +72,14 @@ public class Geary.Imap.Command : RootParameters {
private void stock_params() {
add(tag);
add(new UnquotedStringParameter(name));
add(new AtomParameter(name));
if (args != null) {
foreach (string arg in args) {
switch (DataFormat.is_quoting_required(arg)) {
case DataFormat.Quoting.REQUIRED:
add(new QuotedStringParameter(arg));
break;
case DataFormat.Quoting.OPTIONAL:
add(new UnquotedStringParameter(arg));
break;
case DataFormat.Quoting.UNALLOWED:
error("Command continuations currently unsupported");
default:
assert_not_reached();
}
StringParameter? stringp = StringParameter.get_best_for(arg);
if (stringp != null)
add(stringp);
else
error("Command continuations currently unsupported");
}
}
}

View file

@ -236,7 +236,7 @@ public class Geary.Imap.MessageSet : BaseObject {
// look for open-ended span
if (span_count == 2)
builder.append_printf(",%s", last_seq_num.to_string());
else
else if (last_seq_num != start_of_span)
builder.append_printf(":%s", last_seq_num.to_string());
return builder.str;

View file

@ -20,11 +20,11 @@ public class Geary.Imap.StoreCommand : Command {
base (message_set.is_uid ? UID_NAME : NAME);
add(message_set.to_parameter());
add(new StringParameter("%sflags%s".printf(add_flag ? "+" : "-", silent ? ".silent" : "")));
add(new AtomParameter("%sflags%s".printf(add_flag ? "+" : "-", silent ? ".silent" : "")));
ListParameter list = new ListParameter(this);
foreach(MessageFlag flag in flag_list)
list.add(new StringParameter(flag.value));
list.add(new AtomParameter(flag.value));
add(list);
}

View file

@ -152,9 +152,7 @@ public class Geary.Imap.FetchBodyDataType : BaseObject {
}
public Parameter to_request_parameter() {
// Because of the kooky formatting of the Body[section]<partial> fetch field, use an
// unquoted string and format it ourselves.
return new UnquotedStringParameter(serialize_request());
return new AtomParameter(serialize_request());
}
private string serialize_part_number() {

View file

@ -132,7 +132,7 @@ public enum Geary.Imap.FetchDataType {
* Turns this {@link FetchDataType} into a {@link StringParameter} for transmission.
*/
public StringParameter to_parameter() {
return new StringParameter(to_string());
return new AtomParameter(to_string());
}
/**

View file

@ -42,5 +42,19 @@ public class Geary.Imap.MailboxParameter : StringParameter {
public string decode() {
return imap_utf7_to_utf8(value);
}
/**
* {@inheritDoc}
*/
public override async void serialize(Serializer ser) throws Error {
serialize_string(ser);
}
/**
* {@inheritDoc}
*/
public override string to_string() {
return value;
}
}

View file

@ -68,7 +68,7 @@ public enum Geary.Imap.StatusDataType {
}
public StringParameter to_parameter() {
return new StringParameter(to_string());
return new AtomParameter(to_string());
}
public static StatusDataType from_parameter(StringParameter stringp) throws ImapError {

View file

@ -15,7 +15,7 @@
* See [[http://tools.ietf.org/html/rfc3501#section-2.2.1]]
*/
public class Geary.Imap.Tag : StringParameter, Gee.Hashable<Geary.Imap.Tag> {
public class Geary.Imap.Tag : AtomParameter, Gee.Hashable<Geary.Imap.Tag> {
public const string UNTAGGED_VALUE = "*";
public const string CONTINUATION_VALUE = "+";
public const string UNASSIGNED_VALUE = "----";

View file

@ -0,0 +1,32 @@
/* 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 an IMAP atom.
*
* This class does not check if quoting is required. Use {@link DataFormat.is_quoting_required}
* or {@link StringParameter.get_best_for}.
*
* See {@link StringParameter} for a note about class heirarchy. In particular, note that
* [@link Deserializer} will not create this type of {@link Parameter} because it's unable to
* deduce if a string is an atom or a string from syntax alone.
*
* See [[http://tools.ietf.org/html/rfc3501#section-4.1]]
*/
public class Geary.Imap.AtomParameter : Geary.Imap.UnquotedStringParameter {
public AtomParameter(string value) {
base (value);
}
/**
* {@inheritDoc}
*/
public override async void serialize(Serializer ser) throws Error {
ser.push_unquoted_string(value);
}
}

View file

@ -4,164 +4,11 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
public abstract class Geary.Imap.Parameter : BaseObject {
public abstract async void serialize(Serializer ser) throws Error;
/**
* to_string() returns a representation of the Parameter suitable for logging and debugging,
* but should not be relied upon for wire or persistent representation.
*/
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) {
this.value = value;
}
public bool equals_cs(string value) {
return this.value == value;
}
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(int clamp_min = int.MIN, int clamp_max = int.MAX) throws ImapError {
return int.parse(value).clamp(clamp_min, clamp_max);
}
// TODO: This does not check that the value is a properly-formed long.
public long as_long(int clamp_min = int.MIN, int clamp_max = int.MAX) throws ImapError {
return long.parse(value).clamp(clamp_min, clamp_max);
}
// TODO: This does not check that the value is a properly-formed int64.
public int64 as_int64(int64 clamp_min = int64.MIN, int64 clamp_max = int64.MAX) throws ImapError {
return int64.parse(value).clamp(clamp_min, clamp_max);
}
public override string to_string() {
return value;
}
public override async void serialize(Serializer ser) throws Error {
ser.push_string(value);
}
}
/**
* This delivers the string to the IMAP server with quoting applied whether or not it's required.
* The representation of an IMAP parenthesized list.
*
* This is generally legal, but some servers may not appreciate it.
*
* {@link Deserializer} will never generate this {@link Parameter}.
* See [[http://tools.ietf.org/html/rfc3501#section-4.4]]
*/
public class Geary.Imap.QuotedStringParameter : Geary.Imap.StringParameter {
public QuotedStringParameter(string value) {
base (value);
}
public override string to_string() {
return "\"%s\"".printf(value);
}
public override async void serialize(Serializer ser) throws Error {
ser.push_quoted_string(value);
}
}
/**
* This delivers the string to the IMAP server with no quoting or formatting applied.
*
* This can lead to server errors if misused. Use only if absolutely necessary.
*
* {@link Deserializer} will never generate this Parameter.
*/
public class Geary.Imap.UnquotedStringParameter : Geary.Imap.StringParameter {
public UnquotedStringParameter(string value) {
base (value);
}
public override async void serialize(Serializer ser) throws Error {
ser.push_unquoted_string(value);
}
}
public class Geary.Imap.LiteralParameter : Geary.Imap.Parameter {
private Geary.Memory.AbstractBuffer buffer;
public LiteralParameter(Geary.Memory.AbstractBuffer buffer) {
this.buffer = buffer;
}
public size_t get_size() {
return buffer.get_size();
}
public Geary.Memory.AbstractBuffer get_buffer() {
return buffer;
}
/**
* Returns the LiteralParameter as though it had been a StringParameter on the wire. Note
* that this does not deal with quoting issues or NIL (which should never be literalized to
* begin with). It merely converts the literal data to a UTF-8 string and returns it as a
* StringParameter.
*/
public StringParameter to_string_parameter() {
return new StringParameter(buffer.to_valid_utf8());
}
public override string to_string() {
return "{literal/%lub}".printf(get_size());
}
public override async void serialize(Serializer ser) throws Error {
ser.push_string("{%lu}".printf(get_size()));
ser.push_eol();
yield ser.push_input_stream_literal_data_async(buffer.get_input_stream());
}
}
public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
/**
@ -180,21 +27,35 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
add(initial);
}
/**
* Returns null if no parent (top-level list).
*
* In a fully-formed set of {@link Parameter}s, this means this {@link ListParameter} is
* probably a {@link RootParameters}.
*/
public ListParameter? get_parent() {
return parent;
}
public void add(Parameter param) {
bool added = list.add(param);
assert(added);
/**
* Returns true if added.
*
* The same {@link Parameter} can't be added more than once to the same {@link ListParameter}.
*/
public bool add(Parameter param) {
return list.add(param);
}
public int get_count() {
return list.size;
}
//
// Parameter retrieval
//
/**
* Returns the Parameter at the index in the list, null if index is out of range.
* Returns the {@link Parameter} at the index in the list, null if index is out of range.
*
* TODO: This call can cause memory leaks when used with the "as" operator until the following
* Vala bug is fixed (probably in version 0.19.1).
@ -224,11 +85,14 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
}
/**
* Returns Paramater at index if in range and of Type type, otherwise throws an
* ImapError.TYPE_ERROR. type must be of type Parameter.
* Returns {@link Paramater} at index if in range and of Type type, otherwise throws an
* {@link ImapError.TYPE_ERROR}.
*
* type must be of type Parameter.
*/
public Parameter get_as(int index, Type type) throws ImapError {
assert(type.is_a(typeof(Parameter)));
if (!type.is_a(typeof(Parameter)))
throw new ImapError.TYPE_ERROR("Attempting to cast non-Parameter at index %d", index);
Parameter param = get_required(index);
if (!param.get_type().is_a(type)) {
@ -240,15 +104,25 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
}
/**
* Like get_as(), but returns null if the Parameter at index is a NilParameter.
* Like {@link get_as}, but returns null if the {@link Parameter} at index is a
* {@link NilParameter}.
*
* type must be of type Parameter.
*/
public Parameter? get_as_nullable(int index, Type type) throws ImapError {
assert(type.is_a(typeof(Parameter)));
if (!type.is_a(typeof(Parameter)))
throw new ImapError.TYPE_ERROR("Attempting to cast non-Parameter at index %d", index);
Parameter param = get_required(index);
if (param is NilParameter)
return null;
// Because Deserializer doesn't produce NilParameters, check manually if this Parameter
// can legally be NIL according to IMAP grammer.
StringParameter? stringp = param as StringParameter;
if (stringp != null && NilParameter.is_nil(stringp.value))
return null;
if (!param.get_type().is_a(type)) {
throw new ImapError.TYPE_ERROR("Parameter %d is not of type %s (is %s)", index,
type.name(), param.get_type().name());
@ -258,11 +132,13 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
}
/**
* Like get(), but returns null if Parameter at index is not of the specified type. type must
* be of type Parameter.
* Like {@link get}, but returns null if {@link Parameter} at index is not of the specified type.
*
* type must be of type Parameter.
*/
public Parameter? get_if(int index, Type type) {
assert(type.is_a(typeof(Parameter)));
if (!type.is_a(typeof(Parameter)))
return null;
Parameter? param = get(index);
if (param == null || !param.get_type().is_a(type))
@ -271,44 +147,30 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
return param;
}
/**
* Returns a StringParameter only if the Parameter at index is a StringParameter (quoted or
* atom string).
*/
public StringParameter get_only_as_string(int index) throws ImapError {
return (StringParameter) get_as(index, typeof(StringParameter));
}
//
// String retrieval
//
/**
* Returns a StringParameter only if the Parameter at index is a StringParameter (quoted or
* atom string).
* Returns a {@link StringParameter} only if the {@link Parameter} at index is a StringParameter.
*
* Compare to {@link get_if_string_or_literal}.
*/
public StringParameter? get_only_as_nullable_string(int index) throws ImapError {
return (StringParameter?) get_as_nullable(index, typeof(StringParameter));
}
/**
* Returns a StringParameter only if the Parameter at index is a StringParameter (quoted or
* atom string). Returns an empty StringParameter if index is for a NilParameter;
*/
public StringParameter get_only_as_empty_string(int index) throws ImapError {
StringParameter? param = get_only_as_nullable_string(index);
return param ?? new StringParameter("");
}
/**
* Returns a StringParameter only if the Parameter at index is a StringParameter (quoted or
* atom string).
*/
public StringParameter? get_only_if_string(int index) {
public StringParameter? get_if_string(int index) {
return (StringParameter?) get_if(index, typeof(StringParameter));
}
/**
* Returns the StringParameter at the index only if the Parameter is a StringParameter or a
* LiteralParameter with a length less than or equal to MAX_STRING_LITERAL_LENGTH. Throws an
* ImapError.TYPE_ERROR if a literal longer than that value.
* Returns a {@link StringParameter} for the value at the index only if the {@link Parameter}
* is a StringParameter or a {@link LiteralParameter} with a length less than or equal to
* {@link MAX_STRING_LITERAL_LENGTH}.
*
* Because literal data is being coerced into a StringParameter, the result may not be suitable
* for transmission as-is.
*
* @see get_as_nullable_string
* @throws ImapError.TYPE_ERROR if no StringParameter at index or the literal is longer than
* MAX_STRING_LITERAL_LENGTH.
*/
public StringParameter get_as_string(int index) throws ImapError {
Parameter param = get_required(index);
@ -319,17 +181,25 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
LiteralParameter? literalp = param as LiteralParameter;
if (literalp != null && literalp.get_size() <= MAX_STRING_LITERAL_LENGTH)
return literalp.to_string_parameter();
return literalp.coerce_to_string_parameter();
throw new ImapError.TYPE_ERROR("Parameter %d not of type string or literal (is %s)", index,
param.get_type().name());
}
/**
* Much like get_nullable() for StringParameters, but will convert a LiteralParameter to a
* StringParameter if its length is less than or equal to MAX_STRING_LITERAL_LENGTH. Throws
* an ImapError.TYPE_ERROR if literal is longer than that value.
* Returns a {@link StringParameter} for the value at the index only if the {@link Parameter}
* is a StringParameter or a {@link LiteralParameter} with a length less than or equal to
* {@link MAX_STRING_LITERAL_LENGTH}.
*
* Because literal data is being coerced into a StringParameter, the result may not be suitable
* for transmission as-is.
*
* @return null if no StringParameter or LiteralParameter at index.
* @see get_as_string
* @throws ImapError.TYPE_ERROR if literal is longer than MAX_STRING_LITERAL_LENGTH.
*/
public StringParameter? get_as_nullable_string(int index) throws ImapError {
Parameter? param = get_as_nullable(index, typeof(Parameter));
if (param == null)
@ -341,7 +211,7 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
LiteralParameter? literalp = param as LiteralParameter;
if (literalp != null && literalp.get_size() <= MAX_STRING_LITERAL_LENGTH)
return literalp.to_string_parameter();
return literalp.coerce_to_string_parameter();
throw new ImapError.TYPE_ERROR("Parameter %d not of type string or literal (is %s)", index,
param.get_type().name());
@ -354,60 +224,83 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public StringParameter get_as_empty_string(int index) throws ImapError {
StringParameter? stringp = get_as_nullable_string(index);
return stringp ?? new StringParameter("");
return stringp ?? StringParameter.get_best_for("");
}
//
// List retrieval
//
/**
* Returns the StringParameter at the index only if the Parameter is a StringParameter or a
* LiteralParameter with a length less than or equal to MAX_STRING_LITERAL_LENGTH. Returns null
* if either is not true.
* Returns a {@link ListParameter} at index.
*
* @see get_as
*/
public StringParameter? get_if_string(int index) {
Parameter? param = get(index);
if (param == null)
return null;
StringParameter? stringp = param as StringParameter;
if (stringp != null)
return stringp;
LiteralParameter? literalp = param as LiteralParameter;
if (literalp != null && literalp.get_size() <= MAX_STRING_LITERAL_LENGTH)
return literalp.to_string_parameter();
return null;
}
public ListParameter get_as_list(int index) throws ImapError {
return (ListParameter) get_as(index, typeof(ListParameter));
}
/**
* Returns a {@link ListParameter} at index, null if NIL.
*
* @see get_as_nullable
*/
public ListParameter? get_as_nullable_list(int index) throws ImapError {
return (ListParameter?) get_as_nullable(index, typeof(ListParameter));
}
/**
* Returns [@link ListParameter} at index, an empty list if NIL.
*/
public ListParameter get_as_empty_list(int index) throws ImapError {
ListParameter? param = get_as_nullable_list(index);
return param ?? new ListParameter(this);
}
/**
* Returns a {@link ListParameter} at index, null if not a list.
*
* @see get_if
*/
public ListParameter? get_if_list(int index) {
return (ListParameter?) get_if(index, typeof(ListParameter));
}
//
// Literal retrieval
//
/**
* Returns a {@link LiteralParameter} at index.
*
* @see get_as
*/
public LiteralParameter get_as_literal(int index) throws ImapError {
return (LiteralParameter) get_as(index, typeof(LiteralParameter));
}
/**
* Returns a {@link LiteralParameter} at index, null if NIL.
*
* @see get_as_nullable
*/
public LiteralParameter? get_as_nullable_literal(int index) throws ImapError {
return (LiteralParameter?) get_as_nullable(index, typeof(LiteralParameter));
}
/**
* Returns a {@link LiteralParameter} at index, null if not a list.
*
* @see get_if
*/
public LiteralParameter? get_if_literal(int index) {
return (LiteralParameter?) get_if(index, typeof(LiteralParameter));
}
/**
* Returns [@link LiteralParameter} at index, an empty list if NIL.
*/
public LiteralParameter get_as_empty_literal(int index) throws ImapError {
LiteralParameter? param = get_as_nullable_literal(index);
@ -442,6 +335,9 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
return get_as_nullable_buffer(index) ?? Memory.EmptyBuffer.instance;
}
/**
* Returns a read-only List of {@link Parameter}s.
*/
public Gee.List<Parameter> get_all() {
return list.read_only_view;
}
@ -493,6 +389,9 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
return builder.str;
}
/**
* {@inheritDoc}
*/
public override string to_string() {
return "(%s)".printf(stringize_list());
}
@ -506,6 +405,9 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
}
}
/**
* {@inheritDoc}
*/
public override async void serialize(Serializer ser) throws Error {
ser.push_ascii('(');
yield serialize_list(ser);
@ -513,55 +415,3 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
}
}
public class Geary.Imap.RootParameters : Geary.Imap.ListParameter {
public RootParameters(Parameter? initial = null) {
base (null, initial);
}
/**
* Moves all contained {@link Parameter} objects inside the supplied RootParameters into a
* new RootParameters.
*
* The supplied root object is stripped clean by this call.
*/
public RootParameters.migrate(RootParameters root) {
base (null);
adopt_children(root);
}
/**
* Returns null if the first parameter is not a StringParameter that resembles a Tag.
*/
public Tag? get_tag() {
StringParameter? strparam = get_only_if_string(0);
if (strparam == null)
return null;
if (!Tag.is_tag(strparam))
return null;
return new Tag.from_parameter(strparam);
}
/**
* Returns true if the first parameter is a StringParameter that resembles a Tag.
*/
public bool has_tag() {
StringParameter? strparam = get_only_if_string(0);
if (strparam == null)
return false;
return (strparam != null) ? Tag.is_tag(strparam) : false;
}
public override string to_string() {
return stringize_list();
}
public override async void serialize(Serializer ser) throws Error {
yield serialize_list(ser);
ser.push_eol();
}
}

View file

@ -0,0 +1,67 @@
/* Copyright 2011-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 an IMAP literal parameter.
*
* Because a literal parameter can hold 8-bit data, this is not a descendent of
* {@link StringParameter}, although some times literal data is used to store 8-bit text (for
* example, UTF-8).
*
* See [[http://tools.ietf.org/html/rfc3501#section-4.3]]
*/
public class Geary.Imap.LiteralParameter : Geary.Imap.Parameter {
private Geary.Memory.AbstractBuffer buffer;
public LiteralParameter(Geary.Memory.AbstractBuffer buffer) {
this.buffer = buffer;
}
/**
* Returns the number of bytes in the literal parameter's buffer.
*/
public size_t get_size() {
return buffer.get_size();
}
/**
* Returns the literal paremeter's buffer.
*/
public Geary.Memory.AbstractBuffer get_buffer() {
return buffer;
}
/**
* Returns the {@link LiteralParameter} as though it had been a {@link StringParameter} on the
* wire.
*
* Note that this does not deal with quoting issues or NIL (which should never be
* literalized to begin with). It merely converts the literal data to a UTF-8 string and
* returns it as a StringParameter. Hence, the data is being coerced and may be unsuitable
* for transmitting on the wire.
*/
public StringParameter coerce_to_string_parameter() {
return new UnquotedStringParameter(buffer.to_valid_utf8());
}
/**
* {@inheritDoc}
*/
public override string to_string() {
return "{literal/%lub}".printf(get_size());
}
/**
* {@inheritDoc}
*/
public override async void serialize(Serializer ser) throws Error {
ser.push_unquoted_string("{%lu}".printf(get_size()));
ser.push_eol();
yield ser.push_input_stream_literal_data_async(buffer.get_input_stream());
}
}

View file

@ -0,0 +1,61 @@
/* Copyright 2011-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.
*/
/**
* The representation of IMAP's NIL value.
*
* Note that NIL 'represents the non-existence of a particular data item that is represented as a
* string or parenthesized list, as distinct from the empty string "" or the empty parenthesized
* list () ... NIL is never used for any data item which takes the form of an atom."
*
* Since there's only one form of a NilParameter, it should be retrieved via the {@link instance}
* property.
*
* See [[http://tools.ietf.org/html/rfc3501#section-4.5]]
*/
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() {
}
/**
* See note at {@link NilParameter} for comparison rules of "NIL".
*
* In particular, this should not be used when expecting an atom. A mailbox name of NIL
* means that the mailbox is actually named NIL and does not represent an empty string or empty
* list.
*/
public static bool is_nil(string str) {
return String.ascii_equali(VALUE, str);
}
/**
* {@inheritDoc}
*/
public override async void serialize(Serializer ser) throws Error {
ser.push_nil();
}
/**
* {@inheritDoc}
*/
public override string to_string() {
return VALUE;
}
}

View file

@ -0,0 +1,27 @@
/* Copyright 2011-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.
*/
/**
* The basic abstraction of a single IMAP parameter that may be serialized and deserialized to and
* from the network.
*
* @see Serializer
* @see Deserializer
*/
public abstract class Geary.Imap.Parameter : BaseObject {
/**
* Invoked when the {@link Parameter} is to be serialized out to the network.
*/
public abstract async void serialize(Serializer ser) throws Error;
/**
* Returns a representation of the {@link Parameter} suitable for logging and debugging,
* but should not be relied upon for wire or persistent representation.
*/
public abstract string to_string();
}

View file

@ -0,0 +1,37 @@
/* 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 an IMAP quoted string.
*
* This class does not check if quoting is required. Use {@link DataFormat.is_quoting_required}
* or {@link StringParameter.get_best_for}.
*
* {@link Deserializer} will never generate this {@link Parameter}.
*
* See [[http://tools.ietf.org/html/rfc3501#section-4.3]].
*/
public class Geary.Imap.QuotedStringParameter : Geary.Imap.StringParameter {
public QuotedStringParameter(string value) {
base (value);
}
/**
* {@inheritDoc}
*/
public override string to_string() {
return "\"%s\"".printf(value);
}
/**
* {@inheritDoc}
*/
public override async void serialize(Serializer ser) throws Error {
ser.push_quoted_string(value);
}
}

View file

@ -0,0 +1,74 @@
/* Copyright 2011-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.
*/
/**
* The base respresentation of an complete IMAP message.
*
* By definition, a top-level {@link ListParameter}. A RootParameters object should never be
* added to another list.
*
* @see ServerResponse
* @see Command
*/
public class Geary.Imap.RootParameters : Geary.Imap.ListParameter {
public RootParameters(Parameter? initial = null) {
base (null, initial);
}
/**
* Moves all contained {@link Parameter} objects inside the supplied RootParameters into a
* new RootParameters.
*
* The supplied root object is stripped clean by this call.
*/
public RootParameters.migrate(RootParameters root) {
base (null);
adopt_children(root);
}
/**
* Returns null if the first parameter is not a StringParameter that resembles a Tag.
*/
public Tag? get_tag() {
StringParameter? strparam = get_if_string(0);
if (strparam == null)
return null;
if (!Tag.is_tag(strparam))
return null;
return new Tag.from_parameter(strparam);
}
/**
* Returns true if the first parameter is a StringParameter that resembles a Tag.
*/
public bool has_tag() {
StringParameter? strparam = get_if_string(0);
if (strparam == null)
return false;
return (strparam != null) ? Tag.is_tag(strparam) : false;
}
/**
* {@inheritDoc}
*/
public override string to_string() {
return stringize_list();
}
/**
* {@inheritDoc}
*/
public override async void serialize(Serializer ser) throws Error {
yield serialize_list(ser);
ser.push_eol();
}
}

View file

@ -0,0 +1,136 @@
/* Copyright 2011-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 base abstract representation of string (text) data in IMAP.
*
* Although they may be transmitted in various ways, most parameters in IMAP are strings or text
* format, possibly with some quoting rules applied. This class handles most issues with these
* types of {@link Parameter}s.
*
* Although the IMAP specification doesn't list an atom as a "string", it is here because of the
* common functionality that is needed for comparison and other operations.
*
* Note that {@link NilParameter} is ''not'' a StringParameter, to avoid type confusion.
*
* See [[http://tools.ietf.org/html/rfc3501#section-4.3]]
*/
public abstract class Geary.Imap.StringParameter : Geary.Imap.Parameter {
/**
* The unquoted, decoded string.
*/
public string value { get; private set; }
/**
* Returns {@link} value or null if value is empty (zero-length).
*/
public string? nullable_value {
get {
return String.is_empty(value) ? null : value;
}
}
protected StringParameter(string value) {
this.value = value;
}
/**
* Returns a {@link StringParameter} appropriate for the contents of value.
*
* Will not return an {@link AtomParameter}, but rather an {@link UnquotedStringParameter} if
* suitable. Will not return a {@link NilParameter} for empty strings, but rather a
* {@link QuotedStringParameter}.
*
* Because of these restrictions, should only be used when the context or syntax of the
* Parameter is unknown or uncertain.
*
* @return null if the string must be represented with a {@link LiteralParameter}.
*/
public static StringParameter? get_best_for(string value) {
switch (DataFormat.is_quoting_required(value)) {
case DataFormat.Quoting.REQUIRED:
return new QuotedStringParameter(value);
case DataFormat.Quoting.OPTIONAL:
return new UnquotedStringParameter(value);
case DataFormat.Quoting.UNALLOWED:
return null;
default:
assert_not_reached();
}
}
/**
* Can be used by subclasses to properly serialize the string value according to quoting rules.
*
* NOTE: Literal data is not currently supported.
*/
protected void serialize_string(Serializer ser) throws Error {
switch (DataFormat.is_quoting_required(value)) {
case DataFormat.Quoting.REQUIRED:
ser.push_quoted_string(value);
break;
case DataFormat.Quoting.OPTIONAL:
ser.push_unquoted_string(value);
break;
case DataFormat.Quoting.UNALLOWED:
error("Unable to serialize literal data");
default:
assert_not_reached();
}
}
/**
* Case-sensitive comparison.
*/
public bool equals_cs(string value) {
return this.value == value;
}
/**
* Case-insensitive comparison.
*/
public bool equals_ci(string value) {
return this.value.down() == value.down();
}
/**
* Converts the {@link value} to an int, clamped between clamp_min and clamp_max.
*
* TODO: This does not check that the value is a properly-formed integer. This should be
*. added later.
*/
public int as_int(int clamp_min = int.MIN, int clamp_max = int.MAX) throws ImapError {
return int.parse(value).clamp(clamp_min, clamp_max);
}
/**
* Converts the {@link value} to a long integer, clamped between clamp_min and clamp_max.
*
* TODO: This does not check that the value is a properly-formed long integer. This should be
*. added later.
*/
public long as_long(int clamp_min = int.MIN, int clamp_max = int.MAX) throws ImapError {
return long.parse(value).clamp(clamp_min, clamp_max);
}
/**
* Converts the {@link value} to a 64-bit integer, clamped between clamp_min and clamp_max.
*
* TODO: This does not check that the value is a properly-formed 64-bit integer. This should be
*. added later.
*/
public int64 as_int64(int64 clamp_min = int64.MIN, int64 clamp_max = int64.MAX) throws ImapError {
return int64.parse(value).clamp(clamp_min, clamp_max);
}
}

View file

@ -0,0 +1,39 @@
/* 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 an IMAP string that is not quoted.
*
* This class does not check if quoting is required. Use {@link DataFormat.is_quoting_required}
* or {@link StringParameter.get_best_for}.
*
* The difference between this class and {@link AtomParameter} is that this can be used in any
* circumstance where a string can (or is) represented without quotes or literal data, whereas an
* atom has strict definitions about where it's found.
*
* See [[http://tools.ietf.org/html/rfc3501#section-4.1]]
*/
public class Geary.Imap.UnquotedStringParameter : Geary.Imap.StringParameter {
public UnquotedStringParameter(string value) {
base (value);
}
/**
* {@inheritDoc}
*/
public override async void serialize(Serializer ser) throws Error {
ser.push_unquoted_string(value);
}
/**
* {@inheritDoc}
*/
public override string to_string() {
return value;
}
}

View file

@ -43,7 +43,7 @@ public abstract class Geary.Imap.FetchDataDecoder : BaseObject {
// reasonably-length literals into StringParameters), do so here manually
try {
if (literalp.get_size() <= ListParameter.MAX_STRING_LITERAL_LENGTH)
return decode_string(literalp.to_string_parameter());
return decode_string(literalp.coerce_to_string_parameter());
} catch (ImapError imap_err) {
// if decode_string() throws a TYPE_ERROR, retry as a LiteralParameter, otherwise
// relay the exception to the caller

View file

@ -171,7 +171,7 @@ public enum Geary.Imap.ResponseCodeType {
}
public StringParameter to_parameter() {
return new StringParameter(to_string());
return new AtomParameter(to_string());
}
}

View file

@ -105,7 +105,7 @@ public enum Geary.Imap.ServerDataType {
}
public StringParameter to_parameter() {
return new StringParameter(to_string());
return new AtomParameter(to_string());
}
/**

View file

@ -66,11 +66,7 @@ public enum Geary.Imap.Status {
}
public Parameter to_parameter() {
return new StringParameter(to_string());
}
public void serialize(Serializer ser) throws Error {
ser.push_string(to_string());
return new AtomParameter(to_string());
}
}

View file

@ -822,7 +822,7 @@ public class Geary.Imap.ClientConnection : BaseObject {
try {
Logging.debug(Logging.Flag.NETWORK, "[%s S] %s", to_string(), "done");
ser.push_string("done");
ser.push_unquoted_string("done");
ser.push_eol();
} catch (Error err) {
debug("[%s] Unable to close IDLE: %s", to_string(), err.message);

View file

@ -86,10 +86,10 @@ public class Geary.Imap.Deserializer : BaseObject {
/**
* 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.
* Note that {@link RootParameters} may contain {@link QuotedStringParameter}s,
* {@link UnquotedStringParameter}s, {@link ResponseCode}, and {@link ListParameter}s.
* Deserializer does not produce any other kind of Parameter due to its inability to deduce
* them from syntax alone. ResponseCode, however, can be.
*/
public signal void parameters_ready(RootParameters root);
@ -414,14 +414,14 @@ public class Geary.Imap.Deserializer : BaseObject {
current_string.append_unichar(ch);
}
private void save_string_parameter() {
private void save_string_parameter(bool quoted) {
if (is_current_string_empty())
return;
if (NilParameter.is_nil(current_string.str))
save_parameter(NilParameter.instance);
if (quoted)
save_parameter(new QuotedStringParameter(current_string.str));
else
save_parameter(new StringParameter(current_string.str));
save_parameter(new UnquotedStringParameter(current_string.str));
current_string = null;
}
@ -536,7 +536,7 @@ public class Geary.Imap.Deserializer : BaseObject {
// space indicates end of tag
if (ch == ' ') {
save_string_parameter();
save_string_parameter(false);
return State.START_PARAM;
}
@ -575,15 +575,15 @@ public class Geary.Imap.Deserializer : BaseObject {
return State.SYSTEM_FLAG;
}
// space indicates end-of-atom, end-of-tag, or end-of-system-flag
// space indicates end-of-atom
if (ch == ' ') {
save_string_parameter();
save_string_parameter(false);
return State.START_PARAM;
}
if (ch == get_current_context_terminator()) {
save_string_parameter();
save_string_parameter(false);
return pop();
}
@ -607,7 +607,7 @@ public class Geary.Imap.Deserializer : BaseObject {
// space indicates end-of-system-flag
if (ch == ' ') {
save_string_parameter();
save_string_parameter(false);
return State.START_PARAM;
}
@ -615,7 +615,7 @@ public class Geary.Imap.Deserializer : BaseObject {
// close-parens/close-square-bracket after a system flag indicates end-of-list/end-of-response
// code
if (ch == terminator) {
save_string_parameter();
save_string_parameter(false);
return pop();
}
@ -631,7 +631,7 @@ public class Geary.Imap.Deserializer : BaseObject {
private uint on_atom_eol(uint state, uint event, void *user) {
// clean up final atom
save_string_parameter();
save_string_parameter(false);
return flush_params();
}
@ -649,7 +649,7 @@ public class Geary.Imap.Deserializer : BaseObject {
// DQUOTE ends quoted string and return to parsing atoms
if (ch == '\"') {
save_string_parameter();
save_string_parameter(true);
return State.START_PARAM;
}
@ -706,7 +706,7 @@ public class Geary.Imap.Deserializer : BaseObject {
if (ch != ' ')
return on_partial_body_atom_char(State.PARTIAL_BODY_ATOM, event, user);
save_string_parameter();
save_string_parameter(false);
return State.START_PARAM;
}

View file

@ -44,25 +44,6 @@ public class Geary.Imap.Serializer : BaseObject {
douts.put_byte(ch, null);
}
public void push_string(string str) throws Error {
// see if need to convert to quoted string, only emitting it if required
switch (DataFormat.is_quoting_required(str)) {
case DataFormat.Quoting.OPTIONAL:
douts.put_string(str);
break;
case DataFormat.Quoting.REQUIRED:
bool required = push_quoted_string(str);
assert(required);
break;
case DataFormat.Quoting.UNALLOWED:
default:
// TODO: Not handled currently
assert_not_reached();
}
}
/**
* Pushes the string to the IMAP server with quoting applied whether required or not. Returns
* true if quoting was required.