Beispiel #1
0
        public void TestParseResponseCodeBadUrl()
        {
            const string text = "BADURL \"/INBOX;UIDVALIDITY=785799047/;UID=113330;section=1.5.9\"] CATENATE append has failed, one message expunged\r\n";

            using (var memory = new MemoryStream(Encoding.ASCII.GetBytes(text), false)) {
                using (var tokenizer = new ImapStream(memory, new NullProtocolLogger())) {
                    using (var engine = new ImapEngine(null)) {
                        ImapResponseCode respCode;

                        engine.SetStream(tokenizer);

                        try {
                            respCode = engine.ParseResponseCodeAsync(true, false, CancellationToken.None).GetAwaiter().GetResult();
                        } catch (Exception ex) {
                            Assert.Fail("Parsing RESP-CODE failed: {0}", ex);
                            return;
                        }

                        Assert.AreEqual(ImapResponseCodeType.BadUrl, respCode.Type);
                        Assert.AreEqual("CATENATE append has failed, one message expunged", respCode.Message);

                        var badurl = (BadUrlResponseCode)respCode;
                        Assert.AreEqual("/INBOX;UIDVALIDITY=785799047/;UID=113330;section=1.5.9", badurl.BadUrl);
                    }
                }
            }
        }
Beispiel #2
0
        public void TestParseResponseCodeBadCharset()
        {
            const string text = "BADCHARSET (US-ASCII \"iso-8859-1\" UTF-8)] This is some free-form text\r\n";

            using (var memory = new MemoryStream(Encoding.ASCII.GetBytes(text), false)) {
                using (var tokenizer = new ImapStream(memory, new NullProtocolLogger())) {
                    using (var engine = new ImapEngine(null)) {
                        ImapResponseCode respCode;

                        engine.SetStream(tokenizer);

                        try {
                            respCode = engine.ParseResponseCodeAsync(true, false, CancellationToken.None).GetAwaiter().GetResult();
                        } catch (Exception ex) {
                            Assert.Fail("Parsing RESP-CODE failed: {0}", ex);
                            return;
                        }

                        Assert.AreEqual(ImapResponseCodeType.BadCharset, respCode.Type);
                        Assert.AreEqual("This is some free-form text", respCode.Message);

                        Assert.AreEqual(3, engine.SupportedCharsets.Count);
                        Assert.IsTrue(engine.SupportedCharsets.Contains("US-ASCII"), "US-ASCII");
                        Assert.IsTrue(engine.SupportedCharsets.Contains("iso-8859-1"), "iso-8859-1");
                        Assert.IsTrue(engine.SupportedCharsets.Contains("UTF-8"), "UTF-8");
                    }
                }
            }
        }
Beispiel #3
0
        public void TestParseResponseCodeUndefinedFilter()
        {
            const string text = "UNDEFINED-FILTER filter-name] This is some free-form text\r\n";

            using (var memory = new MemoryStream(Encoding.ASCII.GetBytes(text), false)) {
                using (var tokenizer = new ImapStream(memory, new NullProtocolLogger())) {
                    using (var engine = new ImapEngine(null)) {
                        ImapResponseCode respCode;

                        engine.SetStream(tokenizer);

                        try {
                            respCode = engine.ParseResponseCodeAsync(true, false, CancellationToken.None).GetAwaiter().GetResult();
                        } catch (Exception ex) {
                            Assert.Fail("Parsing RESP-CODE failed: {0}", ex);
                            return;
                        }

                        Assert.AreEqual(ImapResponseCodeType.UndefinedFilter, respCode.Type);
                        Assert.AreEqual("This is some free-form text", respCode.Message);

                        var undefined = (UndefinedFilterResponseCode)respCode;
                        Assert.AreEqual("filter-name", undefined.Name);
                    }
                }
            }
        }
Beispiel #4
0
        public void TestParseResponseCodeNoUpdate()
        {
            const string text = "NOUPDATE \"B02\"] Too many contexts\r\n";

            using (var memory = new MemoryStream(Encoding.ASCII.GetBytes(text), false)) {
                using (var tokenizer = new ImapStream(memory, new NullProtocolLogger())) {
                    using (var engine = new ImapEngine(null)) {
                        ImapResponseCode respCode;

                        engine.SetStream(tokenizer);

                        try {
                            respCode = engine.ParseResponseCodeAsync(false, false, CancellationToken.None).GetAwaiter().GetResult();
                        } catch (Exception ex) {
                            Assert.Fail("Parsing RESP-CODE failed: {0}", ex);
                            return;
                        }

                        Assert.AreEqual(ImapResponseCodeType.NoUpdate, respCode.Type);
                        Assert.AreEqual("Too many contexts", respCode.Message);

                        var noupdate = (NoUpdateResponseCode)respCode;
                        Assert.AreEqual("B02", noupdate.Tag);
                    }
                }
            }
        }
Beispiel #5
0
        public void TestParseResponseCodeMaxConvertParts()
        {
            const string text = "MAXCONVERTPARTS 1] This is some free-form text\r\n";

            using (var memory = new MemoryStream(Encoding.ASCII.GetBytes(text), false)) {
                using (var tokenizer = new ImapStream(memory, new NullProtocolLogger())) {
                    using (var engine = new ImapEngine(null)) {
                        ImapResponseCode respCode;

                        engine.SetStream(tokenizer);

                        try {
                            respCode = engine.ParseResponseCodeAsync(true, false, CancellationToken.None).GetAwaiter().GetResult();
                        } catch (Exception ex) {
                            Assert.Fail("Parsing RESP-CODE failed: {0}", ex);
                            return;
                        }

                        Assert.AreEqual(ImapResponseCodeType.MaxConvertParts, respCode.Type);
                        Assert.AreEqual("This is some free-form text", respCode.Message);

                        var maxconvert = (MaxConvertResponseCode)respCode;
                        Assert.AreEqual(1, maxconvert.MaxConvert);
                    }
                }
            }
        }
Beispiel #6
0
        public void TestParseResponseCodeNewName()
        {
            const string text = "NEWNAME OldName NewName] This is some free-form text\r\n";

            using (var memory = new MemoryStream(Encoding.ASCII.GetBytes(text), false)) {
                using (var tokenizer = new ImapStream(memory, null, new NullProtocolLogger())) {
                    using (var engine = new ImapEngine(null)) {
                        ImapResponseCode respCode;

                        engine.SetStream(tokenizer);

                        try {
                            respCode = engine.ParseResponseCodeAsync(false, CancellationToken.None).GetAwaiter().GetResult();
                        } catch (Exception ex) {
                            Assert.Fail("Parsing RESP-CODE failed: {0}", ex);
                            return;
                        }

                        Assert.AreEqual(ImapResponseCodeType.NewName, respCode.Type);
                        Assert.AreEqual("This is some free-form text", respCode.Message);

                        var newname = (NewNameResponseCode)respCode;
                        Assert.AreEqual("OldName", newname.OldName);
                        Assert.AreEqual("NewName", newname.NewName);
                    }
                }
            }
        }
Beispiel #7
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);
        }