]> code.citadel.org Git - citadel.git/blobdiff - citadel/serv_smtp.c
Removed the VRFY and EXPN commands from our SMTP server
[citadel.git] / citadel / serv_smtp.c
index 569af12bb10bdbda606008d1641768cdb5f20db9..a9e46f681b92a7e8a0fe79cb38c19fb58a382116 100644 (file)
@@ -20,6 +20,9 @@
  * RFC 2821 - Simple Mail Transfer Protocol
  * RFC 2822 - Internet Message Format
  * RFC 2920 - SMTP Service Extension for Command Pipelining
+ *  
+ * The VRFY and EXPN commands have been removed from this implementation
+ * because nobody uses these commands anymore, except for spammers.
  *
  */
 
@@ -86,9 +89,6 @@
 struct citsmtp {               /* Information about the current session */
        int command_state;
        char helo_node[SIZ];
-       struct ctdluser vrfy_buffer;
-       int vrfy_count;
-       char vrfy_match[SIZ];
        char from[SIZ];
        char recipients[SIZ];
        int number_of_recipients;
@@ -106,14 +106,7 @@ enum {                             /* Command states for login authentication */
        smtp_plain
 };
 
-enum {                         /* Delivery modes */
-       smtp_deliver_local,
-       smtp_deliver_remote
-};
-
 #define SMTP           CC->SMTP
-#define SMTP_RECPS     CC->SMTP_RECPS
-#define SMTP_ROOMS     CC->SMTP_ROOMS
 
 
 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
@@ -128,7 +121,7 @@ int run_queue_now = 0;      /* Set to 1 to ignore SMTP send retry times */
 /*
  * Here's where our SMTP session begins its happy day.
  */
-void smtp_greeting(void)
+void smtp_greeting(int is_msa)
 {
        char message_to_spammer[1024];
 
@@ -136,20 +129,17 @@ void smtp_greeting(void)
        CC->internal_pgm = 1;
        CC->cs_flags |= CS_STEALTH;
        SMTP = malloc(sizeof(struct citsmtp));
-       SMTP_RECPS = malloc(SIZ);
-       SMTP_ROOMS = malloc(SIZ);
        memset(SMTP, 0, sizeof(struct citsmtp));
-       memset(SMTP_RECPS, 0, SIZ);
-       memset(SMTP_ROOMS, 0, SIZ);
+       SMTP->is_msa = is_msa;
 
        /* If this config option is set, reject connections from problem
         * addresses immediately instead of after they execute a RCPT
         */
-       if (config.c_rbl_at_greeting) {
+       if ( (config.c_rbl_at_greeting) && (SMTP->is_msa == 0) ) {
                if (rbl_check(message_to_spammer)) {
                        cprintf("550 %s\r\n", message_to_spammer);
                        CC->kill_me = 1;
-                       /* no need to free(valid), it's not allocated yet */
+                       /* no need to free_recipients(valid), it's not allocated yet */
                        return;
                }
        }
@@ -161,10 +151,13 @@ void smtp_greeting(void)
                        config.c_maxsessions
                );
                CC->kill_me = 1;
-               /* no need to free(valid), it's not allocated yet */
+               /* no need to free_recipients(valid), it's not allocated yet */
                return;
        }
 
+       /* Note: the FQDN *must* appear as the first thing after the 220 code.
+        * Some clients (including citmail.c) depend on it being there.
+        */
        cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
 }
 
@@ -175,7 +168,7 @@ void smtp_greeting(void)
 #ifdef HAVE_OPENSSL
 void smtps_greeting(void) {
        CtdlStartTLS(NULL, NULL, NULL);
-       smtp_greeting();
+       smtp_greeting(0);
 }
 #endif
 
