Added SelectExamineResults decoder.

The result of a SELECT or EXAMINE command is now parsed and returned to the caller.  This information is boiled down to the Geary.Folder interface, which adds information about the folder to the object.
This commit is contained in:
Jim Nelson 2011-05-31 15:40:39 -07:00
parent 737f235d60
commit 9ac3fd0b68
17 changed files with 343 additions and 47 deletions

View file

@ -26,6 +26,7 @@ ENGINE_SRC := \
src/engine/imap/Command.vala \ src/engine/imap/Command.vala \
src/engine/imap/Commands.vala \ src/engine/imap/Commands.vala \
src/engine/imap/ResponseCode.vala \ src/engine/imap/ResponseCode.vala \
src/engine/imap/ResponseCodeType.vala \
src/engine/imap/ServerResponse.vala \ src/engine/imap/ServerResponse.vala \
src/engine/imap/StatusResponse.vala \ src/engine/imap/StatusResponse.vala \
src/engine/imap/ServerData.vala \ src/engine/imap/ServerData.vala \
@ -39,10 +40,12 @@ ENGINE_SRC := \
src/engine/imap/Deserializer.vala \ src/engine/imap/Deserializer.vala \
src/engine/imap/Error.vala \ src/engine/imap/Error.vala \
src/engine/imap/Flag.vala \ src/engine/imap/Flag.vala \
src/engine/imap/decoders/CommandResults.vala \
src/engine/imap/decoders/FetchDataDecoder.vala \ src/engine/imap/decoders/FetchDataDecoder.vala \
src/engine/imap/decoders/FetchResults.vala \ src/engine/imap/decoders/FetchResults.vala \
src/engine/imap/decoders/NoopResults.vala \ src/engine/imap/decoders/NoopResults.vala \
src/engine/imap/decoders/ListResults.vala \ src/engine/imap/decoders/ListResults.vala \
src/engine/imap/decoders/SelectExamineResults.vala \
src/engine/rfc822/MailboxAddress.vala \ src/engine/rfc822/MailboxAddress.vala \
src/engine/rfc822/MessageData.vala \ src/engine/rfc822/MessageData.vala \
src/engine/util/String.vala \ src/engine/util/String.vala \

View file

@ -34,15 +34,15 @@ public class FolderListStore : Gtk.TreeStore {
set_column_types(Column.get_types()); set_column_types(Column.get_types());
} }
public void add_folder(string folder) { public void add_folder(Geary.FolderDetail folder) {
Gtk.TreeIter iter; Gtk.TreeIter iter;
append(out iter, null); append(out iter, null);
set(iter, Column.NAME, folder); set(iter, Column.NAME, folder.name);
} }
public void add_folders(Gee.Collection<string> folders) { public void add_folders(Gee.Collection<Geary.FolderDetail> folders) {
foreach (string folder in folders) foreach (Geary.FolderDetail folder in folders)
add_folder(folder); add_folder(folder);
} }

View file

