* Got some more parsing in there
[citadel.git] / citadel / serv_smtp.c
index 687ccd0e482f862f6c4a6f5768e2b3f0777e2dc6..ec7b9ae2e0a5aae699b98e7f1be3dd34a7727a23 100644 (file)
 #include "database.h"
 #include "msgbase.h"
 #include "tools.h"
+#include "internet_addressing.h"
 
-struct citsmtp {
+
+struct citsmtp {               /* Information about the current session */
        int command_state;
        struct usersupp vrfy_buffer;
        int vrfy_count;
        char vrfy_match[256];
+       char from[256];
+       char recipient[256];
+       int number_of_recipients;
 };
 
-enum {
+enum {                         /* Command states for login authentication */
        smtp_command,
        smtp_user,
        smtp_password
@@ -83,12 +88,16 @@ void smtp_hello(char *argbuf, int is_esmtp) {
  */
 void smtp_help(void) {
        cprintf("214-Here's the frequency, Kenneth:\n");
+       cprintf("214-    DATA\n");
        cprintf("214-    EHLO\n");
        cprintf("214-    EXPN\n");
        cprintf("214-    HELO\n");
        cprintf("214-    HELP\n");
+       cprintf("214-    MAIL\n");
        cprintf("214-    NOOP\n");
        cprintf("214-    QUIT\n");
+       cprintf("214-    RCPT\n");
+       cprintf("214-    RSET\n");
        cprintf("214-    VRFY\n");
        cprintf("214 I could tell you more, but then I'd have to kill you.\n");
 }
@@ -157,26 +166,6 @@ void smtp_auth(char *argbuf) {
 }
 
 
-/*
- * Return 0 if a given string fuzzy-matches a Citadel user account
- *
- * FIX ... this needs to be updated to match any and all ways of addressing
- *         a user.  It may even be appropriate to move this out of SMTP and
- *         into the server core.
- */
-int fuzzy_match(struct usersupp *us, char *matchstring) {
-       int a;
-
-       for (a=0; a<strlen(us->fullname); ++a) {
-               if (!strncasecmp(&us->fullname[a],
-                  matchstring, strlen(matchstring))) {
-                       return 0;
-               }
-       }
-       return -1;
-}
-
-
 /*
  * Back end for smtp_vrfy() command
  */
@@ -257,6 +246,162 @@ void smtp_expn(char *argbuf) {
 }
 
 
+/*
+ * Implements the RSET (reset state) command.
+ * Currently this just zeroes out the state buffer.  If pointers to data
+ * allocated with mallok() are ever placed in the state buffer, we have to
+ * be sure to phree() them first!
+ */
+void smtp_rset(void) {
+       memset(SMTP, 0, sizeof(struct citsmtp));
+       if (CC->logged_in) logout(CC);
+       cprintf("250 Zap!\n");
+}
+
+
+
+/*
+ * Implements the "MAIL From:" command
+ */
+void smtp_mail(char *argbuf) {
+       char user[256];
+       char node[256];
+       int cvt;
+
+       if (strlen(SMTP->from) != 0) {
+               cprintf("503 Only one sender permitted\n");
+               return;
+       }
+
+       if (strncasecmp(argbuf, "From:", 5)) {
+               cprintf("501 Syntax error\n");
+               return;
+       }
+
+       strcpy(SMTP->from, &argbuf[5]);
+       striplt(SMTP->from);
+
+       if (strlen(SMTP->from) == 0) {
+               cprintf("501 Empty sender name is not permitted\n");
+               return;
+       }
+
+
+       /* If this SMTP connection is from a logged-in user, make sure that
+        * the user only sends email from his/her own address.
+        */
+       if (CC->logged_in) {
+               cvt = convert_internet_address(user, node, SMTP->from);
+               lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
+               if ( (cvt != 0) || (strcasecmp(user, CC->usersupp.fullname))) {
+                       cprintf("550 <%s> is not your address.\n", SMTP->from);
+                       strcpy(SMTP->from, "");
+                       return;
+               }
+       }
+
+       /* Otherwise, make sure outsiders aren't trying to forge mail from
+        * this system.
+        */
+       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 */
+                       cprintf("550 You must log in to send mail from %s\n",
+                               config.c_fqdn);
+                       strcpy(SMTP->from, "");
+                       return;
+               }
+       }
+
+       cprintf("250 Sender ok.  Groovy.\n");
+}
+
+
+
+/*
+ * Implements the "RCPT To:" command
+ */
+void smtp_rcpt(char *argbuf) {
+       int cvt;
+       char user[256];
+       char node[256];
+
+       if (strlen(SMTP->from) == 0) {
+               cprintf("503 MAIL first, then RCPT.  Duh.\n");
+               return;
+       }
+
+       if (strlen(SMTP->recipient) > 0) {
+               cprintf("550 Only one recipient allowed (FIX)\n");
+               return;
+       }
+
+       if (strncasecmp(argbuf, "To:", 3)) {
+               cprintf("501 Syntax error\n");
+               return;
+       }
+
+       strcpy(SMTP->recipient, &argbuf[3]);
+       striplt(SMTP->recipient);
+
+       cvt = convert_internet_address(user, node, SMTP->recipient);
+       switch(cvt) {
+               case rfc822_address_locally_validated:
+                       cprintf("250 %s is a valid recipient.\n", user);
+                       return;
+               case rfc822_no_such_user:
+                       cprintf("550 %s: no such user\n", SMTP->recipient);
+                       strcpy(SMTP->recipient, "");
+                       return;
+       }
+
+       strcpy(SMTP->recipient, "");
+       cprintf("599 Unknown error (FIX)\n");
+}
+
+
+
+
+/*
+ * Implements the DATA command
+ */
+void smtp_data(void) {
+       char *body;
+       struct CtdlMessage *msg;
+
+/*
+       if (strlen(SMTP->from) == 0) {
+               cprintf("503 Need MAIL command first.\n");
+               return;
+       }
+
+       if (SMTP->number_of_recipients < 1) {
+               cprintf("503 Need RCPT command first.\n");
+               return;
+       }
+
+*/
+
+       cprintf("354 Transmit message now; terminate with '.' by itself\n");
+       body = CtdlReadMessageBody(".", config.c_maxmsglen);
+       if (body == NULL) {
+               cprintf("550 Unable to save message text: internal error.\n");
+               return;
+       }
+
+       fprintf(stderr, "Converting message...\n");
+       msg = convert_internet_message(body);
+       phree(body);
+
+       CtdlSaveMsg(msg, "", BASEROOM, MES_LOCAL, 1);   /* FIX temporary */
+       CtdlFreeMessage(msg);
+
+       cprintf("599 command unfinished but message saved\n");
+}
+
+
+
 
 /* 
  * Main command loop for SMTP sessions.
@@ -286,6 +431,10 @@ void smtp_command_loop(void) {
                smtp_auth(&cmdbuf[5]);
        }
 
+       else if (!strncasecmp(cmdbuf, "DATA", 4)) {
+               smtp_data();
+       }
+
        else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
                smtp_hello(&cmdbuf[5], 1);
        }
@@ -302,6 +451,10 @@ void smtp_command_loop(void) {
                smtp_help();
        }
 
+       else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
+               smtp_mail(&cmdbuf[5]);
+       }
+
        else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
                cprintf("250 This command successfully did nothing.\n");
        }
@@ -312,6 +465,14 @@ void smtp_command_loop(void) {
                return;
                }
 
+       else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
+               smtp_rcpt(&cmdbuf[5]);
+       }
+
+       else if (!strncasecmp(cmdbuf, "RSET", 4)) {
+               smtp_rset();
+       }
+
        else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
                smtp_vrfy(&cmdbuf[5]);
        }