public bool Step ()
{
var supportsLiteralPlus = (Engine.Capabilities & ImapCapabilities.LiteralPlus) != 0;
int timeout = Engine.Stream.CanTimeout ? Engine.Stream.ReadTimeout : -1;
var idle = UserData as ImapIdleContext;
var result = ImapCommandResponse.None;
ImapToken token;
// construct and write the command tag if this is the initial state
if (current == 0) {
Tag = string.Format ("{0}{1:D8}", Engine.TagPrefix, Engine.Tag++);
var buf = Encoding.ASCII.GetBytes (Tag + " ");
Engine.Stream.Write (buf, 0, buf.Length, CancellationToken);
}
do {
var command = parts[current].Command;
Engine.Stream.Write (command, 0, command.Length, CancellationToken);
// if the server doesn't support LITERAL+ (or LITERAL-), we'll need to wait
// for a "+" response before writing out the any literals...
if (parts[current].WaitForContinuation)
break;
// otherwise, we can write out any and all literal tokens we have...
parts[current].Literal.WriteTo (Engine.Stream, CancellationToken);
if (current + 1 >= parts.Count)
break;
current++;
} while (true);
Engine.Stream.Flush (CancellationToken);
// now we need to read the response...
do {
if (Engine.State == ImapEngineState.Idle) {
try {
if (Engine.Stream.CanTimeout)
Engine.Stream.ReadTimeout = -1;
token = Engine.ReadToken (idle.LinkedToken);
if (Engine.Stream.CanTimeout)
Engine.Stream.ReadTimeout = timeout;
} catch (OperationCanceledException) {
if (Engine.Stream.CanTimeout)
Engine.Stream.ReadTimeout = timeout;
if (idle.IsCancellationRequested)
throw;
Engine.Stream.IsConnected = true;
token = Engine.ReadToken (CancellationToken);
}
} else {
token = Engine.ReadToken (CancellationToken);
}
if (token.Type == ImapTokenType.Atom && token.Value.ToString () == "+") {
// we've gotten a continuation response from the server
var text = Engine.ReadLine (CancellationToken).Trim ();
// if we've got a Literal pending, the '+' means we can send it now...
if (!supportsLiteralPlus && parts[current].Literal != null) {
parts[current].Literal.WriteTo (Engine.Stream, CancellationToken);
break;
}
Debug.Assert (ContinuationHandler != null, "The ImapCommand's ContinuationHandler is null");
ContinuationHandler (Engine, this, text);
} else if (token.Type == ImapTokenType.Asterisk) {
// we got an untagged response, let the engine handle this...
Engine.ProcessUntaggedResponse (CancellationToken);
} else if (token.Type == ImapTokenType.Atom && (string) token.Value == Tag) {
// the next token should be "OK", "NO", or "BAD"
token = Engine.ReadToken (CancellationToken);
if (token.Type == ImapTokenType.Atom) {
string atom = (string) token.Value;
switch (atom) {
case "BAD": result = ImapCommandResponse.Bad; break;
case "OK": result = ImapCommandResponse.Ok; break;
case "NO": result = ImapCommandResponse.No; break;
default: throw ImapEngine.UnexpectedToken ("Syntax error in tagged response. Unexpected token: {0}", token);
}
token = Engine.ReadToken (CancellationToken);
if (token.Type == ImapTokenType.OpenBracket) {
var code = Engine.ParseResponseCode (CancellationToken);
RespCodes.Add (code);
break;
}
if (token.Type != ImapTokenType.Eoln) {
// consume the rest of the line...
ResponseText = ((string) (token.Value) + Engine.ReadLine (CancellationToken)).TrimEnd ();
break;
}
} else {
// looks like we didn't get an "OK", "NO", or "BAD"...
throw ImapEngine.UnexpectedToken ("Syntax error in tagged response. Unexpected token: {0}", token);
}
} else if (token.Type == ImapTokenType.OpenBracket) {
// Note: this is a work-around for broken IMAP servers like Office365.com that
// return RESP-CODES that are not preceded by "* OK " such as the example in
// issue #115 (https://github.com/jstedfast/MailKit/issues/115).
var code = Engine.ParseResponseCode (CancellationToken);
RespCodes.Add (code);
} else {
// no clue what we got...
throw ImapEngine.UnexpectedToken ("Syntax error in response. Unexpected token: {0}", token);
}
} while (true);
// the status should always be Active at this point, but just to be sure...
if (Status == ImapCommandStatus.Active) {
current++;
if (current >= parts.Count || result != ImapCommandResponse.None) {
Status = ImapCommandStatus.Complete;
Response = result;
return false;
}
}
return true;
}
}