@ -63,7 +63,8 @@ public class MainWindow : Gtk.Window {
if (account == null) if (account == null)
error("Unable to login"); error("Unable to login");
Gee.Collection<string>? folders = yield account.list("/"); // pull down the root-level folders
Gee.Collection<Geary.FolderDetail> folders = yield account.list(null);
if (folders != null) { if (folders != null) {
debug("%d folders found", folders.size); debug("%d folders found", folders.size);
folder_list_store.add_folders(folders); folder_list_store.add_folders(folders);

View file

@ -4,9 +4,13 @@
* (version 2.1 or later). See the COPYING file in this distribution. * (version 2.1 or later). See the COPYING file in this distribution.
*/ */
public interface Geary.FolderDetail : Object {
public abstract string name { get; protected set; }
}
public interface Geary.Account : Object { public interface Geary.Account : Object {
public abstract async Gee.Collection<string> list(string parent, Cancellable? cancellable = null) public abstract async Gee.Collection<FolderDetail> list(FolderDetail? parent,
throws Error; Cancellable? cancellable = null) throws Error;
public abstract async Folder open(string folder, Cancellable? cancellable = null) throws Error; public abstract async Folder open(string folder, Cancellable? cancellable = null) throws Error;
} }
@ -18,6 +22,12 @@ public interface Geary.Folder : Object {
FOLDER_CLOSED FOLDER_CLOSED
} }
public abstract string name { get; protected set; }
public abstract int count { get; protected set; }
public abstract bool is_readonly { get; protected set; }
public signal void closed(CloseReason reason); public signal void closed(CloseReason reason);
public abstract async Gee.List<Message>? read(int low, int count, Cancellable? cancellable = null) public abstract async Gee.List<Message>? read(int low, int count, Cancellable? cancellable = null)

View file

@ -645,16 +645,18 @@ public class Geary.Imap.ClientSession {
// select/examine // select/examine
// //
public async string select_async(string mailbox, Cancellable? cancellable = null) throws Error { public async SelectExamineResults select_async(string mailbox, Cancellable? cancellable = null)
throws Error {
return yield select_examine_async(mailbox, true, cancellable); return yield select_examine_async(mailbox, true, cancellable);
} }
public async string examine_async(string mailbox, Cancellable? cancellable = null) throws Error { public async SelectExamineResults examine_async(string mailbox, Cancellable? cancellable = null)
throws Error {
return yield select_examine_async(mailbox, false, cancellable); return yield select_examine_async(mailbox, false, cancellable);
} }
public async string select_examine_async(string mailbox, bool is_select, Cancellable? cancellable) public async SelectExamineResults select_examine_async(string mailbox, bool is_select,
throws Error { Cancellable? cancellable) throws Error {
string? old_mailbox = current_mailbox; string? old_mailbox = current_mailbox;
SelectParams params = new SelectParams(mailbox, is_select, cancellable, SelectParams params = new SelectParams(mailbox, is_select, cancellable,
@ -673,7 +675,7 @@ public class Geary.Imap.ClientSession {
assert(current_mailbox != null); assert(current_mailbox != null);
current_mailbox_changed(old_mailbox, current_mailbox, current_mailbox_readonly); current_mailbox_changed(old_mailbox, current_mailbox, current_mailbox_readonly);
return current_mailbox; return SelectExamineResults.decode(params.cmd_response);
} }
private uint on_select(uint state, uint event, void *user, Object? object) { private uint on_select(uint state, uint event, void *user, Object? object) {

View file

@ -31,8 +31,9 @@ public class Geary.Imap.ClientSessionManager : Object, Geary.Account {
session.enable_keepalives(keepalive_sec); session.enable_keepalives(keepalive_sec);
} }
public async Gee.Collection<string> list(string parent, Cancellable? cancellable = null) throws Error { public async Gee.Collection<Geary.FolderDetail> list(Geary.FolderDetail? parent,
string specifier = String.is_empty(parent) ? "/" : parent; Cancellable? cancellable = null) throws Error {
string specifier = (parent != null) ? parent.name : "/";
specifier += (specifier.has_suffix("/")) ? "%" : "/%"; specifier += (specifier.has_suffix("/")) ? "%" : "/%";
ClientSession session = yield get_authorized_session(cancellable); ClientSession session = yield get_authorized_session(cancellable);
@ -40,11 +41,14 @@ public class Geary.Imap.ClientSessionManager : Object, Geary.Account {
ListResults results = ListResults.decode(yield session.send_command_async( ListResults results = ListResults.decode(yield session.send_command_async(
new ListCommand(session.generate_tag(), specifier), cancellable)); new ListCommand(session.generate_tag(), specifier), cancellable));
return results.get_names(); return results.get_all();
} }
public async Geary.Folder open(string folder, Cancellable? cancellable = null) throws Error { public async Geary.Folder open(string folder, Cancellable? cancellable = null) throws Error {
return new Mailbox(yield examine_async(folder, cancellable), on_destroying_mailbox); SelectExamineResults results;
ClientSession session = yield examine_async(folder, out results, cancellable);
return new Mailbox(session, results, on_destroying_mailbox);
} }
private async ClientSession get_authorized_session(Cancellable? cancellable = null) throws Error { private async ClientSession get_authorized_session(Cancellable? cancellable = null) throws Error {
@ -67,18 +71,18 @@ public class Geary.Imap.ClientSessionManager : Object, Geary.Account {
return new_session; return new_session;
} }
public async ClientSession select_async(string folder, Cancellable? cancellable = null) public async ClientSession select_async(string folder, out SelectExamineResults results,
throws Error { Cancellable? cancellable = null) throws Error {
return yield select_examine_async(folder, true, cancellable); return yield select_examine_async(folder, true, out results, cancellable);
} }
public async ClientSession examine_async(string folder, Cancellable? cancellable = null) public async ClientSession examine_async(string folder, out SelectExamineResults results,
throws Error { Cancellable? cancellable = null) throws Error {
return yield select_examine_async(folder, false, cancellable); return yield select_examine_async(folder, false, out results, cancellable);
} }
public async ClientSession select_examine_async(string folder, bool is_select, public async ClientSession select_examine_async(string folder, bool is_select,
Cancellable? cancellable = null) throws Error { out SelectExamineResults results, Cancellable? cancellable = null) throws Error {
ClientSession.Context needed_context = (is_select) ? ClientSession.Context.SELECTED ClientSession.Context needed_context = (is_select) ? ClientSession.Context.SELECTED
: ClientSession.Context.EXAMINED; : ClientSession.Context.EXAMINED;
foreach (ClientSession session in sessions) { foreach (ClientSession session in sessions) {
@ -89,7 +93,7 @@ public class Geary.Imap.ClientSessionManager : Object, Geary.Account {
ClientSession authd = yield get_authorized_session(cancellable); ClientSession authd = yield get_authorized_session(cancellable);
yield authd.select_examine_async(folder, is_select, cancellable); results = yield authd.select_examine_async(folder, is_select, cancellable);
return authd; return authd;
} }

View file

@ -43,6 +43,7 @@ public class Geary.Imap.MessageFlag : Geary.Imap.Flag {
public static MessageFlag FLAGGED = new MessageFlag("\\flagged"); public static MessageFlag FLAGGED = new MessageFlag("\\flagged");
public static MessageFlag RECENT = new MessageFlag("\\recent"); public static MessageFlag RECENT = new MessageFlag("\\recent");
public static MessageFlag SEEN = new MessageFlag("\\seen"); public static MessageFlag SEEN = new MessageFlag("\\seen");
public static MessageFlag ALLOWS_NEW = new MessageFlag("\\*");
public MessageFlag(string value) { public MessageFlag(string value) {
base (value); base (value);
@ -55,6 +56,7 @@ public class Geary.Imap.MailboxAttribute : Geary.Imap.Flag {
public static MailboxAttribute MARKED = new MailboxAttribute("\\marked"); public static MailboxAttribute MARKED = new MailboxAttribute("\\marked");
public static MailboxAttribute UNMARKED = new MailboxAttribute("\\unmarked"); public static MailboxAttribute UNMARKED = new MailboxAttribute("\\unmarked");
public static MailboxAttribute HAS_NO_CHILDREN = new MailboxAttribute("\\hasnochildren"); public static MailboxAttribute HAS_NO_CHILDREN = new MailboxAttribute("\\hasnochildren");
public static MailboxAttribute ALLOWS_NEW = new MailboxAttribute("\\*");
public MailboxAttribute(string value) { public MailboxAttribute(string value) {
base (value); base (value);

View file

@ -5,18 +5,23 @@
*/ */
public class Geary.Imap.Mailbox : Object, Geary.Folder { public class Geary.Imap.Mailbox : Object, Geary.Folder {
public string name { get; private set; } public string name { get; protected set; }
public bool is_readonly { get; private set; } public int count { get; protected set; }
public bool is_readonly { get; protected set; }
private ClientSession? session; private ClientSession? session;
private SelectExamineResults select_results;
private Geary.Delegate.DestructorNotifier<Mailbox>? dtor_notifier; private Geary.Delegate.DestructorNotifier<Mailbox>? dtor_notifier;
internal Mailbox(ClientSession session, Geary.Delegate.DestructorNotifier<Mailbox>? dtor_notifier) { internal Mailbox(ClientSession session, SelectExamineResults results,
Geary.Delegate.DestructorNotifier<Mailbox>? dtor_notifier) {
this.session = session; this.session = session;
this.select_results = results;
this.dtor_notifier = dtor_notifier; this.dtor_notifier = dtor_notifier;
name = session.get_current_mailbox(); name = session.get_current_mailbox();
is_readonly = session.is_current_mailbox_readonly(); is_readonly = results.readonly;
count = results.exists;
session.current_mailbox_changed.connect(on_session_mailbox_changed); session.current_mailbox_changed.connect(on_session_mailbox_changed);
session.logged_out.connect(on_session_logged_out); session.logged_out.connect(on_session_logged_out);

View file

@ -65,6 +65,21 @@ public class Geary.Imap.MessageFlags : Geary.Imap.Flags {
public MessageFlags(Gee.Collection<MessageFlag> flags) { public MessageFlags(Gee.Collection<MessageFlag> flags) {
base (flags); base (flags);
} }
public static MessageFlags from_list(ListParameter listp) throws ImapError {
Gee.Collection<MessageFlag> list = new Gee.ArrayList<MessageFlag>();
foreach (Parameter param in listp.get_all()) {
StringParameter? stringp = param as StringParameter;
if (stringp == null) {
throw new ImapError.TYPE_ERROR("Flags list contained non-string parameter \"%s\"",
param.to_string());
}
list.add(new MessageFlag(stringp.value));
}
return new MessageFlags(list);
}
} }
public class Geary.Imap.MailboxAttributes : Geary.Imap.Flags { public class Geary.Imap.MailboxAttributes : Geary.Imap.Flags {

View file

@ -64,13 +64,13 @@ public class Geary.Imap.StringParameter : Geary.Imap.Parameter {
// TODO: This does not check that the value is a properly-formed integer. This should be // TODO: This does not check that the value is a properly-formed integer. This should be
// added later. // added later.
public int as_int() throws ImapError { public int as_int(int clamp_min = int.MIN, int clamp_max = int.MAX) throws ImapError {
return int.parse(value); return int.parse(value).clamp(clamp_min, clamp_max);
} }
// TODO: This does not check that the value is a properly-formed long. // TODO: This does not check that the value is a properly-formed long.
public long as_long() throws ImapError { public long as_long(int clamp_min = int.MIN, int clamp_max = int.MAX) throws ImapError {
return long.parse(value); return long.parse(value).clamp(clamp_min, clamp_max);
} }
public override string to_string() { public override string to_string() {

View file

@ -9,6 +9,10 @@ public class Geary.Imap.ResponseCode : Geary.Imap.ListParameter {
base (parent, initial); base (parent, initial);
} }
public ResponseCodeType get_code_type() throws ImapError {
return ResponseCodeType.from_parameter(get_as_string(0));
}
public override string to_string() { public override string to_string() {
return "[%s]".printf(stringize_list()); return "[%s]".printf(stringize_list());
} }

View file

@ -0,0 +1,101 @@
/* 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 enum Geary.Imap.ResponseCodeType {
ALERT,
NEWNAME,
PARSE,
PERMANENT_FLAGS,
READONLY,
READWRITE,
TRY_CREATE,
UIDVALIDITY,
UIDNEXT,
UNSEEN;
public string to_string() {
switch (this) {
case ALERT:
return "alert";
case NEWNAME:
return "newname";
case PARSE:
return "parse";
case PERMANENT_FLAGS:
return "permanentflags";
case READONLY:
return "read-only";
case READWRITE:
return "read-write";
case TRY_CREATE:
return "trycreate";
case UIDVALIDITY:
return "uidvalidity";
case UIDNEXT:
return "uidnext";
case UNSEEN:
return "unseen";
default:
assert_not_reached();
}
}
public static ResponseCodeType decode(string value) throws ImapError {
switch (value.down()) {
case "alert":
return ALERT;
case "newname":
return NEWNAME;
case "parse":
return PARSE;
case "permanentflags":
return PERMANENT_FLAGS;
case "read-only":
return READONLY;
case "read-write":
return READWRITE;
case "trycreate":
return TRY_CREATE;
case "uidvalidity":
return UIDVALIDITY;
case "uidnext":
return UIDNEXT;
case "unseen":
return UNSEEN;
default:
throw new ImapError.PARSE_ERROR("Unknown response code \"%s\"", value);
}
}
public static ResponseCodeType from_parameter(StringParameter stringp) throws ImapError {
return decode(stringp.value);
}
public StringParameter to_parameter() {
return new StringParameter(to_string());
}
}

View file

@ -0,0 +1,14 @@
/* Copyright 2011 Yorba Foundation
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
public abstract class Geary.Imap.CommandResults {
public StatusResponse status_response { get; private set; }
public CommandResults(StatusResponse status_response) {
this.status_response = status_response;
}
}

View file

@ -12,16 +12,18 @@
* results for all messages specified. * results for all messages specified.
*/ */
public class Geary.Imap.FetchResults { public class Geary.Imap.FetchResults : Geary.Imap.CommandResults {
public int msg_num { get; private set; } public int msg_num { get; private set; }
private Gee.Map<FetchDataType, MessageData> map = new Gee.HashMap<FetchDataType, MessageData>(); private Gee.Map<FetchDataType, MessageData> map = new Gee.HashMap<FetchDataType, MessageData>();
public FetchResults(int msg_num) { public FetchResults(StatusResponse status_response, int msg_num) {
base (status_response);
this.msg_num = msg_num; this.msg_num = msg_num;
} }
public static FetchResults decode_data(ServerData data) throws ImapError { public static FetchResults decode_data(StatusResponse status_response, ServerData data) throws ImapError {
StringParameter msg_num = data.get_as_string(1); StringParameter msg_num = data.get_as_string(1);
StringParameter cmd = data.get_as_string(2); StringParameter cmd = data.get_as_string(2);
ListParameter list = data.get_as_list(3); ListParameter list = data.get_as_list(3);
@ -32,7 +34,7 @@ public class Geary.Imap.FetchResults {
data.to_string()); data.to_string());
} }
FetchResults results = new FetchResults(msg_num.as_int()); FetchResults results = new FetchResults(status_response, msg_num.as_int());
// walk the list for each returned fetch data item, which is paired by its data item name // walk the list for each returned fetch data item, which is paired by its data item name
// and the structured data itself // and the structured data itself
@ -59,7 +61,7 @@ public class Geary.Imap.FetchResults {
FetchResults[] array = new FetchResults[0]; FetchResults[] array = new FetchResults[0];
foreach (ServerData data in response.server_data) { foreach (ServerData data in response.server_data) {
try { try {
array += decode_data(data); array += decode_data(response.status_response, data);
} catch (ImapError ierr) { } catch (ImapError ierr) {
// drop bad data on the ground // drop bad data on the ground
debug("Dropping FETCH data \"%s\": %s", data.to_string(), ierr.message); debug("Dropping FETCH data \"%s\": %s", data.to_string(), ierr.message);

View file

@ -4,8 +4,8 @@
* (version 2.1 or later). See the COPYING file in this distribution. * (version 2.1 or later). See the COPYING file in this distribution.
*/ */
public class Geary.Imap.FolderDetail { public class Geary.Imap.FolderDetail : Object, Geary.FolderDetail {
public string name { get; private set; } public string name { get; protected set; }
public string delim { get; private set; } public string delim { get; private set; }
public MailboxAttributes attrs { get; private set; } public MailboxAttributes attrs { get; private set; }
@ -16,10 +16,12 @@ public class Geary.Imap.FolderDetail {
} }
} }
public class Geary.Imap.ListResults { public class Geary.Imap.ListResults : Geary.Imap.CommandResults {
private Gee.HashMap<string, FolderDetail> map = new Gee.HashMap<string, FolderDetail>(); private Gee.HashMap<string, FolderDetail> map = new Gee.HashMap<string, FolderDetail>();
private ListResults(Gee.Collection<FolderDetail> details) { public ListResults(StatusResponse status_response, Gee.Collection<FolderDetail> details) {
base (status_response);
foreach (FolderDetail detail in details) foreach (FolderDetail detail in details)
map.set(detail.name, detail); map.set(detail.name, detail);
} }
@ -61,7 +63,7 @@ public class Geary.Imap.ListResults {
} }
} }
return new ListResults(details); return new ListResults(response.status_response, details);
} }
public Gee.Collection<string> get_names() { public Gee.Collection<string> get_names() {

View file

@ -4,8 +4,7 @@
* (version 2.1 or later). See the COPYING file in this distribution. * (version 2.1 or later). See the COPYING file in this distribution.
*/ */
public class Geary.Imap.NoopResults { public class Geary.Imap.NoopResults : Geary.Imap.CommandResults {
public StatusResponse status_response { get; private set; }
public Gee.List<MessageNumber>? expunged { get; private set; } public Gee.List<MessageNumber>? expunged { get; private set; }
/** /**
* -1 if "exists" result not returned by server. * -1 if "exists" result not returned by server.
@ -19,14 +18,15 @@ public class Geary.Imap.NoopResults {
public NoopResults(StatusResponse status_response, Gee.List<MessageNumber>? expunged, int exists, public NoopResults(StatusResponse status_response, Gee.List<MessageNumber>? expunged, int exists,
Gee.List<FetchResults>? flags, int recent) { Gee.List<FetchResults>? flags, int recent) {
this.status_response = status_response; base (status_response);
this.expunged = expunged; this.expunged = expunged;
this.exists = exists; this.exists = exists;
this.flags = flags; this.flags = flags;
this.recent = recent; this.recent = recent;
} }
public static NoopResults decode(CommandResponse response) throws ImapError { public static NoopResults decode(CommandResponse response) {
assert(response.is_sealed()); assert(response.is_sealed());
Gee.List<MessageNumber> expunged = new Gee.ArrayList<MessageNumber>(); Gee.List<MessageNumber> expunged = new Gee.ArrayList<MessageNumber>();
@ -53,7 +53,7 @@ public class Geary.Imap.NoopResults {
break; break;
case ServerDataType.FETCH: case ServerDataType.FETCH:
flags.add(FetchResults.decode_data(data)); flags.add(FetchResults.decode_data(response.status_response, data));
break; break;
default: default:

View file

@ -0,0 +1,131 @@
/* Copyright 2011 Yorba Foundation
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
public class Geary.Imap.SelectExamineResults : Geary.Imap.CommandResults {
/**
* -1 if not specified.
*/
public int exists { get; private set; }
/**
* -1 if not specified.
*/
public int recent { get; private set; }
/**
* -1 if not specified.
*/
public int unseen { get; private set; }
public UID? uidvalidity { get; private set; }
public Flags? flags { get; private set; }
public Flags? permanentflags { get; private set; }
public bool readonly { get; private set; }
private SelectExamineResults(StatusResponse status_response, int exists, int recent, int unseen,
UID? uidvalidity, Flags? flags, Flags? permanentflags, bool readonly) {
base (status_response);
this.exists = exists;
this.recent = recent;
this.unseen = unseen;
this.uidvalidity = uidvalidity;
this.flags = flags;
this.permanentflags = permanentflags;
this.readonly = readonly;
}
public static SelectExamineResults decode(CommandResponse response) throws ImapError {
assert(response.is_sealed());
int exists = -1;
int recent = -1;
int unseen = -1;
UID? uidvalidity = null;
UID? uidnext = null;
MessageFlags? flags = null;
MessageFlags? permanentflags = null;
bool readonly = true;
try {
readonly = response.status_response.response_code.get_as_string(0).value.down() != "read-write";
} catch (ImapError ierr) {
message("Invalid SELECT/EXAMINE read-write indicator: %s",
response.status_response.to_string());
}
foreach (ServerData data in response.server_data) {
try {
StringParameter stringp = data.get_as_string(1);
switch (stringp.value.down()) {
case "ok":
// ok lines are structured like StatusResponses
StatusResponse ok_response = new StatusResponse.reconstitute(data);
if (ok_response.response_code == null) {
message("Invalid SELECT/EXAMINE response \"%s\": no response code",
data.to_string());
break;
}
// the ResponseCode is what we're interested in
switch (ok_response.response_code.get_code_type()) {
case ResponseCodeType.UNSEEN:
unseen = ok_response.response_code.get_as_string(1).as_int(0, int.MAX);
break;
case ResponseCodeType.UIDVALIDITY:
uidvalidity = new UID(
ok_response.response_code.get_as_string(1).as_int());
break;
case ResponseCodeType.UIDNEXT:
uidnext = new UID(ok_response.response_code.get_as_string(1).as_int());
break;
case ResponseCodeType.PERMANENT_FLAGS:
permanentflags = MessageFlags.from_list(
ok_response.response_code.get_as_list(1));
break;
default:
message("Unknown line in SELECT/EXAMINE response: \"%s\"", data.to_string());
break;
}
break;
case "flags":
flags = MessageFlags.from_list(data.get_as_list(2));
break;
default:
// if second parameter is a type descriptor, stringp is an ordinal
switch (ServerDataType.from_parameter(data.get_as_string(2))) {
case ServerDataType.EXISTS:
exists = stringp.as_int(0, int.MAX);
break;
case ServerDataType.RECENT:
recent = stringp.as_int(0, int.MAX);
break;
default:
message("Unknown line in SELECT/EXAMINE response: \"%s\"", data.to_string());
break;
}
break;
}
} catch (ImapError ierr) {
message("SELECT/EXAMINE decode error for \"%s\": %s", data.to_string(), ierr.message);
}
}
// flags, exists, and recent are required
if (flags == null || exists < 0 || recent < 0)
throw new ImapError.PARSE_ERROR("Incomplete SELECT/EXAMINE Response: \"%s\"", response.to_string());
return new SelectExamineResults(response.status_response, exists, recent, unseen,
uidvalidity, flags, permanentflags, readonly);
}
}