diff --git a/NEWS b/NEWS
index 968dd7b..11e621e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,8 +1,13 @@
-------------------------------------------------------------------------------
Changes in version 2.00
-- This version updates the API to add new features. Plugins compiled
- for previous versions of mailfront will not work without recompiling.
+- This version updates the plugin API to add new features:
+
+ - Capabilities reported by the SMTP EHLO response can be added by
+ plugins.
+
+ Plugins compiled for previous versions of mailfront will not work
+ without recompiling.
Development of this version has been sponsored by FutureQuest, Inc.
ossi@FutureQuest.net http://www.FutureQuest.net/
diff --git a/TODO b/TODO
index b2dafd5..8ebaccb 100644
--- a/TODO
+++ b/TODO
@@ -14,6 +14,10 @@ Plugin: clamav
- Add tests (not sure how to emulate the daemon properly)
+Plugin: lua
+
+ - Fix hooks that can modify strings to actually allow modification
+
Plugin: patterns
- Add better MIME attachment discrimination to patterns, instead of
diff --git a/mailfront.c b/mailfront.c
index e0ea324..6d843a1 100644
--- a/mailfront.c
+++ b/mailfront.c
@@ -57,11 +57,13 @@ static const response* handle_init(void)
return 0;
}
-const response* handle_helo(str* host)
+const response* handle_helo(str* host, str* capabilities)
{
const response* resp;
- MODULE_CALL(helo, (host), 0, 0);
+ MODULE_CALL(helo, (host, capabilities), 0, 0);
session_setstr("helo_domain", host->s);
+ if (session.backend->helo != 0)
+ return session.backend->helo(host, capabilities);
return 0;
}
@@ -188,13 +190,20 @@ int respond_line(unsigned number, int final,
return 1;
}
-int respond(const response* resp)
+int respond_multiline(unsigned number, int final, const char* msg)
{
- const char* msg;
const char* nl;
- for (msg = resp->message; (nl = strchr(msg, '\n')) != 0; msg = nl + 1)
- respond_line(resp->number, 0, msg, nl-msg);
- return respond_line(resp->number, 1, msg, strlen(msg));
+ while ((nl = strchr(msg, '\n')) != 0) {
+ if (!respond_line(number, 0, msg, nl-msg))
+ return 0;
+ msg = nl + 1;
+ }
+ return respond_line(number, final, msg, strlen(msg));
+}
+
+int respond(const response* resp)
+{
+ return respond_multiline(resp->number, 1, resp->message);
}
const response* backend_data_block(const char* data, unsigned long len)
diff --git a/mailfront.h b/mailfront.h
index 4b4522f..9926604 100644
--- a/mailfront.h
+++ b/mailfront.h
@@ -17,7 +17,7 @@ struct plugin
const char* name;
unsigned flags;
const response* (*init)(void);
- const response* (*helo)(str*);
+ const response* (*helo)(str* hostname, str* capabilities);
const response* (*reset)(void);
const response* (*sender)(str*);
const response* (*recipient)(str*);
@@ -43,7 +43,7 @@ extern const char* getprotoenv(const char*);
/* From mailfront.c */
extern const char UNKNOWN[];
-extern const response* handle_helo(str* host);
+extern const response* handle_helo(str* host, str* capabilities);
extern const response* handle_reset(void);
extern const response* handle_sender(str* sender);
extern const response* handle_recipient(str* recip);
@@ -53,6 +53,7 @@ extern const response* handle_message_end(void);
extern int respond(const response*);
extern int respond_line(unsigned number, int final,
const char* msg, unsigned long len);
+extern int respond_multiline(unsigned number, int final, const char* msg);
extern const response* backend_data_block(const char* data, unsigned long len);
extern int scratchfile(void);
diff --git a/plugin-api.html b/plugin-api.html
index f2ffb97..63ae4e5 100644
--- a/plugin-api.html
+++ b/plugin-api.html
@@ -121,10 +121,12 @@
Hook Functions
responses to the sender address or data, and after the SMTP
HELO command.
-const response* helo(str* hostname) This hook is
-called when the SMTP HELO or EHLO commands are issued.
-As yet nothing actually uses the hostname string. Other
-protocols will not call this hook.
+const response* helo(str* hostname, str* capabilities)
+This hook is called when the SMTP HELO or EHLO
+commands are issued. As yet nothing actually uses the hostname
+string. Other protocols will not call this hook.
+The capabilities variable contains a list of SMTP EHLO response
+capabilities, each followed by a newline.
const response* sender(str* address) This hook is
called after a sender email address is transmitted by the client, and is
diff --git a/plugin-lua.c b/plugin-lua.c
index 0863a01..c9f58e5 100644
--- a/plugin-lua.c
+++ b/plugin-lua.c
@@ -225,11 +225,12 @@ static const response* reset(void)
return 0;
}
-static const response* helo(str* hostname)
+static const response* helo(str* hostname, str* capabilities)
{
if (setup("helo")) {
lua_pushlstring(L, hostname->s, hostname->len);
- return callit(1);
+ lua_pushlstring(L, capabilities->s, capabilities->len);
+ return callit(2);
}
return 0;
}
diff --git a/plugin-template.c b/plugin-template.c
index 98409ed..779d614 100644
--- a/plugin-template.c
+++ b/plugin-template.c
@@ -13,7 +13,7 @@ static const response* init(void)
/* The helo function is called once by the SMTP protocol when either the
* HELO or EHLO command is issued. The parameter is the hostname given
* in the command. */
-static const response* helo(str* hostname)
+static const response* helo(str* hostname, str* capabilities)
{
return 0;
}
diff --git a/protocol-smtp.c b/protocol-smtp.c
index de604df..aa7a4d9 100644
--- a/protocol-smtp.c
+++ b/protocol-smtp.c
@@ -26,6 +26,7 @@ static str cmd;
static str arg;
static str addr;
static str params;
+static str init_capabilities;
static RESPONSE(no_mail, 503, "5.5.1 You must send MAIL FROM: first");
static RESPONSE(vrfy, 252, "2.5.2 Send some mail, I'll try my best.");
@@ -42,7 +43,6 @@ static RESPONSE(toobig, 552, "5.2.3 The message would exceed the maximum message
static RESPONSE(toomanyunimp, 503, "5.5.0 Too many unimplemented commands.\n5.5.0 Closing connection.");
static RESPONSE(goodbye, 221, "2.0.0 Good bye.");
static RESPONSE(authenticated, 235, "2.7.0 Authentication succeeded.");
-static RESPONSE(ehlo, 250, "8BITMIME\nENHANCEDSTATUSCODES\nPIPELINING");
static int saw_mail = 0;
static int saw_rcpt = 0;
@@ -126,32 +126,26 @@ static int HELO(void)
{
const response* resp;
if (response_ok(resp = handle_reset()))
- resp = handle_helo(&arg);
+ resp = handle_helo(&arg, &line);
return (resp != 0) ? respond(resp)
: respond_line(250, 1, domain_name.s, domain_name.len);
}
static int EHLO(void)
{
- static str auth_resp;
const response* resp;
protocol.name = "ESMTP";
+ line.len = 0;
if (!response_ok(resp = handle_reset())
- || !response_ok(resp = handle_helo(&arg)))
+ || !response_ok(resp = handle_helo(&arg, &line)))
return respond(resp);
+ if (!str_cat(&line, &init_capabilities)) {
+ respond(&resp_oom);
+ return 0;
+ }
if (!respond_line(250, 0, domain_name.s, domain_name.len)) return 0;
- switch (sasl_auth_caps(&auth_resp)) {
- case 0: break;
- case 1:
- if (!respond_line(250, 0, auth_resp.s, auth_resp.len)) return 0;
- break;
- default: return respond(&resp_internal);
- }
- if (!str_copys(&line, "SIZE ")) return 0;
- if (!str_catu(&line, session_getnum("maxdatabytes", 0))) return 0;
- if (!respond_line(250, 0, line.s, line.len)) return 0;
- return respond(&resp_ehlo);
+ return respond_multiline(250, 1, line.s);
}
static void do_reset(void)
@@ -391,14 +385,31 @@ static int init(void)
if ((tmp = getenv("MAXNOTIMPL")) != 0)
maxnotimpl = strtoul(tmp, 0, 10);
+ if (!sasl_auth_init(&saslauth)) {
+ respond(&resp_authfail);
+ return 1;
+ }
+ switch (sasl_auth_caps(&init_capabilities)) {
+ case 0: break;
+ case 1: break;
+ default:
+ respond(&resp_authfail);
+ return 1;
+ }
+
+ if (!str_copys(&init_capabilities, "SIZE ")
+ || !str_catu(&init_capabilities, session_getnum("maxdatabytes", 0))
+ || !str_catc(&init_capabilities, '\n')
+ || !str_cats(&init_capabilities, "8BITMIME\nENHANCEDSTATUSCODES\nPIPELINING")) {
+ respond(&resp_oom);
+ return 1;
+ }
+
return 0;
}
static int mainloop(void)
{
- if (!sasl_auth_init(&saslauth))
- return respond(&resp_authfail);
-
if (!respond_line(220, 1, str_welcome.s, str_welcome.len)) return 0;
while (ibuf_getstr_crlf(&inbuf, &line))
if (!smtp_dispatch()) {