Advertise only available mail capabilities
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
add_executable(bongopop3
|
||||
pop3.c
|
||||
capabilities.c
|
||||
)
|
||||
|
||||
target_link_libraries(bongopop3
|
||||
@@ -12,3 +13,14 @@ target_link_libraries(bongopop3
|
||||
)
|
||||
|
||||
install(TARGETS bongopop3 DESTINATION ${SBIN_INSTALL_DIR})
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_executable(pop3-capabilities-test
|
||||
tests/capabilities-test.c
|
||||
capabilities.c
|
||||
)
|
||||
target_include_directories(pop3-capabilities-test PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
add_test(NAME pop3-capabilities COMMAND pop3-capabilities-test)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "capabilities.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int
|
||||
Append(char *buffer, size_t buffer_size, size_t *length,
|
||||
const char *format, ...)
|
||||
{
|
||||
int written;
|
||||
va_list args;
|
||||
|
||||
if (*length >= buffer_size) return 0;
|
||||
|
||||
va_start(args, format);
|
||||
written = vsnprintf(buffer + *length, buffer_size - *length, format, args);
|
||||
va_end(args);
|
||||
if (written < 0 || (size_t)written >= buffer_size - *length) {
|
||||
buffer[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
*length += (size_t)written;
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t
|
||||
POP3FormatCapabilities(char *buffer, size_t buffer_size,
|
||||
int tls_active, int stls_available,
|
||||
const char *sasl_mechanisms)
|
||||
{
|
||||
size_t length = 0;
|
||||
|
||||
if (!buffer || buffer_size == 0) return 0;
|
||||
buffer[0] = '\0';
|
||||
|
||||
if (!Append(buffer, buffer_size, &length,
|
||||
"+OK\r\nTOP\r\nRESP-CODES\r\nAUTH-RESP-CODE\r\n"
|
||||
"PIPELINING\r\nEXPIRE NEVER\r\nUIDL\r\n") ||
|
||||
(tls_active && sasl_mechanisms && *sasl_mechanisms &&
|
||||
!Append(buffer, buffer_size, &length, "SASL %s\r\nUSER\r\n",
|
||||
sasl_mechanisms)) ||
|
||||
(!tls_active && stls_available &&
|
||||
!Append(buffer, buffer_size, &length, "STLS\r\n")) ||
|
||||
!Append(buffer, buffer_size, &length, ".\r\n"))
|
||||
return 0;
|
||||
|
||||
return length;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef BONGO_POP3_CAPABILITIES_H
|
||||
#define BONGO_POP3_CAPABILITIES_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* Returns the number of bytes written, or 0 if the buffer was too small. */
|
||||
size_t POP3FormatCapabilities(char *buffer, size_t buffer_size,
|
||||
int tls_active, int stls_available,
|
||||
const char *sasl_mechanisms);
|
||||
|
||||
#endif
|
||||
+14
-12
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <xpl.h>
|
||||
#include <bongoutil.h>
|
||||
#include "capabilities.h"
|
||||
#include <connio.h>
|
||||
#include <msgapi.h>
|
||||
#include <logger.h>
|
||||
@@ -905,22 +906,23 @@ static int
|
||||
POP3CommandCapa(void *param)
|
||||
{
|
||||
int ccode;
|
||||
size_t capabilityLength;
|
||||
POP3Client *client = (POP3Client *)param;
|
||||
BongoSaslSession *session = NULL;
|
||||
const char *mechanisms = NULL;
|
||||
|
||||
ccode = ConnWrite(client->conn, "+OK\r\nTOP\r\nRESP-CODES\r\nAUTH-RESP-CODE\r\n"
|
||||
"PIPELINING\r\nEXPIRE NEVER\r\nUIDL\r\n",
|
||||
sizeof("+OK\r\nTOP\r\nRESP-CODES\r\nAUTH-RESP-CODE\r\n"
|
||||
"PIPELINING\r\nEXPIRE NEVER\r\nUIDL\r\n") - 1);
|
||||
if (ccode != -1 && client->conn->ssl.enable &&
|
||||
BongoSaslSessionCreate(&session, "pop", BongoGlobals.hostname,
|
||||
NULL, NULL, 1, POP3SaslVerify, client) &&
|
||||
BongoSaslMechanisms(session, &mechanisms))
|
||||
ccode = ConnWriteF(client->conn, "SASL %s\r\nUSER\r\n", mechanisms);
|
||||
if (ccode != -1 && !client->conn->ssl.enable)
|
||||
ccode = ConnWrite(client->conn, "STLS\r\n", 6);
|
||||
if (ccode != -1) ccode = ConnWrite(client->conn, ".\r\n", 3);
|
||||
if (client->conn->ssl.enable) {
|
||||
if (!BongoSaslSessionCreate(&session, "pop", BongoGlobals.hostname,
|
||||
NULL, NULL, 1, POP3SaslVerify, client) ||
|
||||
!BongoSaslMechanisms(session, &mechanisms))
|
||||
mechanisms = NULL;
|
||||
}
|
||||
capabilityLength = POP3FormatCapabilities(
|
||||
client->buffer, sizeof(client->buffer), client->conn->ssl.enable,
|
||||
POP3.server.ssl.enable && POP3.server.ssl.context != NULL, mechanisms);
|
||||
ccode = capabilityLength
|
||||
? ConnWrite(client->conn, client->buffer, capabilityLength)
|
||||
: ConnWrite(client->conn, MSGERRINTERNAL, sizeof(MSGERRINTERNAL) - 1);
|
||||
BongoSaslSessionDestroy(&session);
|
||||
|
||||
return(ccode);
|
||||
|
||||
@@ -37,8 +37,6 @@
|
||||
#define MSGOK "+OK\r\n"
|
||||
#define MSGOKHELLO "Bongo POP3 server ready\r\n"
|
||||
#define MSGOKBYE "signing off"
|
||||
#define MSGCAPALIST_TLS "+OK\r\nTOP\r\nUSER\r\nRESP-CODES\r\nAUTH-RESP-CODE\r\nPIPELINING\r\nEXPIRE NEVER\r\nUIDL\r\n.\r\n"
|
||||
#define MSGCAPALIST_STARTTLS "+OK\r\nTOP\r\nRESP-CODES\r\nAUTH-RESP-CODE\r\nPIPELINING\r\nEXPIRE NEVER\r\nUIDL\r\nSTLS\r\n.\r\n"
|
||||
#define MSGWRONGMETHOD "-ERR [AUTH] Unsupported method\r\n"
|
||||
#define MSGOKPASSWD "+OK Password required\r\n"
|
||||
#define MSGOKSTARTTLS "+OK Begin TLS negotiations\r\n"
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "capabilities.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
char buffer[1024];
|
||||
char small[8];
|
||||
|
||||
assert(POP3FormatCapabilities(buffer, sizeof(buffer), 0, 0, NULL) > 0);
|
||||
assert(!strstr(buffer, "STLS"));
|
||||
assert(!strstr(buffer, "SASL"));
|
||||
assert(!strstr(buffer, "USER"));
|
||||
|
||||
assert(POP3FormatCapabilities(buffer, sizeof(buffer), 0, 1, NULL) > 0);
|
||||
assert(strstr(buffer, "STLS\r\n"));
|
||||
assert(!strstr(buffer, "SASL"));
|
||||
|
||||
assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 1,
|
||||
"PLAIN LOGIN") > 0);
|
||||
assert(!strstr(buffer, "STLS"));
|
||||
assert(strstr(buffer, "SASL PLAIN LOGIN\r\n"));
|
||||
assert(strstr(buffer, "USER\r\n"));
|
||||
|
||||
assert(POP3FormatCapabilities(small, sizeof(small), 1, 1,
|
||||
"PLAIN LOGIN") == 0);
|
||||
assert(small[0] == '\0');
|
||||
return 0;
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
add_executable(bongosmtp
|
||||
smtpd.c
|
||||
capabilities.c
|
||||
)
|
||||
|
||||
target_link_libraries(bongosmtp
|
||||
@@ -31,3 +32,14 @@ target_link_libraries(bongosmtpc
|
||||
|
||||
install(TARGETS bongosmtp DESTINATION ${SBIN_INSTALL_DIR})
|
||||
install(TARGETS bongosmtpc DESTINATION ${SBIN_INSTALL_DIR})
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_executable(smtp-capabilities-test
|
||||
tests/capabilities-test.c
|
||||
capabilities.c
|
||||
)
|
||||
target_include_directories(smtp-capabilities-test PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
add_test(NAME smtp-capabilities COMMAND smtp-capabilities-test)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "capabilities.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int
|
||||
Append(char *buffer, size_t buffer_size, size_t *length,
|
||||
const char *format, ...)
|
||||
{
|
||||
int written;
|
||||
va_list args;
|
||||
|
||||
if (*length >= buffer_size) return 0;
|
||||
|
||||
va_start(args, format);
|
||||
written = vsnprintf(buffer + *length, buffer_size - *length, format, args);
|
||||
va_end(args);
|
||||
if (written < 0 || (size_t)written >= buffer_size - *length) {
|
||||
buffer[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
*length += (size_t)written;
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t
|
||||
SMTPFormatEhlo(char *buffer, size_t buffer_size,
|
||||
const SMTPCapabilityOptions *options)
|
||||
{
|
||||
size_t length = 0;
|
||||
|
||||
if (!buffer || buffer_size == 0 || !options || !options->hostname)
|
||||
return 0;
|
||||
buffer[0] = '\0';
|
||||
|
||||
if (!Append(buffer, buffer_size, &length, "250-%s Pleased to meet you\r\n",
|
||||
options->hostname) ||
|
||||
(options->etrn &&
|
||||
!Append(buffer, buffer_size, &length, "250-ETRN\r\n")) ||
|
||||
(options->starttls &&
|
||||
!Append(buffer, buffer_size, &length, "250-STARTTLS\r\n")) ||
|
||||
(options->auth &&
|
||||
!Append(buffer, buffer_size, &length, "250-AUTH PLAIN LOGIN\r\n")) ||
|
||||
(options->smtputf8 &&
|
||||
!Append(buffer, buffer_size, &length, "250-SMTPUTF8\r\n")) ||
|
||||
!Append(buffer, buffer_size, &length, "250-HELP\r\n") ||
|
||||
(options->expn &&
|
||||
!Append(buffer, buffer_size, &length, "250-EXPN\r\n")) ||
|
||||
!Append(buffer, buffer_size, &length,
|
||||
"250-PIPELINING\r\n250-8BITMIME\r\n250-DSN\r\n250 SIZE") ||
|
||||
(options->message_limit > 0 &&
|
||||
!Append(buffer, buffer_size, &length, " %d", options->message_limit)) ||
|
||||
!Append(buffer, buffer_size, &length, "\r\n"))
|
||||
return 0;
|
||||
|
||||
return length;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef BONGO_SMTP_CAPABILITIES_H
|
||||
#define BONGO_SMTP_CAPABILITIES_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct {
|
||||
const char *hostname;
|
||||
int message_limit;
|
||||
int etrn;
|
||||
int starttls;
|
||||
int auth;
|
||||
int smtputf8;
|
||||
int expn;
|
||||
} SMTPCapabilityOptions;
|
||||
|
||||
/* Returns the number of bytes written, or 0 if the buffer was too small. */
|
||||
size_t SMTPFormatEhlo(char *buffer, size_t buffer_size,
|
||||
const SMTPCapabilityOptions *options);
|
||||
|
||||
#endif
|
||||
+23
-27
@@ -46,6 +46,7 @@
|
||||
#include "mailauth.h"
|
||||
#include "smtpd.h"
|
||||
#include "external_accounts.h"
|
||||
#include "capabilities.h"
|
||||
|
||||
struct {
|
||||
int port;
|
||||
@@ -1911,35 +1912,30 @@ HandleConnection (void *param)
|
||||
switch (toupper (Client->Command[1])) {
|
||||
case 'H':{ /* EHLO */
|
||||
if (Client->State == STATE_FRESH) {
|
||||
SMTPCapabilityOptions capabilities;
|
||||
size_t answerLength;
|
||||
|
||||
XplRWReadLockAcquire (&ConfigLock);
|
||||
if (SMTP.message_limit > 0) {
|
||||
snprintf (Answer, sizeof (Answer),
|
||||
"250-%s %s\r\n%s%s%s%s%s %d\r\n",
|
||||
BongoGlobals.hostname, MSG250HELLO,
|
||||
SMTP.accept_etrn ? MSG250ETRN : "",
|
||||
((SMTP.allow_client_ssl || Client->InternalRelay || Client->Submission)
|
||||
&& (Client->InternalRelay ? InternalSSLContext != NULL : TRUE)
|
||||
&& !Client->client.conn->ssl.enable) ? MSG250TLS : "",
|
||||
(AllowAuth == TRUE &&
|
||||
Client->client.conn->ssl.enable) ? MSG250AUTH : "",
|
||||
SMTP.smtp_utf8_enabled ? MSG250SMTPUTF8 : "",
|
||||
MSG250EHLO, SMTP.message_limit);
|
||||
}
|
||||
else {
|
||||
snprintf (Answer, sizeof (Answer),
|
||||
"250-%s %s\r\n%s%s%s%s%s\r\n",
|
||||
BongoGlobals.hostname, MSG250HELLO,
|
||||
SMTP.accept_etrn ? MSG250ETRN : "",
|
||||
((SMTP.allow_client_ssl || Client->InternalRelay || Client->Submission)
|
||||
&& (Client->InternalRelay ? InternalSSLContext != NULL : TRUE)
|
||||
&& !Client->client.conn->ssl.enable) ? MSG250TLS : "",
|
||||
(AllowAuth == TRUE &&
|
||||
Client->client.conn->ssl.enable) ? MSG250AUTH : "",
|
||||
SMTP.smtp_utf8_enabled ? MSG250SMTPUTF8 : "",
|
||||
MSG250EHLO);
|
||||
}
|
||||
capabilities.hostname = BongoGlobals.hostname;
|
||||
capabilities.message_limit = SMTP.message_limit;
|
||||
capabilities.etrn = SMTP.accept_etrn;
|
||||
capabilities.starttls =
|
||||
(SMTP.allow_client_ssl || Client->InternalRelay || Client->Submission) &&
|
||||
(Client->InternalRelay ? InternalSSLContext != NULL : SSLContext != NULL) &&
|
||||
!Client->client.conn->ssl.enable;
|
||||
capabilities.auth = AllowAuth == TRUE &&
|
||||
Client->client.conn->ssl.enable;
|
||||
capabilities.smtputf8 = SMTP.smtp_utf8_enabled;
|
||||
capabilities.expn = SMTP.allow_expn;
|
||||
answerLength = SMTPFormatEhlo(Answer, sizeof(Answer),
|
||||
&capabilities);
|
||||
XplRWReadLockRelease (&ConfigLock);
|
||||
ConnWrite (Client->client.conn, Answer, strlen (Answer));
|
||||
if (!answerLength) {
|
||||
ConnWrite(Client->client.conn, MSG451INTERNALERR,
|
||||
MSG451INTERNALERR_LEN);
|
||||
break;
|
||||
}
|
||||
ConnWrite (Client->client.conn, Answer, answerLength);
|
||||
snprintf(Client->RemoteHost, sizeof(Client->RemoteHost),
|
||||
"%s", Client->Command + 5);
|
||||
Client->State = STATE_HELO;
|
||||
|
||||
@@ -74,12 +74,6 @@
|
||||
#define MSG250HELLO "Pleased to meet you"
|
||||
#define MSG250HELLO_LEN 19
|
||||
|
||||
#define MSG250ETRN "250-ETRN\r\n"
|
||||
#define MSG250TLS "250-STARTTLS\r\n"
|
||||
#define MSG250AUTH "250-AUTH PLAIN LOGIN\r\n"
|
||||
#define MSG250SMTPUTF8 "250-SMTPUTF8\r\n"
|
||||
#define MSG250EHLO "250-HELP\r\n250-EXPN\r\n250-PIPELINING\r\n250-8BITMIME\r\n250-DSN\r\n250 SIZE"
|
||||
|
||||
#define MSG251NOMESSAGES "251 OK No messages waiting for you\r\n"
|
||||
#define MSG251NOMESSAGES_LEN 36
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "capabilities.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
char buffer[1024];
|
||||
char small[8];
|
||||
SMTPCapabilityOptions options = {
|
||||
"mail.example.test", 52428800, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
assert(SMTPFormatEhlo(buffer, sizeof(buffer), &options) > 0);
|
||||
assert(strstr(buffer, "250-mail.example.test Pleased to meet you\r\n"));
|
||||
assert(strstr(buffer, "250 SIZE 52428800\r\n"));
|
||||
assert(!strstr(buffer, "EXPN"));
|
||||
assert(!strstr(buffer, "STARTTLS"));
|
||||
assert(!strstr(buffer, "AUTH"));
|
||||
assert(!strstr(buffer, "SMTPUTF8"));
|
||||
|
||||
options.etrn = 1;
|
||||
options.starttls = 1;
|
||||
options.auth = 1;
|
||||
options.smtputf8 = 1;
|
||||
options.expn = 1;
|
||||
assert(SMTPFormatEhlo(buffer, sizeof(buffer), &options) > 0);
|
||||
assert(strstr(buffer, "250-ETRN\r\n"));
|
||||
assert(strstr(buffer, "250-STARTTLS\r\n"));
|
||||
assert(strstr(buffer, "250-AUTH PLAIN LOGIN\r\n"));
|
||||
assert(strstr(buffer, "250-SMTPUTF8\r\n"));
|
||||
assert(strstr(buffer, "250-EXPN\r\n"));
|
||||
|
||||
options.message_limit = 0;
|
||||
assert(SMTPFormatEhlo(buffer, sizeof(buffer), &options) > 0);
|
||||
assert(strstr(buffer, "250 SIZE\r\n"));
|
||||
assert(!strstr(buffer, "250 SIZE "));
|
||||
|
||||
assert(SMTPFormatEhlo(small, sizeof(small), &options) == 0);
|
||||
assert(small[0] == '\0');
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user