IList<AuthenticationSecret> DetectLoginSecrets (byte[] buffer, int offset, int endIndex)
		{
			var secrets = new List<AuthenticationSecret> ();
			int index = offset;

			if (commandState == ImapAuthCommandState.LoginNewLine)
				return EmptyAuthSecrets;

			if (commandState == ImapAuthCommandState.Login) {
				if (SkipText ("LOGIN ", buffer, ref index, endIndex))
					commandState = ImapAuthCommandState.UserName;

				if (index >= endIndex || commandState == ImapAuthCommandState.Error)
					return EmptyAuthSecrets;
			}

			if (commandState == ImapAuthCommandState.UserName) {
				if (SkipLoginToken (secrets, buffer, ref index, endIndex, (byte) ' ')) {
					commandState = ImapAuthCommandState.Password;
					ClearLoginTokenState ();
				}

				if (index >= endIndex || commandState == ImapAuthCommandState.Error)
					return secrets;
			}

			if (commandState == ImapAuthCommandState.Password) {
				if (SkipLoginToken (secrets, buffer, ref index, endIndex, (byte) '\r')) {
					commandState = ImapAuthCommandState.LoginNewLine;
					ClearLoginTokenState ();
				}
			}

			return secrets;
		}
		IList<AuthenticationSecret> DetectAuthSecrets (byte[] buffer, int offset, int endIndex)
		{
			int index = offset;

			if (commandState == ImapAuthCommandState.Authenticate) {
				if (SkipText ("AUTHENTICATE ", buffer, ref index, endIndex))
					commandState = ImapAuthCommandState.AuthMechanism;

				if (index >= endIndex || commandState == ImapAuthCommandState.Error)
					return EmptyAuthSecrets;
			}

			if (commandState == ImapAuthCommandState.AuthMechanism) {
				while (index < endIndex && buffer[index] != (byte) ' ' && buffer[index] != (byte) '\r')
					index++;

				if (index < endIndex) {
					if (buffer[index] == (byte) ' ') {
						commandState = ImapAuthCommandState.AuthToken;
					} else {
						commandState = ImapAuthCommandState.AuthNewLine;
					}

					index++;
				}

				if (index >= endIndex)
					return EmptyAuthSecrets;
			}

			if (commandState == ImapAuthCommandState.AuthNewLine) {
				if (buffer[index] == (byte) '\n') {
					commandState = ImapAuthCommandState.AuthToken;
					index++;
				} else {
					commandState = ImapAuthCommandState.Error;
				}

				if (index >= endIndex || commandState == ImapAuthCommandState.Error)
					return EmptyAuthSecrets;
			}

			int startIndex = index;
			while (index < endIndex && buffer[index] != (byte) '\r')
				index++;

			if (index < endIndex)
				commandState = ImapAuthCommandState.AuthNewLine;

			if (index == startIndex)
				return EmptyAuthSecrets;

			var secret = new AuthenticationSecret (startIndex, index - startIndex);

			return new AuthenticationSecret[] { secret };
		}
		bool SkipText (string text, byte[] buffer, ref int index, int endIndex)
		{
			while (index < endIndex && textIndex < text.Length) {
				if (buffer[index] != (byte) text[textIndex]) {
					commandState = ImapAuthCommandState.Error;
					break;
				}

				textIndex++;
				index++;
			}

			return textIndex == text.Length;
		}
		public IList<AuthenticationSecret> DetectSecrets (byte[] buffer, int offset, int count)
		{
			if (!IsAuthenticating || commandState == ImapAuthCommandState.Error || count == 0)
				return EmptyAuthSecrets;

			int endIndex = offset + count;
			int index = offset;

			if (commandState == ImapAuthCommandState.None) {
				// skip over the tag
				while (index < endIndex && buffer[index] != (byte) ' ')
					index++;

				if (index < endIndex) {
					commandState = ImapAuthCommandState.Command;
					index++;
				}

				if (index >= endIndex)
					return EmptyAuthSecrets;
			}

			if (commandState == ImapAuthCommandState.Command) {
				switch ((char) buffer[index]) {
				case 'A':
					commandState = ImapAuthCommandState.Authenticate;
					textIndex = 1;
					index++;
					break;
				case 'L':
					commandState = ImapAuthCommandState.Login;
					textIndex = 1;
					index++;
					break;
				default:
					commandState = ImapAuthCommandState.Error;
					break;
				}

				if (index >= endIndex || commandState == ImapAuthCommandState.Error)
					return EmptyAuthSecrets;
			}

			if (commandState >= ImapAuthCommandState.Authenticate && commandState <= ImapAuthCommandState.AuthToken)
				return DetectAuthSecrets (buffer, index, endIndex);

			return DetectLoginSecrets (buffer, index, endIndex);
		}
		bool SkipLoginToken (List<AuthenticationSecret> secrets, byte[] buffer, ref int index, int endIndex, byte sentinel)
		{
			int startIndex;

			if (tokenType == ImapLoginTokenType.None) {
				switch ((char) buffer[index]) {
				case '{':
					literalState = ImapLiteralState.Octets;
					tokenType = ImapLoginTokenType.Literal;
					index++;
					break;
				case '"':
					tokenType = ImapLoginTokenType.QString;
					index++;
					break;
				default:
					tokenType = ImapLoginTokenType.Atom;
					break;
				}
			}

			switch (tokenType) {
			case ImapLoginTokenType.Literal:
				return SkipLiteralToken (secrets, buffer, ref index, endIndex, sentinel);
			case ImapLoginTokenType.QString:
				if (qstringState != ImapQStringState.Complete) {
					startIndex = index;

					while (index < endIndex) {
						if (qstringState == ImapQStringState.Escaped) {
							qstringState = ImapQStringState.None;
						} else if (buffer[index] == (byte) '\\') {
							qstringState = ImapQStringState.Escaped;
						} else if (buffer[index] == (byte) '"') {
							qstringState = ImapQStringState.EndQuote;
							break;
						}
						index++;
					}

					if (index > startIndex)
						secrets.Add (new AuthenticationSecret (startIndex, index - startIndex));

					if (qstringState == ImapQStringState.EndQuote) {
						qstringState = ImapQStringState.Complete;
						index++;
					}
				}

				if (index >= endIndex)
					return false;

				if (buffer[index] != sentinel) {
					commandState = ImapAuthCommandState.Error;
					return false;
				}

				index++;

				return true;
			default:
				startIndex = index;

				while (index < endIndex && buffer[index] != sentinel)
					index++;

				if (index > startIndex)
					secrets.Add (new AuthenticationSecret (startIndex, index - startIndex));

				if (index >= endIndex)
					return false;
				
				index++;

				return true;
			}
		}