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