]> code.citadel.org Git - citadel.git/blobdiff - citadel/serv_smtp.c
* sysdep.c: added new event hook type EVT_TIMER. Timer event hooks are called
[citadel.git] / citadel / serv_smtp.c
index 4c694c342b534ed86c117365256898483682ebdc..099e0110af6fce5d47f3e4aaeedb207123a69778 100644 (file)
@@ -13,9 +13,9 @@
 #include <sys/wait.h>
 #include <string.h>
 #include <limits.h>
+#include <time.h>
 #include "citadel.h"
 #include "server.h"
-#include <time.h>
 #include "sysdep_decls.h"
 #include "citserver.h"
 #include "support.h"
 #include "tools.h"
 #include "internet_addressing.h"
 #include "genstamp.h"
+#include "domain.h"
+#include "clientsocket.h"
 
 
 struct citsmtp {               /* Information about the current session */
        int command_state;
+       char helo_node[256];
        struct usersupp vrfy_buffer;
        int vrfy_count;
        char vrfy_match[256];
@@ -65,6 +68,8 @@ long SYM_SMTP_RECP;
 /*****************************************************************************/
 
 
+
+
 /*
  * Here's where our SMTP session begins its happy day.
  */
@@ -87,6 +92,8 @@ void smtp_greeting(void) {
  */
 void smtp_hello(char *argbuf, int is_esmtp) {
 
+       safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
+
        if (!is_esmtp) {
                cprintf("250 Greetings and joyous salutations.\r\n");
        }
@@ -324,9 +331,9 @@ void smtp_mail(char *argbuf) {
        else {
                cvt = convert_internet_address(user, node, SMTP->from);
                lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
-               if (!strcasecmp(node, config.c_nodename)) { /* FIX use fcn */
+               if (CtdlHostAlias(node) == hostalias_localhost) {
                        cprintf("550 You must log in to send mail from %s\r\n",
-                               config.c_fqdn);
+                               node);
                        strcpy(SMTP->from, "");
                        return;
                }
@@ -537,11 +544,12 @@ void smtp_data(void) {
        
        generate_rfc822_datestamp(nowstamp, time(NULL));
        body = mallok(4096);
+
        if (body != NULL) sprintf(body,
                "Received: from %s\n"
                "       by %s;\n"
                "       %s\n",
-                       "FIX.FIX.com",
+                       SMTP->helo_node,
                        config.c_fqdn,
                        nowstamp);
        
@@ -675,14 +683,259 @@ void smtp_command_loop(void) {
  * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
  *
  */
-void smtp_try(char *key, char *addr, int *status, char *dsn) {
+void smtp_try(char *key, char *addr, int *status, char *dsn, long msgnum)
+{
+       char buf[256];
+       int sock = (-1);
+       char mxhosts[1024];
+       int num_mxhosts;
+       int mx;
+       char user[256], node[256], name[256];
+
+       /* Parse out the host portion of the recipient address */
+       process_rfc822_addr(addr, user, node, name);
+       lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
+               user, node, name);
+
+       num_mxhosts = getmx(mxhosts, node);
+       lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
+       if (num_mxhosts < 1) {
+               *status = 5;
+               sprintf(dsn, "No MX hosts found for <%s>", node);
+               return;
+       }
+
+       for (mx=0; mx<num_mxhosts; ++mx) {
+               extract(buf, mxhosts, mx);
+               lprintf(9, "Trying <%s>\n", buf);
+               sock = sock_connect(buf, "25", "tcp");
+               sprintf(dsn, "Could not connect: %s", strerror(errno));
+               if (sock >= 0) lprintf(9, "Connected!\n");
+               if (sock < 0) sprintf(dsn, "%s", strerror(errno));
+               if (sock >= 0) break;
+       }
+
+       if (sock < 0) {
+               *status = 4;    /* dsn is already filled in */
+               return;
+       }
+
+       /* Process the SMTP greeting from the server */
+       if (sock_gets(sock, buf) < 0) {
+               *status = 4;
+               strcpy(dsn, "Connection broken during SMTP conversation");
+               sock_close(sock);
+               return;
+       }
+       lprintf(9, "%s\n", buf);
+       if (buf[0] != '2') {
+               if (buf[0] == '4') {
+                       *status = 4;
+                       strcpy(dsn, &buf[4]);
+                       sock_close(sock);
+                       return;
+               }
+               else {
+                       *status = 5;
+                       strcpy(dsn, &buf[4]);
+                       sock_close(sock);
+                       return;
+               }
+       }
+
+       /* At this point we know we are talking to a real SMTP server */
+
+       /* Do a HELO command */
+       sprintf(buf, "HELO %s", config.c_fqdn);
+       sock_puts(sock, buf);
+       if (sock_gets(sock, buf) < 0) {
+               *status = 4;
+               strcpy(dsn, "Connection broken during SMTP conversation");
+               sock_close(sock);
+               return;
+       }
+       lprintf(9, "%s\n", buf);
+       if (buf[0] != '2') {
+               if (buf[0] == '4') {
+                       *status = 4;
+                       strcpy(dsn, &buf[4]);
+                       sock_close(sock);
+                       return;
+               }
+               else {
+                       *status = 5;
+                       strcpy(dsn, &buf[4]);
+                       sock_close(sock);
+                       return;
+               }
+       }
+
+
+       /* HELO succeeded, now try the MAIL From: command */
+       sprintf(buf, "MAIL From: FIX@uncnsrd.mt-kisco.ny.us"); /* FIX */
+       sock_puts(sock, buf);
+       if (sock_gets(sock, buf) < 0) {
+               *status = 4;
+               strcpy(dsn, "Connection broken during SMTP conversation");
+               sock_close(sock);
+               return;
+       }
+       lprintf(9, "%s\n", buf);
+       if (buf[0] != '2') {
+               if (buf[0] == '4') {
+                       *status = 4;
+                       strcpy(dsn, &buf[4]);
+                       sock_close(sock);
+                       return;
+               }
+               else {
+                       *status = 5;
+                       strcpy(dsn, &buf[4]);
+                       sock_close(sock);
+                       return;
+               }
+       }
+
+
+       /* MAIL succeeded, now try the RCPT To: command */
+       sprintf(buf, "RCPT To: %s", addr);
+       sock_puts(sock, buf);
+       if (sock_gets(sock, buf) < 0) {
+               *status = 4;
+               strcpy(dsn, "Connection broken during SMTP conversation");
+               sock_close(sock);
+               return;
+       }
+       lprintf(9, "%s\n", buf);
+       if (buf[0] != '2') {
+               if (buf[0] == '4') {
+                       *status = 4;
+                       strcpy(dsn, &buf[4]);
+                       sock_close(sock);
+                       return;
+               }
+               else {
+                       *status = 5;
+                       strcpy(dsn, &buf[4]);
+                       sock_close(sock);
+                       return;
+               }
+       }
+
+
+       /* RCPT succeeded, now try the DATA command */
+       sock_puts(sock, "DATA");
+       if (sock_gets(sock, buf) < 0) {
+               *status = 4;
+               strcpy(dsn, "Connection broken during SMTP conversation");
+               sock_close(sock);
+               return;
+       }
+       lprintf(9, "%s\n", buf);
+       if (buf[0] != '3') {
+               if (buf[0] == '4') {
+                       *status = 3;
+                       strcpy(dsn, &buf[4]);
+                       sock_close(sock);
+                       return;
+               }
+               else {
+                       *status = 5;
+                       strcpy(dsn, &buf[4]);
+                       sock_close(sock);
+                       return;
+               }
+       }
 
-       *status = 3;
-       strcpy(dsn, "smtp_try() is not finished yet");
+       /* If we reach this point, the server is expecting data */
+
+       CtdlRedirectOutput(NULL, sock);
+       CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
+       CtdlRedirectOutput(NULL, -1);
+
+       sock_puts(sock, ".");
+       if (sock_gets(sock, buf) < 0) {
+               *status = 4;
+               strcpy(dsn, "Connection broken during SMTP conversation");
+               sock_close(sock);
+               return;
+       }
+       lprintf(9, "%s\n", buf);
+       if (buf[0] != '2') {
+               if (buf[0] == '4') {
+                       *status = 4;
+                       strcpy(dsn, &buf[4]);
+                       sock_close(sock);
+                       return;
+               }
+               else {
+                       *status = 5;
+                       strcpy(dsn, &buf[4]);
+                       sock_close(sock);
+                       return;
+               }
+       }
+
+       /* We did it! */
+       strcpy(dsn, &buf[4]);
+       *status = 2;
+
+       sock_puts(sock, "QUIT");
+       sock_gets(sock, buf);
+       lprintf(9, "%s\n", buf);
+       sock_close(sock);
 }
 
 
 
+/*
+ * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to remove
+ * all of the completed deliveries from a set of delivery instructions.
+ *
+ * It returns the number of incomplete deliveries remaining.
+ */
+int smtp_purge_completed_deliveries(char *instr) {
+       int i;
+       int lines;
+       int status;
+       char buf[1024];
+       char key[1024];
+       char addr[1024];
+       char dsn[1024];
+       int completed;
+       int incomplete = 0;
+
+       lines = num_tokens(instr, '\n');
+       for (i=0; i<lines; ++i) {
+               extract_token(buf, instr, i, '\n');
+               extract(key, buf, 0);
+               extract(addr, buf, 1);
+               status = extract_int(buf, 2);
+               extract(dsn, buf, 3);
+
+               completed = 0;
+
+               if (
+                  (!strcasecmp(key, "local"))
+                  || (!strcasecmp(key, "remote"))
+                  || (!strcasecmp(key, "ignet"))
+                  || (!strcasecmp(key, "room"))
+               ) {
+                       if (status == 2) completed = 1;
+                       else ++incomplete;
+               }
+
+               if (completed) {
+                       remove_token(instr, i, '\n');
+                       --i;
+                       --lines;
+               }
+       }
+
+       return(incomplete);
+}
+
+
 /*
  * smtp_do_procmsg()
  *
@@ -700,6 +953,7 @@ void smtp_do_procmsg(long msgnum) {
        char addr[1024];
        char dsn[1024];
        long text_msgid = (-1);
+       int incomplete_deliveries_remaining;
 
        msg = CtdlFetchMessage(msgnum);
        if (msg == NULL) {
@@ -716,7 +970,7 @@ void smtp_do_procmsg(long msgnum) {
                extract_token(buf, instr, i, '\n');
                if (num_tokens(buf, '|') < 2) {
                        lprintf(9, "removing <%s>\n", buf);
-                       remove_token(instr, i, '|');
+                       remove_token(instr, i, '\n');
                        --lines;
                        --i;
                }
@@ -733,7 +987,7 @@ void smtp_do_procmsg(long msgnum) {
        }
 
        if (text_msgid < 0L) {
-               lprintf(3, "SMTP: no 'msgid' directive found!\n", msgnum);
+               lprintf(3, "SMTP: no 'msgid' directive found!\n");
                phree(instr);
                return;
        }
@@ -755,7 +1009,7 @@ void smtp_do_procmsg(long msgnum) {
                        --i;
                        --lines;
                        lprintf(9, "SMTP: Trying <%s>\n", addr);
-                       smtp_try(key, addr, &status, dsn);
+                       smtp_try(key, addr, &status, dsn, text_msgid);
                        if (status != 2) {
                                if (results == NULL) {
                                        results = mallok(1024);
@@ -778,16 +1032,44 @@ void smtp_do_procmsg(long msgnum) {
                phree(results);
        }
 
-       /* Delete the instructions and replace with the updated ones */
-       CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);    
-        msg = mallok(sizeof(struct CtdlMessage));
-       memset(msg, 0, sizeof(struct CtdlMessage));
-       msg->cm_magic = CTDLMESSAGE_MAGIC;
-       msg->cm_anon_type = MES_NORMAL;
-       msg->cm_format_type = FMT_RFC822;
-       msg->cm_fields['M'] = instr;
-       CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
-       CtdlFreeMessage(msg);
+
+
+       /*
+        * Go through the delivery list, deleting completed deliveries
+        */
+       incomplete_deliveries_remaining = 
+               smtp_purge_completed_deliveries(instr);
+
+
+       /*
+        * No delivery instructions remain, so delete both the instructions
+        * message and the message message.
+        */
+       if (incomplete_deliveries_remaining <= 0)  {
+               CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);    
+               CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, NULL);    
+       }
+
+
+       /*
+        * Uncompleted delivery instructions remain, so delete the old
+        * instructions and replace with the updated ones.
+        */
+       if (incomplete_deliveries_remaining > 0) {
+               CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);    
+               msg = mallok(sizeof(struct CtdlMessage));
+               memset(msg, 0, sizeof(struct CtdlMessage));
+               msg->cm_magic = CTDLMESSAGE_MAGIC;
+               msg->cm_anon_type = MES_NORMAL;
+               msg->cm_format_type = FMT_RFC822;
+               msg->cm_fields['M'] = malloc(strlen(instr)+256);
+               sprintf(msg->cm_fields['M'],
+                       "Content-type: %s\n\n%s\n", SPOOLMIME, instr);
+               phree(instr);
+               CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
+               CtdlFreeMessage(msg);
+       }
+
 }
 
 
@@ -811,8 +1093,6 @@ void smtp_do_queue(void) {
 
 
 
-
-
 /*****************************************************************************/
 /*                      MODULE INITIALIZATION STUFF                          */
 /*****************************************************************************/
@@ -826,6 +1106,7 @@ char *Dynamic_Module_Init(void)
                                smtp_greeting,
                                smtp_command_loop);
        create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0);
+       CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
        return "$Id$";
 }