/*
* $Id$
*
- * An implementation of RFC821 (Simple Mail Transfer Protocol) for the
- * Citadel system.
+ * This module is an SMTP and ESMTP implementation for the Citadel system.
+ * It is compliant with all of the following:
+ *
+ * RFC 821 - Simple Mail Transfer Protocol
+ * RFC 876 - Survey of SMTP Implementations
+ * RFC 1047 - Duplicate messages and SMTP
+ * RFC 1854 - command pipelining
+ * RFC 1869 - Extended Simple Mail Transfer Protocol
+ * RFC 1870 - SMTP Service Extension for Message Size Declaration
+ * RFC 1893 - Enhanced Mail System Status Codes
+ * RFC 2033 - Local Mail Transfer Protocol
+ * RFC 2034 - SMTP Service Extension for Returning Enhanced Error Codes
+ * RFC 2197 - SMTP Service Extension for Command Pipelining
+ * RFC 2554 - SMTP Service Extension for Authentication
+ * RFC 2821 - Simple Mail Transfer Protocol
+ * RFC 2822 - Internet Message Format
+ * RFC 2920 - SMTP Service Extension for Command Pipelining
*
*/
char from[SIZ];
char recipients[SIZ];
int number_of_recipients;
- int number_of_rooms;
int delivery_mode;
int message_originated_locally;
+ int is_lmtp;
};
enum { /* Command states for login authentication */
#define SMTP_RECPS ((char *)CtdlGetUserData(SYM_SMTP_RECPS))
#define SMTP_ROOMS ((char *)CtdlGetUserData(SYM_SMTP_ROOMS))
-long SYM_SMTP;
-long SYM_SMTP_RECPS;
-long SYM_SMTP_ROOMS;
int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
cprintf("220 %s ESMTP Citadel/UX server ready.\r\n", config.c_fqdn);
}
+/*
+ * LMTP is like SMTP but with some extra bonus footage added.
+ */
+void lmtp_greeting(void) {
+ smtp_greeting();
+ SMTP->is_lmtp = 1;
+}
+
/*
* Implement HELO and EHLO commands.
+ *
+ * which_command: 0=HELO, 1=EHLO, 2=LHLO
*/
-void smtp_hello(char *argbuf, int is_esmtp) {
+void smtp_hello(char *argbuf, int which_command) {
safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
- if (!is_esmtp) {
- cprintf("250 Greetings and joyous salutations.\r\n");
+ if ( (which_command != 2) && (SMTP->is_lmtp) ) {
+ cprintf("500 Only LHLO is allowed when running LMTP\r\n");
+ return;
+ }
+
+ if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
+ cprintf("500 LHLO is only allowed when running LMTP\r\n");
+ return;
+ }
+
+ if (which_command == 0) {
+ cprintf("250 Hello %s (%s [%s])\r\n",
+ SMTP->helo_node,
+ CC->cs_host,
+ CC->cs_addr
+ );
}
else {
- cprintf("250-Greetings and joyous salutations.\r\n");
+ if (which_command == 1) {
+ cprintf("250-Hello %s (%s [%s])\r\n",
+ SMTP->helo_node,
+ CC->cs_host,
+ CC->cs_addr
+ );
+ }
+ else {
+ cprintf("250-Greetings and joyous salutations.\r\n");
+ }
cprintf("250-HELP\r\n");
cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
cprintf("250-PIPELINING\r\n");
}
+
/*
* Implement HELP command.
*/
CC->cs_flags &= ~CS_STEALTH;
}
else {
- cprintf("500 5.7.0 Authentication failed.\r\n");
+ cprintf("535 5.7.0 Authentication failed.\r\n");
}
SMTP->command_state = smtp_command;
}
char buf[SIZ];
if (strncasecmp(argbuf, "login", 5) ) {
- cprintf("550 5.7.4 We only support LOGIN authentication.\r\n");
+ cprintf("504 5.7.4 We only support LOGIN authentication.\r\n");
return;
}
* be sure to phree() them first!
*/
void smtp_rset(void) {
+ int is_lmtp;
+
+ /*
+ * Our entire SMTP state is discarded when a RSET command is issued,
+ * but we need to preserve this one little piece of information, so
+ * we save it for later.
+ */
+ is_lmtp = SMTP->is_lmtp;
+
memset(SMTP, 0, sizeof(struct citsmtp));
/*
* }
*/
+ /*
+ * Reinstate this little piece of information we saved (see above).
+ */
+ SMTP->is_lmtp = is_lmtp;
+
cprintf("250 2.0.0 Zap!\r\n");
}
return;
}
+ else if (SMTP->is_lmtp) {
+ /* Bypass forgery checking for LMTP */
+ }
+
/* Otherwise, make sure outsiders aren't trying to forge mail from
* this system.
*/
}
/* RBL check */
- if ( (!CC->logged_in) && (!CC->is_local_socket) ) {
+ if ( (!CC->logged_in)
+ && (!SMTP->is_lmtp) ) {
if (rbl_check(message_to_spammer)) {
cprintf("550 %s\r\n", message_to_spammer);
/* no need to phree(valid), it's not allocated yet */
}
if (valid->num_internet > 0) {
- if (SMTP->message_originated_locally == 0) {
+ if ( (SMTP->message_originated_locally == 0)
+ && (SMTP->is_lmtp == 0) ) {
cprintf("551 5.7.1 <%s> - relaying denied\r\n", recp);
phree(valid);
return;
char nowstamp[SIZ];
struct recptypes *valid;
int scan_errors;
+ int i;
+ char result[SIZ];
if (strlen(SMTP->from) == 0) {
cprintf("503 5.5.1 Need MAIL command first.\r\n");
datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
body = mallok(4096);
- /* FIXME
- it should be Received: from %s (real.name.dom [w.x.y.z])
- */
if (body != NULL) snprintf(body, 4096,
- "Received: from %s (%s)\n"
+ "Received: from %s (%s [%s])\n"
" by %s; %s\n",
SMTP->helo_node,
CC->cs_host,
+ CC->cs_addr,
config.c_fqdn,
nowstamp);
"5.7.1 Message rejected by filter");
}
- cprintf("550 %s\r\n", msg->cm_fields['0']);
+ sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
}
else { /* Ok, we'll accept this message. */
msgnum = CtdlSubmitMsg(msg, valid, "");
if (msgnum > 0L) {
- cprintf("250 2.0.0 Message accepted.\r\n");
+ sprintf(result, "250 2.0.0 Message accepted.\r\n");
}
else {
- cprintf("550 5.5.0 Internal delivery error\r\n");
+ sprintf(result, "550 5.5.0 Internal delivery error\r\n");
+ }
+ }
+
+ /* For SMTP and ESTMP, just print the result message. For LMTP, we
+ * have to print one result message for each recipient. Since there
+ * is nothing in Citadel which would cause different recipients to
+ * have different results, we can get away with just spitting out the
+ * same message once for each recipient.
+ */
+ if (SMTP->is_lmtp) {
+ for (i=0; i<SMTP->number_of_recipients; ++i) {
+ cprintf("%s", result);
}
}
+ else {
+ cprintf("%s", result);
+ }
CtdlFreeMessage(msg);
phree(valid);
smtp_data();
}
- else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
- smtp_hello(&cmdbuf[5], 1);
- }
-
else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
smtp_expn(&cmdbuf[5]);
}
smtp_hello(&cmdbuf[5], 0);
}
+ else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
+ smtp_hello(&cmdbuf[5], 1);
+ }
+
+ else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
+ smtp_hello(&cmdbuf[5], 2);
+ }
+
else if (!strncasecmp(cmdbuf, "HELP", 4)) {
smtp_help();
}
}
else {
- cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
+ cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
}
}
char *serv_smtp_init(void)
{
- SYM_SMTP = CtdlGetDynamicSymbol();
-
CtdlRegisterServiceHook(config.c_smtp_port, /* On the net... */
NULL,
smtp_greeting,
smtp_command_loop);
CtdlRegisterServiceHook(0, /* ...and locally */
- "smtp.socket",
- smtp_greeting,
+ "lmtp.socket",
+ lmtp_greeting,
smtp_command_loop);
smtp_init_spoolout();