Added NOOP idling support to ClientSession.
ClientSession can now automatically send NOOP commands as keepalives at a specified interval. Also, the CommandResponse decoders have been moved into their own directory (they will soon be fertile and multiply). More work ahead on the FetchResults object, which should be like NoopResults and completely encapsulate all the information returned from a FETCH.
This commit is contained in:
parent
e56a6a599a
commit
5021d8372d
13 changed files with 408 additions and 53 deletions
20
Makefile
20
Makefile
|
|
@ -3,7 +3,7 @@ BUILD_ROOT = 1
|
|||
|
||||
VALAC := valac
|
||||
|
||||
APPS := console syntax lsmbox readmail
|
||||
APPS := console syntax lsmbox readmail watchmbox
|
||||
|
||||
ENGINE_SRC := \
|
||||
src/engine/Engine.vala \
|
||||
|
|
@ -20,20 +20,22 @@ ENGINE_SRC := \
|
|||
src/engine/imap/Tag.vala \
|
||||
src/engine/imap/Command.vala \
|
||||
src/engine/imap/Commands.vala \
|
||||
src/engine/imap/FetchCommand.vala \
|
||||
src/engine/imap/ResponseCode.vala \
|
||||
src/engine/imap/ServerResponse.vala \
|
||||
src/engine/imap/StatusResponse.vala \
|
||||
src/engine/imap/ServerData.vala \
|
||||
src/engine/imap/ServerDataType.vala \
|
||||
src/engine/imap/FetchDataType.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/imap/decoders/FetchDataDecoder.vala \
|
||||
src/engine/imap/decoders/FetchResults.vala \
|
||||
src/engine/imap/decoders/NoopResults.vala \
|
||||
src/engine/rfc822/MailboxAddress.vala \
|
||||
src/engine/rfc822/MessageData.vala \
|
||||
src/engine/util/String.vala \
|
||||
|
|
@ -51,7 +53,10 @@ LSMBOX_SRC := \
|
|||
READMAIL_SRC := \
|
||||
src/tests/readmail.vala
|
||||
|
||||
ALL_SRC := $(ENGINE_SRC) $(CONSOLE_SRC) $(SYNTAX_SRC) $(LSMBOX_SRC) $(READMAIL_SRC)
|
||||
WATCHMBOX_SRC := \
|
||||
src/tests/watchmbox.vala
|
||||
|
||||
ALL_SRC := $(ENGINE_SRC) $(CONSOLE_SRC) $(SYNTAX_SRC) $(LSMBOX_SRC) $(READMAIL_SRC) $(WATCHMBOX_SRC)
|
||||
|
||||
EXTERNAL_PKGS := \
|
||||
gio-2.0 \
|
||||
|
|
@ -86,3 +91,8 @@ readmail: $(ENGINE_SRC) $(READMAIL_SRC) Makefile
|
|||
$(ENGINE_SRC) $(READMAIL_SRC) \
|
||||
-o $@
|
||||
|
||||
watchmbox: $(ENGINE_SRC) $(WATCHMBOX_SRC) Makefile
|
||||
$(VALAC) --save-temps -g $(foreach pkg,$(EXTERNAL_PKGS),--pkg=$(pkg)) \
|
||||
$(ENGINE_SRC) $(WATCHMBOX_SRC) \
|
||||
-o $@
|
||||
|
||||
|
|
|
|||
|
|
@ -354,9 +354,9 @@ class ImapConsole : Gtk.Window {
|
|||
|
||||
status("Fetching %s".printf(args[0]));
|
||||
|
||||
Geary.Imap.FetchDataItem[] data_items = new Geary.Imap.FetchDataItem[0];
|
||||
Geary.Imap.FetchDataType[] data_items = new Geary.Imap.FetchDataType[0];
|
||||
for (int ctr = 1; ctr < args.length; ctr++)
|
||||
data_items += Geary.Imap.FetchDataItem.decode(args[ctr]);
|
||||
data_items += Geary.Imap.FetchDataType.decode(args[ctr]);
|
||||
|
||||
cx.post(new Geary.Imap.FetchCommand(cx.generate_tag(), args[0], data_items), on_fetch);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
*/
|
||||
|
||||
public class Geary.Imap.ClientSession : Object, Geary.Account {
|
||||
// 30 min keepalive required to maintain session; back off by 30 sec for breathing room
|
||||
public const int MIN_KEEPALIVE_SEC = (30 * 60) - 30;
|
||||
public const int DEFAULT_KEEPALIVE_SEC = 60;
|
||||
|
||||
// Need this because delegates with targets cannot be stored in ADTs.
|
||||
private class CommandCallback {
|
||||
public SourceFunc callback;
|
||||
|
|
@ -149,13 +153,26 @@ public class Geary.Imap.ClientSession : Object, Geary.Account {
|
|||
private Gee.Queue<CommandCallback> cb_queue = new Gee.LinkedList<CommandCallback>();
|
||||
private Gee.Queue<CommandResponse> cmd_response_queue = new Gee.LinkedList<CommandResponse>();
|
||||
private CommandResponse current_cmd_response = new CommandResponse();
|
||||
|
||||
private uint keepalive_id = 0;
|
||||
|
||||
// state used only during connect and disconnect
|
||||
private bool awaiting_connect_response = false;
|
||||
private ServerData? connect_response = null;
|
||||
private AsyncParams? connect_params = null;
|
||||
private AsyncParams? disconnect_params = null;
|
||||
|
||||
public virtual signal void unsolicited_expunged(MessageNumber msg) {
|
||||
}
|
||||
|
||||
public virtual signal void unsolicited_exists(int exists) {
|
||||
}
|
||||
|
||||
public virtual signal void unsolicitied_recent(int recent) {
|
||||
}
|
||||
|
||||
public virtual signal void unsolicited_flags(FetchResults flags) {
|
||||
}
|
||||
|
||||
public ClientSession(string server, uint default_port) {
|
||||
this.server = server;
|
||||
this.default_port = default_port;
|
||||
|
|
@ -422,6 +439,75 @@ public class Geary.Imap.ClientSession : Object, Geary.Account {
|
|||
return State.NOAUTH;
|
||||
}
|
||||
|
||||
//
|
||||
// keepalives (nop idling to keep the session alive and to periodically receive notifications
|
||||
// of changes)
|
||||
//
|
||||
|
||||
/**
|
||||
* Returns true if keepalives are activated, false if already enabled.
|
||||
*/
|
||||
public bool enable_keepalives(int seconds = DEFAULT_KEEPALIVE_SEC) {
|
||||
if (keepalive_id != 0)
|
||||
return false;
|
||||
|
||||
keepalive_id = Timeout.add_seconds(seconds, on_keepalive);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if keepalives are disactivated, false if already disabled.
|
||||
*/
|
||||
public bool disable_keepalives() {
|
||||
if (keepalive_id == 0)
|
||||
return false;
|
||||
|
||||
Source.remove(keepalive_id);
|
||||
keepalive_id = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool on_keepalive() {
|
||||
send_command_async.begin(new NoopCommand(generate_tag()), null, on_keepalive_completed);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void on_keepalive_completed(Object? source, AsyncResult result) {
|
||||
NoopResults results;
|
||||
try {
|
||||
results = NoopResults.decode(send_command_async.end(result));
|
||||
} catch (Error err) {
|
||||
message("Keepalive error: %s", err.message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (results.status_response.status != Status.OK) {
|
||||
debug("Keepalive failed: %s", results.status_response.to_string());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (results.expunged != null) {
|
||||
foreach (MessageNumber msg in results.expunged)
|
||||
unsolicited_expunged(msg);
|
||||
}
|
||||
|
||||
if (results.has_exists())
|
||||
unsolicited_exists(results.exists);
|
||||
|
||||
if (results.has_recent())
|
||||
unsolicitied_recent(results.recent);
|
||||
|
||||
if (results.flags != null) {
|
||||
foreach (FetchResults flags in results.flags)
|
||||
unsolicited_flags(flags);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// send commands
|
||||
//
|
||||
|
|
@ -739,7 +825,9 @@ public class Geary.Imap.ClientSession : Object, Geary.Account {
|
|||
}
|
||||
|
||||
private uint on_ignored_transition(uint state, uint event) {
|
||||
#if VERBOSE_SESSION
|
||||
debug("Ignored transition: %s@%s", fsm.get_event_string(event), fsm.get_state_string(state));
|
||||
#endif
|
||||
|
||||
return state;
|
||||
}
|
||||
|
|
@ -813,15 +901,21 @@ public class Geary.Imap.ClientSession : Object, Geary.Account {
|
|||
//
|
||||
|
||||
private void on_network_connected() {
|
||||
#if VERBOSE_SESSION
|
||||
debug("Connected to %s", server);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void on_network_disconnected() {
|
||||
#if VERBOSE_SESSION
|
||||
debug("Disconnected from %s", server);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void on_network_sent_command(Command cmd) {
|
||||
#if VERBOSE_SESSION
|
||||
debug("Sent command %s", cmd.to_string());
|
||||
#endif
|
||||
}
|
||||
|
||||
private void on_received_status_response(StatusResponse status_response) {
|
||||
|
|
|
|||
|
|
@ -76,3 +76,24 @@ public class Geary.Imap.CloseCommand : Command {
|
|||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.FetchCommand : Command {
|
||||
public const string NAME = "fetch";
|
||||
|
||||
public FetchCommand(Tag tag, string msg_span, FetchDataType[] data_items) {
|
||||
base (tag, NAME);
|
||||
|
||||
add(new StringParameter(msg_span));
|
||||
|
||||
assert(data_items.length > 0);
|
||||
if (data_items.length == 1) {
|
||||
add(data_items[0].to_parameter());
|
||||
} else {
|
||||
ListParameter data_item_list = new ListParameter(this);
|
||||
foreach (FetchDataType data_item in data_items)
|
||||
data_item_list.add(data_item.to_parameter());
|
||||
|
||||
add(data_item_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
// TODO: Support body[section]<partial> and body.peek[section]<partial> forms
|
||||
public enum Geary.Imap.FetchDataItem {
|
||||
public enum Geary.Imap.FetchDataType {
|
||||
UID,
|
||||
FLAGS,
|
||||
INTERNALDATE,
|
||||
|
|
@ -66,7 +66,7 @@ public enum Geary.Imap.FetchDataItem {
|
|||
}
|
||||
}
|
||||
|
||||
public static FetchDataItem decode(string value) throws ImapError {
|
||||
public static FetchDataType decode(string value) throws ImapError {
|
||||
switch (value.down()) {
|
||||
case "uid":
|
||||
return UID;
|
||||
|
|
@ -116,7 +116,7 @@ public enum Geary.Imap.FetchDataItem {
|
|||
return new StringParameter(to_string());
|
||||
}
|
||||
|
||||
public static FetchDataItem from_parameter(StringParameter strparam) throws ImapError {
|
||||
public static FetchDataType from_parameter(StringParameter strparam) throws ImapError {
|
||||
return decode(strparam.value);
|
||||
}
|
||||
|
||||
|
|
@ -152,24 +152,3 @@ public enum Geary.Imap.FetchDataItem {
|
|||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.FetchCommand : Command {
|
||||
public const string NAME = "fetch";
|
||||
|
||||
public FetchCommand(Tag tag, string msg_span, FetchDataItem[] data_items) {
|
||||
base (tag, NAME);
|
||||
|
||||
add(new StringParameter(msg_span));
|
||||
|
||||
assert(data_items.length > 0);
|
||||
if (data_items.length == 1) {
|
||||
add(data_items[0].to_parameter());
|
||||
} else {
|
||||
ListParameter data_item_list = new ListParameter(this);
|
||||
foreach (FetchDataItem data_item in data_items)
|
||||
data_item_list.add(data_item.to_parameter());
|
||||
|
||||
add(data_item_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ private class Geary.Imap.MessageStreamImpl : Object, Geary.MessageStream {
|
|||
|
||||
public async Gee.List<Message>? read(Cancellable? cancellable = null) throws Error {
|
||||
CommandResponse resp = yield sess.send_command_async(new FetchCommand(sess.generate_tag(),
|
||||
span, { FetchDataItem.ENVELOPE }), cancellable);
|
||||
span, { FetchDataType.ENVELOPE }), cancellable);
|
||||
|
||||
if (resp.status_response.status != Status.OK)
|
||||
throw new ImapError.SERVER_ERROR(resp.status_response.text);
|
||||
|
|
@ -41,7 +41,7 @@ private class Geary.Imap.MessageStreamImpl : Object, Geary.MessageStream {
|
|||
|
||||
FetchResults[] results = FetchResults.decode(resp);
|
||||
foreach (FetchResults res in results) {
|
||||
Envelope envelope = (Envelope) res.get_data(FetchDataItem.ENVELOPE);
|
||||
Envelope envelope = (Envelope) res.get_data(FetchDataType.ENVELOPE);
|
||||
msgs.add(new Message(res.msg_num, envelope.from, envelope.subject, envelope.sent));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,12 @@ public class Geary.Imap.UID : Geary.Common.IntMessageData, Geary.Imap.MessageDat
|
|||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.MessageNumber : Geary.Common.IntMessageData, Geary.Imap.MessageData {
|
||||
public MessageNumber(int value) {
|
||||
base (value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.Imap.Flag {
|
||||
public static Flag ANSWERED = new Flag("\\answered");
|
||||
public static Flag DELETED = new Flag("\\deleted");
|
||||
|
|
|
|||
101
src/engine/imap/ServerDataType.vala
Normal file
101
src/engine/imap/ServerDataType.vala
Normal 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.ServerDataType {
|
||||
CAPABILITY,
|
||||
EXISTS,
|
||||
EXPUNGE,
|
||||
FETCH,
|
||||
FLAGS,
|
||||
LIST,
|
||||
LSUB,
|
||||
RECENT,
|
||||
SEARCH,
|
||||
STATUS;
|
||||
|
||||
public string to_string() {
|
||||
switch (this) {
|
||||
case CAPABILITY:
|
||||
return "capability";
|
||||
|
||||
case EXISTS:
|
||||
return "exists";
|
||||
|
||||
case EXPUNGE:
|
||||
return "expunge";
|
||||
|
||||
case FETCH:
|
||||
return "fetch";
|
||||
|
||||
case FLAGS:
|
||||
return "flags";
|
||||
|
||||
case LIST:
|
||||
return "list";
|
||||
|
||||
case LSUB:
|
||||
return "lsub";
|
||||
|
||||
case RECENT:
|
||||
return "recent";
|
||||
|
||||
case SEARCH:
|
||||
return "search";
|
||||
|
||||
case STATUS:
|
||||
return "status";
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
public static ServerDataType decode(string value) throws ImapError {
|
||||
switch (value.down()) {
|
||||
case "capability":
|
||||
return CAPABILITY;
|
||||
|
||||
case "exists":
|
||||
return EXISTS;
|
||||
|
||||
case "expunge":
|
||||
return EXPUNGE;
|
||||
|
||||
case "fetch":
|
||||
return FETCH;
|
||||
|
||||
case "flags":
|
||||
return FLAGS;
|
||||
|
||||
case "list":
|
||||
return LIST;
|
||||
|
||||
case "lsub":
|
||||
return LSUB;
|
||||
|
||||
case "recent":
|
||||
return RECENT;
|
||||
|
||||
case "search":
|
||||
return SEARCH;
|
||||
|
||||
case "status":
|
||||
return STATUS;
|
||||
|
||||
default:
|
||||
throw new ImapError.PARSE_ERROR("\"%s\" is not a valid server data type", value);
|
||||
}
|
||||
}
|
||||
|
||||
public StringParameter to_parameter() {
|
||||
return new StringParameter(to_string());
|
||||
}
|
||||
|
||||
public static ServerDataType from_parameter(StringParameter param) throws ImapError {
|
||||
return decode(param.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -9,17 +9,17 @@
|
|||
* 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().
|
||||
* Note that FetchDataDecoders are keyed off of FetchDataType; new implementations should add
|
||||
* themselves to FetchDataType.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 FetchDataType data_item { get; private set; }
|
||||
|
||||
public FetchDataDecoder(FetchDataItem data_item) {
|
||||
public FetchDataDecoder(FetchDataType data_item) {
|
||||
this.data_item = data_item;
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ public abstract class Geary.Imap.FetchDataDecoder {
|
|||
|
||||
public class Geary.Imap.UIDDecoder : Geary.Imap.FetchDataDecoder {
|
||||
public UIDDecoder() {
|
||||
base (FetchDataItem.UID);
|
||||
base (FetchDataType.UID);
|
||||
}
|
||||
|
||||
protected override MessageData decode_string(StringParameter stringp) throws ImapError {
|
||||
|
|
@ -67,7 +67,7 @@ public class Geary.Imap.UIDDecoder : Geary.Imap.FetchDataDecoder {
|
|||
|
||||
public class Geary.Imap.FlagsDecoder : Geary.Imap.FetchDataDecoder {
|
||||
public FlagsDecoder() {
|
||||
base (FetchDataItem.FLAGS);
|
||||
base (FetchDataType.FLAGS);
|
||||
}
|
||||
|
||||
protected override MessageData decode_list(ListParameter listp) throws ImapError {
|
||||
|
|
@ -81,7 +81,7 @@ public class Geary.Imap.FlagsDecoder : Geary.Imap.FetchDataDecoder {
|
|||
|
||||
public class Geary.Imap.InternalDateDecoder : Geary.Imap.FetchDataDecoder {
|
||||
public InternalDateDecoder() {
|
||||
base (FetchDataItem.INTERNALDATE);
|
||||
base (FetchDataType.INTERNALDATE);
|
||||
}
|
||||
|
||||
protected override MessageData decode_string(StringParameter stringp) throws ImapError {
|
||||
|
|
@ -91,7 +91,7 @@ public class Geary.Imap.InternalDateDecoder : Geary.Imap.FetchDataDecoder {
|
|||
|
||||
public class Geary.Imap.RFC822SizeDecoder : Geary.Imap.FetchDataDecoder {
|
||||
public RFC822SizeDecoder() {
|
||||
base (FetchDataItem.RFC822_SIZE);
|
||||
base (FetchDataType.RFC822_SIZE);
|
||||
}
|
||||
|
||||
protected override MessageData decode_string(StringParameter stringp) throws ImapError {
|
||||
|
|
@ -101,7 +101,7 @@ public class Geary.Imap.RFC822SizeDecoder : Geary.Imap.FetchDataDecoder {
|
|||
|
||||
public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
|
||||
public EnvelopeDecoder() {
|
||||
base (FetchDataItem.ENVELOPE);
|
||||
base (FetchDataType.ENVELOPE);
|
||||
}
|
||||
|
||||
// TODO: This doesn't handle group lists (see Johnson, p.268)
|
||||
|
|
@ -149,7 +149,7 @@ public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
|
|||
|
||||
public class Geary.Imap.RFC822HeaderDecoder : Geary.Imap.FetchDataDecoder {
|
||||
public RFC822HeaderDecoder() {
|
||||
base (FetchDataItem.RFC822_HEADER);
|
||||
base (FetchDataType.RFC822_HEADER);
|
||||
}
|
||||
|
||||
protected override MessageData decode_literal(LiteralParameter literalp) throws ImapError {
|
||||
|
|
@ -159,7 +159,7 @@ public class Geary.Imap.RFC822HeaderDecoder : Geary.Imap.FetchDataDecoder {
|
|||
|
||||
public class Geary.Imap.RFC822TextDecoder : Geary.Imap.FetchDataDecoder {
|
||||
public RFC822TextDecoder() {
|
||||
base (FetchDataItem.RFC822_TEXT);
|
||||
base (FetchDataType.RFC822_TEXT);
|
||||
}
|
||||
|
||||
protected override MessageData decode_literal(LiteralParameter literalp) throws ImapError {
|
||||
|
|
@ -169,7 +169,7 @@ public class Geary.Imap.RFC822TextDecoder : Geary.Imap.FetchDataDecoder {
|
|||
|
||||
public class Geary.Imap.RFC822FullDecoder : Geary.Imap.FetchDataDecoder {
|
||||
public RFC822FullDecoder() {
|
||||
base (FetchDataItem.RFC822);
|
||||
base (FetchDataType.RFC822);
|
||||
}
|
||||
|
||||
protected override MessageData decode_literal(LiteralParameter literalp) throws ImapError {
|
||||
|
|
@ -15,13 +15,13 @@
|
|||
public class Geary.Imap.FetchResults {
|
||||
public int msg_num { get; private set; }
|
||||
|
||||
private Gee.Map<FetchDataItem, MessageData> map = new Gee.HashMap<FetchDataItem, MessageData>();
|
||||
private Gee.Map<FetchDataType, MessageData> map = new Gee.HashMap<FetchDataType, MessageData>();
|
||||
|
||||
public FetchResults(int msg_num) {
|
||||
this.msg_num = msg_num;
|
||||
}
|
||||
|
||||
private static FetchResults decode_data(ServerData data) throws ImapError {
|
||||
public static FetchResults decode_data(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));
|
||||
|
|
@ -38,7 +38,7 @@ public class Geary.Imap.FetchResults {
|
|||
// 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);
|
||||
FetchDataType data_item = FetchDataType.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",
|
||||
|
|
@ -54,6 +54,8 @@ public class Geary.Imap.FetchResults {
|
|||
}
|
||||
|
||||
public static FetchResults[] decode(CommandResponse response) throws ImapError {
|
||||
assert(response.is_sealed());
|
||||
|
||||
FetchResults[] array = new FetchResults[0];
|
||||
foreach (ServerData data in response.server_data)
|
||||
array += decode_data(data);
|
||||
|
|
@ -61,11 +63,11 @@ public class Geary.Imap.FetchResults {
|
|||
return array;
|
||||
}
|
||||
|
||||
public void set_data(FetchDataItem data_item, MessageData primitive) {
|
||||
public void set_data(FetchDataType data_item, MessageData primitive) {
|
||||
map.set(data_item, primitive);
|
||||
}
|
||||
|
||||
public MessageData? get_data(FetchDataItem data_item) {
|
||||
public MessageData? get_data(FetchDataType data_item) {
|
||||
return map.get(data_item);
|
||||
}
|
||||
|
||||
80
src/engine/imap/decoders/NoopResults.vala
Normal file
80
src/engine/imap/decoders/NoopResults.vala
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/* 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.NoopResults {
|
||||
public StatusResponse status_response { get; private set; }
|
||||
public Gee.List<MessageNumber>? expunged { get; private set; }
|
||||
/**
|
||||
* -1 if "exists" result not returned by server.
|
||||
*/
|
||||
public int exists { get; private set; }
|
||||
public Gee.List<FetchResults>? flags { get; private set; }
|
||||
/**
|
||||
* -1 if "recent" result not returned by server.
|
||||
*/
|
||||
public int recent { get; private set; }
|
||||
|
||||
public NoopResults(StatusResponse status_response, Gee.List<MessageNumber>? expunged, int exists,
|
||||
Gee.List<FetchResults>? flags, int recent) {
|
||||
this.status_response = status_response;
|
||||
this.expunged = expunged;
|
||||
this.exists = exists;
|
||||
this.flags = flags;
|
||||
this.recent = recent;
|
||||
}
|
||||
|
||||
public static NoopResults decode(CommandResponse response) throws ImapError {
|
||||
assert(response.is_sealed());
|
||||
|
||||
Gee.List<MessageNumber> expunged = new Gee.ArrayList<MessageNumber>();
|
||||
Gee.List<FetchResults> flags = new Gee.ArrayList<FetchResults>();
|
||||
int exists = -1;
|
||||
int recent = -1;
|
||||
|
||||
foreach (ServerData data in response.server_data) {
|
||||
try {
|
||||
int ordinal = data.get_as_string(1).as_int().clamp(-1, int.MAX);
|
||||
ServerDataType type = ServerDataType.from_parameter(data.get_as_string(2));
|
||||
|
||||
switch (type) {
|
||||
case ServerDataType.EXPUNGE:
|
||||
expunged.add(new MessageNumber(ordinal));
|
||||
break;
|
||||
|
||||
case ServerDataType.EXISTS:
|
||||
exists = ordinal;
|
||||
break;
|
||||
|
||||
case ServerDataType.RECENT:
|
||||
recent = ordinal;
|
||||
break;
|
||||
|
||||
case ServerDataType.FETCH:
|
||||
flags.add(FetchResults.decode_data(data));
|
||||
break;
|
||||
|
||||
default:
|
||||
message("NOOP server data type \"%s\" unrecognized", type.to_string());
|
||||
break;
|
||||
}
|
||||
} catch (ImapError ierr) {
|
||||
message("NOOP decode error for \"%s\": %s", data.to_string(), ierr.message);
|
||||
}
|
||||
}
|
||||
|
||||
return new NoopResults(response.status_response, (expunged.size > 0) ? expunged : null,
|
||||
exists, (flags.size > 0) ? flags : null, recent);
|
||||
}
|
||||
|
||||
public bool has_exists() {
|
||||
return exists >= 0;
|
||||
}
|
||||
|
||||
public bool has_recent() {
|
||||
return recent >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -18,13 +18,13 @@ async void async_start() {
|
|||
yield sess.examine_async(mailbox);
|
||||
|
||||
Geary.Imap.FetchCommand fetch = new Geary.Imap.FetchCommand(sess.generate_tag(),
|
||||
"%d".printf(msg_num), { Geary.Imap.FetchDataItem.RFC822 });
|
||||
"%d".printf(msg_num), { Geary.Imap.FetchDataType.RFC822 });
|
||||
Geary.Imap.CommandResponse resp = yield sess.send_command_async(fetch);
|
||||
Geary.Imap.FetchResults[] results = Geary.Imap.FetchResults.decode(resp);
|
||||
|
||||
assert(results.length == 1);
|
||||
Geary.RFC822.Full? full =
|
||||
results[0].get_data(Geary.Imap.FetchDataItem.RFC822) as Geary.RFC822.Full;
|
||||
results[0].get_data(Geary.Imap.FetchDataType.RFC822) as Geary.RFC822.Full;
|
||||
assert(full != null);
|
||||
|
||||
DataInputStream dins = new DataInputStream(full.buffer.get_input_stream());
|
||||
|
|
|
|||
62
src/tests/watchmbox.vala
Normal file
62
src/tests/watchmbox.vala
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
MainLoop? main_loop = null;
|
||||
Geary.Imap.ClientSession? sess = null;
|
||||
string? user = null;
|
||||
string? pass = null;
|
||||
string? mailbox = null;
|
||||
|
||||
void on_exists(int exists) {
|
||||
stdout.printf("EXISTS: %d\n", exists);
|
||||
}
|
||||
|
||||
void on_expunged(Geary.Imap.MessageNumber expunged) {
|
||||
stdout.printf("EXPUNGED: %d\n", expunged.value);
|
||||
}
|
||||
|
||||
void on_recent(int recent) {
|
||||
stdout.printf("RECENT: %d\n", recent);
|
||||
}
|
||||
|
||||
async void async_start() {
|
||||
try {
|
||||
yield sess.connect_async();
|
||||
yield sess.login_async(user, pass);
|
||||
yield sess.examine_async(mailbox);
|
||||
|
||||
sess.unsolicited_exists.connect(on_exists);
|
||||
sess.unsolicited_expunged.connect(on_expunged);
|
||||
sess.unsolicitied_recent.connect(on_recent);
|
||||
|
||||
sess.enable_keepalives(5);
|
||||
} catch (Error err) {
|
||||
debug("Error: %s", err.message);
|
||||
}
|
||||
}
|
||||
|
||||
int main(string[] args) {
|
||||
if (args.length < 4) {
|
||||
stderr.printf("usage: watchmbox <user> <pass> <mailbox>\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
main_loop = new MainLoop();
|
||||
|
||||
user = args[1];
|
||||
pass = args[2];
|
||||
mailbox = args[3];
|
||||
|
||||
sess = new Geary.Imap.ClientSession("imap.gmail.com", 993);
|
||||
async_start.begin();
|
||||
|
||||
stdout.printf("Watching %s, Ctrl+C to exit...\n", mailbox);
|
||||
main_loop.run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue