Пример #1
0
        /// <summary>
        /// Sends the next part of the command to the server.
        /// </summary>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        /// <exception cref="ImapProtocolException">
        /// An IMAP protocol error occurred.
        /// </exception>
        public async Task <bool> StepAsync(bool doAsync)
        {
            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(CultureInfo.InvariantCulture, "{0}{1:D8}", Engine.TagPrefix, Engine.Tag++);

                var buf = Encoding.ASCII.GetBytes(Tag + " ");

                if (doAsync)
                {
                    await Engine.Stream.WriteAsync(buf, 0, buf.Length, CancellationToken).ConfigureAwait(false);
                }
                else
                {
                    Engine.Stream.Write(buf, 0, buf.Length, CancellationToken);
                }
            }

            do
            {
                var command = parts[current].Command;

                if (doAsync)
                {
                    await Engine.Stream.WriteAsync(command, 0, command.Length, CancellationToken).ConfigureAwait(false);
                }
                else
                {
                    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...
                await parts[current].Literal.WriteToAsync(Engine.Stream, doAsync, CancellationToken).ConfigureAwait(false);

                if (current + 1 >= parts.Count)
                {
                    break;
                }

                current++;
            } while (true);

            if (doAsync)
            {
                await Engine.Stream.FlushAsync(CancellationToken).ConfigureAwait(false);
            }
            else
            {
                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 = await Engine.ReadTokenAsync(doAsync, idle.LinkedToken).ConfigureAwait(false);

                        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 = await Engine.ReadTokenAsync(doAsync, CancellationToken).ConfigureAwait(false);
                    }
                }
                else
                {
                    token = await Engine.ReadTokenAsync(doAsync, CancellationToken).ConfigureAwait(false);
                }

                if (token.Type == ImapTokenType.Atom && token.Value.ToString() == "+")
                {
                    // we've gotten a continuation response from the server
                    var text = (await Engine.ReadLineAsync(doAsync, CancellationToken).ConfigureAwait(false)).Trim();

                    // if we've got a Literal pending, the '+' means we can send it now...
                    if (!supportsLiteralPlus && parts[current].Literal != null)
                    {
                        await parts[current].Literal.WriteToAsync(Engine.Stream, doAsync, CancellationToken).ConfigureAwait(false);
                        break;
                    }

                    if (ContinuationHandler != null)
                    {
                        await ContinuationHandler(Engine, this, text, doAsync).ConfigureAwait(false);
                    }
                    else if (doAsync)
                    {
                        await Engine.Stream.WriteAsync(NewLine, 0, NewLine.Length, CancellationToken).ConfigureAwait(false);

                        await Engine.Stream.FlushAsync(CancellationToken).ConfigureAwait(false);
                    }
                    else
                    {
                        Engine.Stream.Write(NewLine, 0, NewLine.Length, CancellationToken);
                        Engine.Stream.Flush(CancellationToken);
                    }
                }
                else if (token.Type == ImapTokenType.Asterisk)
                {
                    // we got an untagged response, let the engine handle this...
                    await Engine.ProcessUntaggedResponseAsync(doAsync, CancellationToken).ConfigureAwait(false);
                }
                else if (token.Type == ImapTokenType.Atom && (string)token.Value == Tag)
                {
                    // the next token should be "OK", "NO", or "BAD"
                    token = await Engine.ReadTokenAsync(doAsync, CancellationToken).ConfigureAwait(false);

                    ImapEngine.AssertToken(token, ImapTokenType.Atom, "Syntax error in tagged response. Unexpected token: {0}", token);

                    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 = await Engine.ReadTokenAsync(doAsync, CancellationToken).ConfigureAwait(false);

                    if (token.Type == ImapTokenType.OpenBracket)
                    {
                        var code = await Engine.ParseResponseCodeAsync(true, doAsync, CancellationToken).ConfigureAwait(false);

                        RespCodes.Add(code);
                        break;
                    }

                    if (token.Type != ImapTokenType.Eoln)
                    {
                        // consume the rest of the line...
                        var line = await Engine.ReadLineAsync(doAsync, CancellationToken).ConfigureAwait(false);

                        ResponseText = ((string)(token.Value) + line).TrimEnd();
                        break;
                    }
                }
                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 = await Engine.ParseResponseCodeAsync(false, doAsync, CancellationToken).ConfigureAwait(false);

                    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);
        }