Advertise only available mail capabilities

This commit is contained in:
Mario Fetka
2026-07-16 20:03:43 +02:00
parent 613e58c3e6
commit 300083232c
12 changed files with 271 additions and 47 deletions
+12
View File
@@ -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()
+48
View File
@@ -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;
}
+11
View File
@@ -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
View File
@@ -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);
-2
View File
@@ -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"
+31
View File
@@ -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;
}
+12
View File
@@ -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()
+57
View File
@@ -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;
}
+20
View File
@@ -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
View File
@@ -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;
-6
View File
@@ -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
+43
View File
@@ -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;
}