@@ -184,8 +177,7 @@ void smtps_greeting(void) {
  * SMTP MSA port requires authentication.
  */
 void smtp_msa_greeting(void) {
-       smtp_greeting();
-       SMTP->is_msa = 1;
+       smtp_greeting(1);
 }
 
 
@@ -193,16 +185,24 @@ void smtp_msa_greeting(void) {
  * LMTP is like SMTP but with some extra bonus footage added.
  */
 void lmtp_greeting(void) {
-       smtp_greeting();
+       smtp_greeting(0);
        SMTP->is_lmtp = 1;
 }
 
 
+/* 
+ * Generic SMTP MTA greeting
+ */
+void smtp_mta_greeting(void) {
+       smtp_greeting(0);
+}
+
+
 /*
  * We also have an unfiltered LMTP socket that bypasses spam filters.
  */
 void lmtp_unfiltered_greeting(void) {
-       smtp_greeting();
+       smtp_greeting(0);
        SMTP->is_lmtp = 1;
        SMTP->is_unfiltered = 1;
 }
@@ -295,7 +295,6 @@ void smtp_help(void) {
        cprintf("214-Commands accepted:\r\n");
        cprintf("214-    DATA\r\n");
        cprintf("214-    EHLO\r\n");
-       cprintf("214-    EXPN\r\n");
        cprintf("214-    HELO\r\n");
        cprintf("214-    HELP\r\n");
        cprintf("214-    MAIL\r\n");
@@ -303,7 +302,6 @@ void smtp_help(void) {
        cprintf("214-    QUIT\r\n");
        cprintf("214-    RCPT\r\n");
        cprintf("214-    RSET\r\n");
-       cprintf("214-    VRFY\r\n");
        cprintf("214     \r\n");
 }
 
@@ -422,86 +420,6 @@ void smtp_auth(char *argbuf) {
 }
 
 
-/*
- * Back end for smtp_vrfy() command
- */
-void smtp_vrfy_backend(struct ctdluser *us, void *data) {
-
-       if (!fuzzy_match(us, SMTP->vrfy_match)) {
-               ++SMTP->vrfy_count;
-               memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
-       }
-}
-
-
-/* 
- * Implements the VRFY (verify user name) command.
- * Performs fuzzy match on full user names.
- */
-void smtp_vrfy(char *argbuf) {
-       SMTP->vrfy_count = 0;
-       strcpy(SMTP->vrfy_match, argbuf);
-       ForEachUser(smtp_vrfy_backend, NULL);
-
-       if (SMTP->vrfy_count < 1) {
-               cprintf("550 5.1.1 String does not match anything.\r\n");
-       }
-       else if (SMTP->vrfy_count == 1) {
-               cprintf("250 %s <cit%ld@%s>\r\n",
-                       SMTP->vrfy_buffer.fullname,
-                       SMTP->vrfy_buffer.usernum,
-                       config.c_fqdn);
-       }
-       else if (SMTP->vrfy_count > 1) {
-               cprintf("553 5.1.4 Request ambiguous: %d users matched.\r\n",
-                       SMTP->vrfy_count);
-       }
-
-}
-
-
-
-/*
- * Back end for smtp_expn() command
- */
-void smtp_expn_backend(struct ctdluser *us, void *data) {
-
-       if (!fuzzy_match(us, SMTP->vrfy_match)) {
-
-               if (SMTP->vrfy_count >= 1) {
-                       cprintf("250-%s <cit%ld@%s>\r\n",
-                               SMTP->vrfy_buffer.fullname,
-                               SMTP->vrfy_buffer.usernum,
-                               config.c_fqdn);
-               }
-
-               ++SMTP->vrfy_count;
-               memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
-       }
-}
-
-
-/* 
- * Implements the EXPN (expand user name) command.
- * Performs fuzzy match on full user names.
- */
-void smtp_expn(char *argbuf) {
-       SMTP->vrfy_count = 0;
-       strcpy(SMTP->vrfy_match, argbuf);
-       ForEachUser(smtp_expn_backend, NULL);
-
-       if (SMTP->vrfy_count < 1) {
-               cprintf("550 5.1.1 String does not match anything.\r\n");
-       }
-       else if (SMTP->vrfy_count >= 1) {
-               cprintf("250 %s <cit%ld@%s>\r\n",
-                       SMTP->vrfy_buffer.fullname,
-                       SMTP->vrfy_buffer.usernum,
-                       config.c_fqdn);
-       }
-}
-
-
 /*
  * Implements the RSET (reset state) command.
  * Currently this just zeroes out the state buffer.  If pointers to data
@@ -667,7 +585,7 @@ void smtp_rcpt(char *argbuf) {
                if (config.c_rbl_at_greeting == 0) {    /* Don't RBL again if we already did it */
                        if (rbl_check(message_to_spammer)) {
                                cprintf("550 %s\r\n", message_to_spammer);
-                               /* no need to free(valid), it's not allocated yet */
+                               /* no need to free_recipients(valid), it's not allocated yet */
                                return;
                        }
                }
@@ -676,7 +594,7 @@ void smtp_rcpt(char *argbuf) {
        valid = validate_recipients(recp);
        if (valid->num_error != 0) {
                cprintf("599 5.1.1 Error: %s\r\n", valid->errormsg);
-               free(valid);
+               free_recipients(valid);
                return;
        }
 
@@ -684,7 +602,7 @@ void smtp_rcpt(char *argbuf) {
                if (CC->logged_in) {
                         if (CtdlCheckInternetMailPermission(&CC->user)==0) {
                                cprintf("551 5.7.1 <%s> - you do not have permission to send Internet mail\r\n", recp);
-                                free(valid);
+                                free_recipients(valid);
                                 return;
                         }
                 }
@@ -694,7 +612,7 @@ void smtp_rcpt(char *argbuf) {
                if ( (SMTP->message_originated_locally == 0)
                   && (SMTP->is_lmtp == 0) ) {
                        cprintf("551 5.7.1 <%s> - relaying denied\r\n", recp);
-                       free(valid);
+                       free_recipients(valid);
                        return;
                }
        }
@@ -706,7 +624,7 @@ void smtp_rcpt(char *argbuf) {
        strcat(SMTP->recipients, recp);
        SMTP->number_of_recipients += 1;
        if (valid != NULL) 
-               free(valid);
+               free_recipients(valid);
 }
 
 
@@ -860,7 +778,7 @@ void smtp_data(void) {
 
        /* Clean up */
        CtdlFreeMessage(msg);
-       free(valid);
+       free_recipients(valid);
        smtp_data_clear();      /* clear out the buffers now */
 }
 
@@ -924,10 +842,6 @@ void smtp_command_loop(void) {
                smtp_data();
        }
 
-       else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
-               smtp_expn(&cmdbuf[5]);
-       }
-
        else if (!strncasecmp(cmdbuf, "HELO", 4)) {
                smtp_hello(&cmdbuf[5], 0);
        }
@@ -970,10 +884,6 @@ void smtp_command_loop(void) {
                smtp_starttls();
        }
 #endif
-       else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
-               smtp_vrfy(&cmdbuf[5]);
-       }
-
        else {
                cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
        }
@@ -1193,9 +1103,10 @@ void smtp_try(const char *key, const char *addr, int *status,
 
        /* Do an AUTH command if necessary */
        if (strlen(mx_user) > 0) {
+               char encoded[1024];
                sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
-               CtdlEncodeBase64(mailfrom, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2);
-               snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", mailfrom);
+               CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2);
+               snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
                lprintf(CTDL_DEBUG, ">%s", buf);
                sock_write(sock, buf, strlen(buf));
                if (ml_sock_gets(sock, buf) < 0) {
@@ -1360,6 +1271,7 @@ void smtp_do_bounce(char *instr) {
        char addr[1024];
        char dsn[1024];
        char bounceto[1024];
+       char boundary[64];
        int num_bounces = 0;
        int bounce_this = 0;
        long bounce_msgid = (-1);
@@ -1368,13 +1280,16 @@ void smtp_do_bounce(char *instr) {
        int give_up = 0;
        struct recptypes *valid;
        int successful_bounce = 0;
+       static int seq = 0;
+       char *omsgtext;
+       size_t omsgsize;
+       long omsgid = (-1);
 
        lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
        strcpy(bounceto, "");
-
+       sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
        lines = num_tokens(instr, '\n');
 
-
        /* See if it's time to give up on delivery of this message */
        for (i=0; i<lines; ++i) {
                extract_token(buf, instr, i, '\n', sizeof buf);
@@ -1389,25 +1304,39 @@ void smtp_do_bounce(char *instr) {
                give_up = 1;
        }
 
+       /* Start building our bounce message */
+
        bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
        if (bmsg == NULL) return;
        memset(bmsg, 0, sizeof(struct CtdlMessage));
 
         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
         bmsg->cm_anon_type = MES_NORMAL;
-        bmsg->cm_format_type = 1;
+        bmsg->cm_format_type = FMT_RFC822;
         bmsg->cm_fields['A'] = strdup("Citadel");
         bmsg->cm_fields['O'] = strdup(MAILROOM);
         bmsg->cm_fields['N'] = strdup(config.c_nodename);
         bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
-
-       if (give_up) bmsg->cm_fields['M'] = strdup(
+       bmsg->cm_fields['M'] = malloc(1024);
+
+        strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
+        strcat(bmsg->cm_fields['M'], boundary);
+        strcat(bmsg->cm_fields['M'], "\"\r\n");
+        strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
+        strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
+        strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
+        strcat(bmsg->cm_fields['M'], "--");
+        strcat(bmsg->cm_fields['M'], boundary);
+        strcat(bmsg->cm_fields['M'], "\r\n");
+        strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
+
+       if (give_up) strcat(bmsg->cm_fields['M'],
 "A message you sent could not be delivered to some or all of its recipients\n"
 "due to prolonged unavailability of its destination(s).\n"
 "Giving up on the following addresses:\n\n"
 );
 
-        else bmsg->cm_fields['M'] = strdup(
+        else strcat(bmsg->cm_fields['M'],
 "A message you sent could not be delivered to some or all of its recipients.\n"
 "The following addresses were undeliverable:\n\n"
 );
@@ -1430,12 +1359,11 @@ void smtp_do_bounce(char *instr) {
                        strcpy(bounceto, addr);
                }
 
-               if (
-                  (!strcasecmp(key, "local"))
-                  || (!strcasecmp(key, "remote"))
-                  || (!strcasecmp(key, "ignet"))
-                  || (!strcasecmp(key, "room"))
-               ) {
+               if (!strcasecmp(key, "msgid")) {
+                       omsgid = atol(addr);
+               }
+
+               if (!strcasecmp(key, "remote")) {
                        if (status == 5) bounce_this = 1;
                        if (give_up) bounce_this = 1;
                }
@@ -1453,7 +1381,7 @@ void smtp_do_bounce(char *instr) {
                        strcat(bmsg->cm_fields['M'], addr);
                        strcat(bmsg->cm_fields['M'], ": ");
                        strcat(bmsg->cm_fields['M'], dsn);
-                       strcat(bmsg->cm_fields['M'], "\n");
+                       strcat(bmsg->cm_fields['M'], "\r\n");
 
                        remove_token(instr, i, '\n');
                        --i;
@@ -1461,6 +1389,36 @@ void smtp_do_bounce(char *instr) {
                }
        }
 
+       /* Attach the original message */
+       if (omsgid >= 0) {
+               strcat(bmsg->cm_fields['M'], "--");
+               strcat(bmsg->cm_fields['M'], boundary);
+               strcat(bmsg->cm_fields['M'], "\r\n");
+               strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
+               strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
+               strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
+               strcat(bmsg->cm_fields['M'], "\r\n");
+       
+               CC->redirect_buffer = malloc(SIZ);
+               CC->redirect_len = 0;
+               CC->redirect_alloc = SIZ;
+               CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
+               omsgtext = CC->redirect_buffer;
+               omsgsize = CC->redirect_len;
+               CC->redirect_buffer = NULL;
+               CC->redirect_len = 0;
+               CC->redirect_alloc = 0;
+               bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
+                               (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
+               strcat(bmsg->cm_fields['M'], omsgtext);
+               free(omsgtext);
+       }
+
+       /* Close the multipart MIME scope */
+        strcat(bmsg->cm_fields['M'], "--");
+        strcat(bmsg->cm_fields['M'], boundary);
+        strcat(bmsg->cm_fields['M'], "--\r\n");
+
        /* Deliver the bounce if there's anything worth mentioning */
        lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
        if (num_bounces > 0) {
@@ -1488,7 +1446,7 @@ void smtp_do_bounce(char *instr) {
 
                /* Free up the memory we used */
                if (valid != NULL) {
-                       free(valid);
+                       free_recipients(valid);
                }
        }
 
@@ -1524,12 +1482,7 @@ int smtp_purge_completed_deliveries(char *instr) {
 
                completed = 0;
 
-               if (
-                  (!strcasecmp(key, "local"))
-                  || (!strcasecmp(key, "remote"))
-                  || (!strcasecmp(key, "ignet"))
-                  || (!strcasecmp(key, "room"))
-               ) {
+               if (!strcasecmp(key, "remote")) {
                        if (status == 2) completed = 1;
                        else ++incomplete;
                }
@@ -1843,8 +1796,6 @@ void smtp_cleanup_function(void) {
 
        lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
        free(SMTP);
-       free(SMTP_ROOMS);
-       free(SMTP_RECPS);
 }
 
 
@@ -1856,7 +1807,7 @@ char *serv_smtp_init(void)
 
        CtdlRegisterServiceHook(config.c_smtp_port,     /* SMTP MTA */
                                NULL,
-                               smtp_greeting,
+                               smtp_mta_greeting,
                                smtp_command_loop,
                                NULL);