]> code.citadel.org Git - citadel.git/blobdiff - citadel/serv_smtp.c
* Add specific error codes for every command on the wire protocol, so that
[citadel.git] / citadel / serv_smtp.c
index 28a2ec01534ece1ce522d8b11bad3c088ad037e3..56408ca6e245398c5354293b7e83a1b0ab203883 100644 (file)
@@ -1,8 +1,23 @@
 /*
  * $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
  *
  */
 
@@ -68,9 +83,9 @@ struct citsmtp {              /* Information about the current session */
        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 */
@@ -88,9 +103,6 @@ enum {                               /* Delivery modes */
 #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 */
 
@@ -120,19 +132,52 @@ void smtp_greeting(void) {
        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");
@@ -142,6 +187,7 @@ void smtp_hello(char *argbuf, int is_esmtp) {
 }
 
 
+
 /*
  * Implement HELP command.
  */
@@ -198,7 +244,7 @@ void smtp_get_pass(char *argbuf) {
                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;
 }
@@ -211,7 +257,7 @@ void smtp_auth(char *argbuf) {
        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;
        }
 
@@ -314,6 +360,15 @@ void smtp_expn(char *argbuf) {
  * 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));
 
        /*
@@ -327,6 +382,11 @@ void smtp_rset(void) {
         * }
         */
 
+       /*
+        * Reinstate this little piece of information we saved (see above).
+        */
+       SMTP->is_lmtp = is_lmtp;
+
        cprintf("250 2.0.0 Zap!\r\n");
 }
 
@@ -386,6 +446,10 @@ void smtp_mail(char *argbuf) {
                return;
        }
 
+       else if (SMTP->is_lmtp) {
+               /* Bypass forgery checking for LMTP */
+       }
+
        /* Otherwise, make sure outsiders aren't trying to forge mail from
         * this system.
         */
@@ -433,7 +497,8 @@ void smtp_rcpt(char *argbuf) {
        }
 
        /* 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 */
@@ -449,7 +514,8 @@ void smtp_rcpt(char *argbuf) {
        }
 
        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;
@@ -477,6 +543,8 @@ void smtp_data(void) {
        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");
@@ -551,18 +619,33 @@ void smtp_data(void) {
                                "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);
@@ -606,10 +689,6 @@ void smtp_command_loop(void) {
                smtp_data();
        }
 
-       else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
-               smtp_hello(&cmdbuf[5], 1);
-       }
-
        else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
                smtp_expn(&cmdbuf[5]);
        }
@@ -618,6 +697,14 @@ void smtp_command_loop(void) {
                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();
        }
@@ -1395,7 +1482,7 @@ void cmd_smtp(char *argbuf) {
        }
 
        else {
-               cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE);
+               cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
        }
 
 }
@@ -1433,16 +1520,14 @@ void smtp_init_spoolout(void) {
 
 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();