diff --git a/docs/imap.md b/docs/imap.md
index 77c7fa4..dc7dd08 100644
--- a/docs/imap.md
+++ b/docs/imap.md
@@ -37,6 +37,10 @@ ad-hoc downgrade.
AUTH capabilities are generated from the GNU GSASL mechanisms which Bongo
has actually enabled. Password mechanisms are advertised only on a TLS
connection; unencrypted connections offer STARTTLS and keep LOGIN disabled.
+STARTTLS rejects arguments and repeated or post-authentication use, discards
+already buffered cleartext before the TLS handshake, and closes the connection
+without attempting a plaintext IMAP response if negotiation fails. A client
+which ignores `LOGINDISABLED` receives a tagged `NO` without a password lookup.
Relevant standards are [RFC 9051](https://www.rfc-editor.org/rfc/rfc9051.html),
[RFC 3501](https://www.rfc-editor.org/rfc/rfc3501.html),
diff --git a/docs/protocols.md b/docs/protocols.md
index 5626c48..c60b6c8 100644
--- a/docs/protocols.md
+++ b/docs/protocols.md
@@ -34,6 +34,10 @@ POP3 implements the normal transaction commands and advertises `TOP`,
`RESP-CODES`, `AUTH-RESP-CODE`, `PIPELINING`, `EXPIRE NEVER`, `UIDL`, and
`UTF8`. STLS is offered before authentication; SASL and password login are
advertised only according to the TLS policy. POP3S provides implicit TLS.
+After a successful `UTF8` command, `STLS` is no longer advertised and is
+rejected so that the negotiated UTF-8 session state cannot cross into TLS.
+Both IMAP STARTTLS and POP3 STLS discard buffered cleartext before negotiating
+TLS.
## Server-side filtering
diff --git a/docs/release-testing-0.7.md b/docs/release-testing-0.7.md
index d5cada3..1b03a6d 100644
--- a/docs/release-testing-0.7.md
+++ b/docs/release-testing-0.7.md
@@ -154,7 +154,7 @@ inputs and outputs is absent from the ZIP.
| ID | Test | R1 | R2 | R3 |
| --- | --- | --- | --- | --- |
| IMAP-01 | Capability list matches implemented rev2/rev1/legacy behaviour | | | |
-| IMAP-02 | Port 143 STARTTLS and pre-TLS `LOGINDISABLED` | | | |
+| IMAP-02 | Port 143 STARTTLS, pre-TLS `LOGINDISABLED`, tagged `NO` for plaintext LOGIN, repeated STARTTLS rejection | | | |
| IMAP-03 | Port 993 TLS, LOGIN, SASL PLAIN, and SASL LOGIN | | | |
| IMAP-04 | CAPABILITY, NOOP, LOGOUT, ID, and NAMESPACE | | | |
| IMAP-05 | LIST, LSUB, extended LIST, LIST-STATUS, hierarchy quoting | | | |
@@ -171,18 +171,18 @@ inputs and outputs is absent from the ZIP.
| IMAP-16 | ENABLE UTF8=ACCEPT and UTF-8 mailbox/message names | | | |
| IMAP-17 | SPECIAL-USE and CREATE-SPECIAL-USE folders | | | |
| IMAP-18 | Old-client plain IMAP4 subset without rev1/rev2 assumptions | | | |
-| IMAP-19 | Invalid states, malformed sets/literals, oversized/pipelined input | | | |
+| IMAP-19 | Invalid states, malformed sets/literals, oversized input, and STARTTLS plaintext-pipeline injection resistance | | | |
| IMAP-20 | Reconnect, simultaneous clients, restart, mailbox consistency | | | |
## POP3 and ManageSieve
| ID | Test | R1 | R2 | R3 |
| --- | --- | --- | --- | --- |
-| POP-01 | Port 110 CAPA/STLS; credentials unavailable before TLS | | | |
+| POP-01 | Port 110 CAPA/STLS; credentials unavailable before TLS; buffered plaintext discarded | | | |
| POP-02 | Port 995 TLS with USER/PASS and shared GSASL mechanisms | | | |
| POP-03 | STAT, LIST, UIDL, RETR, TOP, and dot transparency | | | |
| POP-04 | DELE, RSET, NOOP, QUIT commit, and disconnect rollback | | | |
-| POP-05 | UTF8, PIPELINING, response codes, malformed input, timeout | | | |
+| POP-05 | UTF8, STLS rejection after UTF8, PIPELINING, response codes, malformed input, timeout | | | |
| SIEVE-01 | Port 4190 greeting, STARTTLS, and GSASL authentication | | | |
| SIEVE-02 | CAPABILITY, HAVESPACE, PUT/CHECK/LIST/GETSCRIPT | | | |
| SIEVE-03 | SETACTIVE, RENAME, DELETE, LOGOUT, and persistence | | | |
diff --git a/src/agents/imap/CMakeLists.txt b/src/agents/imap/CMakeLists.txt
index 5a3b2e6..7128106 100644
--- a/src/agents/imap/CMakeLists.txt
+++ b/src/agents/imap/CMakeLists.txt
@@ -1,6 +1,7 @@
add_executable(bongoimap
imapd.c
capabilities.c
+ protocol.c
enable.c
utf8-mailbox.c
append-message.c
@@ -46,6 +47,15 @@ if(BUILD_TESTING)
)
add_test(NAME imap-capabilities COMMAND imap-capabilities-test)
+ add_executable(imap-protocol-test
+ tests/protocol-test.c
+ protocol.c
+ )
+ target_include_directories(imap-protocol-test PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+ add_test(NAME imap-protocol COMMAND imap-protocol-test)
+
add_executable(imap-enable-test
tests/enable-test.c
enable.c
diff --git a/src/agents/imap/imapd.h b/src/agents/imap/imapd.h
index 2392937..34467f6 100755
--- a/src/agents/imap/imapd.h
+++ b/src/agents/imap/imapd.h
@@ -98,6 +98,8 @@ typedef enum {
STATUS_INVALID_ARGUMENT,
STATUS_INVALID_STATE,
STATUS_UNKNOWN_COMMAND,
+ STATUS_LOGIN_DISABLED,
+ STATUS_TLS_UNAVAILABLE,
STATUS_TLS_NEGOTIATION_FAILURE,
STATUS_CHARSET_NOT_SUPPORTED,
STATUS_UID_NOT_FOUND,
@@ -156,6 +158,8 @@ static ImapErrorString ImapErrorStrings[] = {
{ STATUS_INVALID_ARGUMENT, "%s BAD %s invalid arguments\r\n" },
{ STATUS_INVALID_STATE, "%s BAD %s invalid command in current state\r\n" },
{ STATUS_UNKNOWN_COMMAND, "* BAD command unrecognized\r\n" },
+ { STATUS_LOGIN_DISABLED, "%s NO %s plaintext LOGIN disabled; use STARTTLS\r\n" },
+ { STATUS_TLS_UNAVAILABLE, "%s NO %s TLS unavailable\r\n" },
{ STATUS_TLS_NEGOTIATION_FAILURE, "%s BAD %s negotiation failed\r\n" },
{ 0, NULL }
};
diff --git a/src/agents/imap/protocol.c b/src/agents/imap/protocol.c
new file mode 100644
index 0000000..835a8ae
--- /dev/null
+++ b/src/agents/imap/protocol.c
@@ -0,0 +1,57 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2001 Novell, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Novell, Inc.
+ *
+ * To contact Novell about this file by physical or electronic mail, you
+ * may find current contact information at www.novell.com.
+ *
+ ****************************************************************************/
+
+#include "protocol.h"
+
+#include
+#include
+
+static int
+EqualsIgnoreCase(const char *left, const char *right)
+{
+ if (!left || !right) return 0;
+ while (*left && *right) {
+ if (toupper((unsigned char)*left) != toupper((unsigned char)*right))
+ return 0;
+ left++;
+ right++;
+ }
+ return *left == '\0' && *right == '\0';
+}
+
+IMAPStartTlsStatus
+IMAPStartTlsCheck(const char *command_line, int authenticated,
+ int tls_active, int tls_available)
+{
+ if (!EqualsIgnoreCase(command_line, "STARTTLS"))
+ return IMAP_STARTTLS_INVALID_ARGUMENTS;
+ if (authenticated || tls_active) return IMAP_STARTTLS_INVALID_STATE;
+ if (!tls_available) return IMAP_STARTTLS_UNAVAILABLE;
+ return IMAP_STARTTLS_READY;
+}
+
+IMAPLoginStatus
+IMAPLoginCheck(int authenticated, int tls_active)
+{
+ if (authenticated) return IMAP_LOGIN_INVALID_STATE;
+ if (!tls_active) return IMAP_LOGIN_DISABLED;
+ return IMAP_LOGIN_READY;
+}
diff --git a/src/agents/imap/protocol.h b/src/agents/imap/protocol.h
new file mode 100644
index 0000000..799ac66
--- /dev/null
+++ b/src/agents/imap/protocol.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2001 Novell, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Novell, Inc.
+ *
+ * To contact Novell about this file by physical or electronic mail, you
+ * may find current contact information at www.novell.com.
+ *
+ ****************************************************************************/
+
+#ifndef BONGO_IMAP_PROTOCOL_H
+#define BONGO_IMAP_PROTOCOL_H
+
+typedef enum {
+ IMAP_STARTTLS_READY = 0,
+ IMAP_STARTTLS_INVALID_ARGUMENTS,
+ IMAP_STARTTLS_INVALID_STATE,
+ IMAP_STARTTLS_UNAVAILABLE
+} IMAPStartTlsStatus;
+
+typedef enum {
+ IMAP_LOGIN_READY = 0,
+ IMAP_LOGIN_DISABLED,
+ IMAP_LOGIN_INVALID_STATE
+} IMAPLoginStatus;
+
+IMAPStartTlsStatus IMAPStartTlsCheck(const char *command_line,
+ int authenticated, int tls_active,
+ int tls_available);
+IMAPLoginStatus IMAPLoginCheck(int authenticated, int tls_active);
+
+#endif
diff --git a/src/agents/imap/session.c b/src/agents/imap/session.c
index 69a03f5..7a2393e 100644
--- a/src/agents/imap/session.c
+++ b/src/agents/imap/session.c
@@ -29,6 +29,7 @@
#include
#include "imapd.h"
+#include "protocol.h"
void
ImapAnnotateAliasClient(ImapSession *session)
@@ -298,10 +299,20 @@ ImapCommandLogin(void *param)
{
ImapSession *session = (ImapSession *)param;
long ccode;
+ IMAPLoginStatus status;
char *username = NULL;
char *password = NULL;
- if (session->client.state == STATE_FRESH && session->client.conn->ssl.enable) {
+ status = IMAPLoginCheck(session->client.state >= STATE_AUTH,
+ session->client.conn->ssl.enable);
+ if (status == IMAP_LOGIN_DISABLED)
+ return(SendError(session->client.conn, session->command.tag, "LOGIN",
+ STATUS_LOGIN_DISABLED));
+ if (status == IMAP_LOGIN_INVALID_STATE)
+ return(SendError(session->client.conn, session->command.tag, "LOGIN",
+ STATUS_INVALID_STATE));
+
+ if (status == IMAP_LOGIN_READY) {
ccode = GrabTwoArguments(session, session->command.buffer + 6, &username, &password);
if (ccode == STATUS_CONTINUE) {
X500Encode(username);
@@ -335,27 +346,48 @@ int
ImapCommandStartTls(void *param)
{
long ccode;
+ IMAPStartTlsStatus status;
ImapSession *session = (ImapSession *)param;
- if (session->client.state < STATE_AUTH) {
- if (Imap.server.ssl.config.options) {
- if (((ccode = ConnWriteF(session->client.conn, "%s OK Begin TLS negotiation\r\n",session->command.tag)) != -1) && (ccode = ConnFlush(session->client.conn))) {
- session->client.conn->ssl.enable = TRUE;
- if (ConnNegotiate(session->client.conn, Imap.server.ssl.context)) {
- return(STATUS_CONTINUE);
- }
+ status = IMAPStartTlsCheck(session->command.buffer,
+ session->client.state >= STATE_AUTH,
+ session->client.conn->ssl.enable,
+ Imap.server.ssl.context != NULL);
+ if (status == IMAP_STARTTLS_INVALID_ARGUMENTS)
+ return(SendError(session->client.conn, session->command.tag,
+ "STARTTLS", STATUS_INVALID_ARGUMENT));
+ if (status == IMAP_STARTTLS_INVALID_STATE)
+ return(SendError(session->client.conn, session->command.tag,
+ "STARTTLS", STATUS_INVALID_STATE));
+ if (status == IMAP_STARTTLS_UNAVAILABLE)
+ return(SendError(session->client.conn, session->command.tag,
+ "STARTTLS", STATUS_TLS_UNAVAILABLE));
- return(SendError(session->client.conn, session->command.tag, "STARTTLS", STATUS_TLS_NEGOTIATION_FAILURE));
- }
+ ccode = ConnWriteF(session->client.conn,
+ "%s OK Begin TLS negotiation\r\n",
+ session->command.tag);
+ if (ccode == -1 || ConnFlush(session->client.conn) == -1)
+ return(STATUS_ABORT);
- return(STATUS_ABORT);
- }
-
- return(SendError(session->client.conn, session->command.tag, "", STATUS_UNKNOWN_COMMAND));
- }
-
- return(SendError(session->client.conn, session->command.tag, "STARTTLS", STATUS_INVALID_STATE));
+ /*
+ * Never carry pipelined cleartext across STARTTLS. RFC 9051 requires
+ * extra bytes already received after the command to become TLS input or
+ * to be discarded. Discarding them avoids plaintext command injection
+ * and matches the POP3 STLS path.
+ */
+ session->client.conn->receive.read =
+ session->client.conn->receive.write =
+ session->client.conn->receive.buffer;
+ session->client.conn->receive.remaining = CONN_TCP_MTU;
+ session->client.conn->receive.buffer[0] = '\0';
+ session->client.conn->ssl.enable = TRUE;
+ session->client.enabledCapabilities = 0;
+ if (ConnNegotiate(session->client.conn, Imap.server.ssl.context))
+ return(STATUS_CONTINUE);
+ /* TLS bytes may already have been exchanged; no IMAP response is safe. */
+ ConnClose(session->client.conn);
+ return(STATUS_ABORT);
}
diff --git a/src/agents/imap/tests/protocol-test.c b/src/agents/imap/tests/protocol-test.c
new file mode 100644
index 0000000..702361c
--- /dev/null
+++ b/src/agents/imap/tests/protocol-test.c
@@ -0,0 +1,46 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2001 Novell, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Novell, Inc.
+ *
+ * To contact Novell about this file by physical or electronic mail, you
+ * may find current contact information at www.novell.com.
+ *
+ ****************************************************************************/
+
+#include "protocol.h"
+
+#include
+
+int
+main(void)
+{
+ assert(IMAPStartTlsCheck("STARTTLS", 0, 0, 1) == IMAP_STARTTLS_READY);
+ assert(IMAPStartTlsCheck("starttls", 0, 0, 1) == IMAP_STARTTLS_READY);
+ assert(IMAPStartTlsCheck("STARTTLS ", 0, 0, 1) ==
+ IMAP_STARTTLS_INVALID_ARGUMENTS);
+ assert(IMAPStartTlsCheck("STARTTLS extra", 0, 0, 1) ==
+ IMAP_STARTTLS_INVALID_ARGUMENTS);
+ assert(IMAPStartTlsCheck("STARTTLS", 1, 0, 1) ==
+ IMAP_STARTTLS_INVALID_STATE);
+ assert(IMAPStartTlsCheck("STARTTLS", 0, 1, 1) ==
+ IMAP_STARTTLS_INVALID_STATE);
+ assert(IMAPStartTlsCheck("STARTTLS", 0, 0, 0) ==
+ IMAP_STARTTLS_UNAVAILABLE);
+ assert(IMAPLoginCheck(0, 1) == IMAP_LOGIN_READY);
+ assert(IMAPLoginCheck(0, 0) == IMAP_LOGIN_DISABLED);
+ assert(IMAPLoginCheck(1, 1) == IMAP_LOGIN_INVALID_STATE);
+ assert(IMAPLoginCheck(1, 0) == IMAP_LOGIN_INVALID_STATE);
+ return 0;
+}
diff --git a/src/agents/pop/capabilities.c b/src/agents/pop/capabilities.c
index 38d3ff8..fd54d26 100644
--- a/src/agents/pop/capabilities.c
+++ b/src/agents/pop/capabilities.c
@@ -47,6 +47,7 @@ Append(char *buffer, size_t buffer_size, size_t *length,
size_t
POP3FormatCapabilities(char *buffer, size_t buffer_size,
int authorization_state, int tls_active,
+ int utf8_active,
int stls_available,
const char *sasl_mechanisms)
{
@@ -63,7 +64,8 @@ POP3FormatCapabilities(char *buffer, size_t buffer_size,
sasl_mechanisms)) ||
(authorization_state && tls_active &&
!Append(buffer, buffer_size, &length, "USER\r\n")) ||
- (authorization_state && !tls_active && stls_available &&
+ (authorization_state && !tls_active && !utf8_active &&
+ stls_available &&
!Append(buffer, buffer_size, &length, "STLS\r\n")) ||
!Append(buffer, buffer_size, &length, ".\r\n"))
return 0;
diff --git a/src/agents/pop/capabilities.h b/src/agents/pop/capabilities.h
index f7344c4..f02b976 100644
--- a/src/agents/pop/capabilities.h
+++ b/src/agents/pop/capabilities.h
@@ -27,6 +27,7 @@
/* Returns the number of bytes written, or 0 if the buffer was too small. */
size_t POP3FormatCapabilities(char *buffer, size_t buffer_size,
int authorization_state, int tls_active,
+ int utf8_active,
int stls_available,
const char *sasl_mechanisms);
diff --git a/src/agents/pop/pop3.c b/src/agents/pop/pop3.c
index 6ef3cc8..9054415 100644
--- a/src/agents/pop/pop3.c
+++ b/src/agents/pop/pop3.c
@@ -1136,7 +1136,7 @@ POP3CommandCapa(void *param)
}
capabilityLength = POP3FormatCapabilities(
client->buffer, sizeof(client->buffer), authorizationState,
- client->conn->ssl.enable,
+ client->conn->ssl.enable, client->utf8Mode,
POP3.server.ssl.enable && POP3.server.ssl.context != NULL, mechanisms);
ccode = capabilityLength
? ConnWrite(client->conn, client->buffer, capabilityLength)
@@ -1533,6 +1533,7 @@ POP3CommandSTLS(void *param)
{
int ccode;
BOOL result;
+ POP3STLSStatus status;
POP3Client *client = (POP3Client *)param;
if (!POP3ArgumentsEmpty(client->command +
@@ -1544,19 +1545,19 @@ POP3CommandSTLS(void *param)
return ConnWrite(client->conn, MSGERRSYNTAX,
sizeof(MSGERRSYNTAX) - 1);
- if ((client->state == POP3_CLIENT_FRESH ||
- client->state == POP3_CLIENT_AUTHORIZATION) &&
- !client->conn->ssl.enable) {
- if (POP3.server.ssl.enable && POP3.server.ssl.context) {
- ccode = ConnWrite(client->conn, MSGOKSTARTTLS, sizeof(MSGOKSTARTTLS) - 1);
- } else {
- return ConnWrite(client->conn,
- "-ERR [SYS/PERM] TLS unavailable\r\n",
- sizeof("-ERR [SYS/PERM] TLS unavailable\r\n") - 1);
- }
- } else {
+ status = POP3STLSCheck(client->state == POP3_CLIENT_FRESH ||
+ client->state == POP3_CLIENT_AUTHORIZATION,
+ client->conn->ssl.enable, client->utf8Mode,
+ POP3.server.ssl.enable && POP3.server.ssl.context);
+ if (status == POP3_STLS_INVALID_STATE) {
return(ConnWrite(client->conn, MSGERRBADSTATE, sizeof(MSGERRBADSTATE) - 1));
}
+ if (status == POP3_STLS_UNAVAILABLE)
+ return ConnWrite(client->conn,
+ "-ERR [SYS/PERM] TLS unavailable\r\n",
+ sizeof("-ERR [SYS/PERM] TLS unavailable\r\n") - 1);
+
+ ccode = ConnWrite(client->conn, MSGOKSTARTTLS, sizeof(MSGOKSTARTTLS) - 1);
if (ccode != -1) {
ccode = ConnFlush(client->conn);
diff --git a/src/agents/pop/protocol.c b/src/agents/pop/protocol.c
index 621464c..5c35538 100644
--- a/src/agents/pop/protocol.c
+++ b/src/agents/pop/protocol.c
@@ -57,6 +57,16 @@ POP3ArgumentsEmpty(const char *arguments)
return arguments && *arguments == '\0';
}
+POP3STLSStatus
+POP3STLSCheck(int authorization_state, int tls_active, int utf8_active,
+ int tls_available)
+{
+ if (!authorization_state || tls_active || utf8_active)
+ return POP3_STLS_INVALID_STATE;
+ if (!tls_available) return POP3_STLS_UNAVAILABLE;
+ return POP3_STLS_READY;
+}
+
int
POP3ParseMessageNumber(const char *arguments, uint32_t *message_number)
{
diff --git a/src/agents/pop/protocol.h b/src/agents/pop/protocol.h
index bd0ed34..a6b1828 100644
--- a/src/agents/pop/protocol.h
+++ b/src/agents/pop/protocol.h
@@ -35,10 +35,18 @@ typedef struct {
int ends_in_cr;
} POP3MultilineWriter;
+typedef enum {
+ POP3_STLS_READY = 0,
+ POP3_STLS_INVALID_STATE,
+ POP3_STLS_UNAVAILABLE
+} POP3STLSStatus;
+
int POP3Contains8Bit(const void *data, size_t length);
int POP3UTF8Valid(const void *data, size_t length);
int POP3ArgumentsEmpty(const char *arguments);
+POP3STLSStatus POP3STLSCheck(int authorization_state, int tls_active,
+ int utf8_active, int tls_available);
int POP3ParseMessageNumber(const char *arguments, uint32_t *message_number);
int POP3ParseTopArguments(const char *arguments, uint32_t *message_number,
uint32_t *line_count);
diff --git a/src/agents/pop/tests/capabilities-test.c b/src/agents/pop/tests/capabilities-test.c
index 1338e65..8446b84 100644
--- a/src/agents/pop/tests/capabilities-test.c
+++ b/src/agents/pop/tests/capabilities-test.c
@@ -30,29 +30,35 @@ main(void)
char buffer[1024];
char small[8];
- assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 0, 0, NULL) > 0);
+ assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 0, 0, 0,
+ NULL) > 0);
assert(!strstr(buffer, "STLS"));
assert(!strstr(buffer, "SASL"));
assert(!strstr(buffer, "USER"));
assert(strstr(buffer, "UTF8\r\n"));
- assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 0, 1, NULL) > 0);
+ assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 0, 0, 1,
+ NULL) > 0);
assert(strstr(buffer, "STLS\r\n"));
assert(!strstr(buffer, "SASL"));
- assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 1, 1,
+ assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 1, 0, 1,
"PLAIN LOGIN") > 0);
assert(!strstr(buffer, "STLS"));
assert(strstr(buffer, "SASL PLAIN LOGIN\r\n"));
assert(strstr(buffer, "USER\r\n"));
- assert(POP3FormatCapabilities(buffer, sizeof(buffer), 0, 1, 1,
+ assert(POP3FormatCapabilities(buffer, sizeof(buffer), 0, 1, 0, 1,
"PLAIN LOGIN") > 0);
assert(!strstr(buffer, "STLS"));
assert(strstr(buffer, "SASL PLAIN LOGIN\r\n"));
assert(!strstr(buffer, "USER"));
- assert(POP3FormatCapabilities(small, sizeof(small), 1, 1, 1,
+ assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 0, 1, 1,
+ NULL) > 0);
+ assert(!strstr(buffer, "STLS"));
+
+ assert(POP3FormatCapabilities(small, sizeof(small), 1, 1, 0, 1,
"PLAIN LOGIN") == 0);
assert(small[0] == '\0');
return 0;
diff --git a/src/agents/pop/tests/protocol-test.c b/src/agents/pop/tests/protocol-test.c
index e4336ec..efb5f2a 100644
--- a/src/agents/pop/tests/protocol-test.c
+++ b/src/agents/pop/tests/protocol-test.c
@@ -49,6 +49,11 @@ TestArguments(void)
assert(POP3ArgumentsEmpty(""));
assert(!POP3ArgumentsEmpty(" "));
+ assert(POP3STLSCheck(1, 0, 0, 1) == POP3_STLS_READY);
+ assert(POP3STLSCheck(0, 0, 0, 1) == POP3_STLS_INVALID_STATE);
+ assert(POP3STLSCheck(1, 1, 0, 1) == POP3_STLS_INVALID_STATE);
+ assert(POP3STLSCheck(1, 0, 1, 1) == POP3_STLS_INVALID_STATE);
+ assert(POP3STLSCheck(1, 0, 0, 0) == POP3_STLS_UNAVAILABLE);
assert(POP3ParseMessageNumber(" 1", &message) && message == 1);
assert(POP3ParseMessageNumber("\t4294967295", &message) &&
message == UINT32_MAX);