* Implemented most of MAIL FROM:
authorArt Cancro <ajc@citadel.org>
Sat, 11 Dec 1999 05:43:18 +0000 (05:43 +0000)
committerArt Cancro <ajc@citadel.org>
Sat, 11 Dec 1999 05:43:18 +0000 (05:43 +0000)
citadel/internet_addressing.c
citadel/internet_addressing.h
citadel/network/mail.sysinfo
citadel/routines.c
citadel/serv_smtp.c
citadel/server.h
citadel/tools.c
citadel/tools.h

index 2965f1297230058ef3eff7c7ca4d4474261ce3a0..8881214d075b09379c06eb702b302e5c53050af0 100644 (file)
 #include "config.h"
 #include "tools.h"
 #include "internet_addressing.h"
+#include "user_ops.h"
+
 
 /*
  * 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.
+ * FIX ... this needs to be updated to match any and all address syntaxes.
  */
 int fuzzy_match(struct usersupp *us, char *matchstring) {
        int a;
@@ -48,3 +48,202 @@ int fuzzy_match(struct usersupp *us, char *matchstring) {
 }
 
 
+
+
+/*
+ * Split an RFC822-style address into userid, host, and full name
+ * (Originally from citmail.c, and unchanged so far)
+ *
+ */
+void process_rfc822_addr(char *rfc822, char *user, char *node, char *name)
+{
+       int a;
+
+       /* extract full name - first, it's From minus <userid> */
+       strcpy(name, rfc822);
+       for (a = 0; a < strlen(name); ++a) {
+               if (name[a] == '<') {
+                       do {
+                               strcpy(&name[a], &name[a + 1]);
+                       } while ((strlen(name) > 0) && (name[a] != '>'));
+                       strcpy(&name[a], &name[a + 1]);
+               }
+       }
+       /* strip anything to the left of a bang */
+       while ((strlen(name) > 0) && (haschar(name, '!') > 0))
+               strcpy(name, &name[1]);
+
+       /* and anything to the right of a @ or % */
+       for (a = 0; a < strlen(name); ++a) {
+               if (name[a] == '@')
+                       name[a] = 0;
+               if (name[a] == '%')
+                       name[a] = 0;
+       }
+
+       /* but if there are parentheses, that changes the rules... */
+       if ((haschar(rfc822, '(') == 1) && (haschar(rfc822, ')') == 1)) {
+               strcpy(name, rfc822);
+               while ((strlen(name) > 0) && (name[0] != '(')) {
+                       strcpy(&name[0], &name[1]);
+               }
+               strcpy(&name[0], &name[1]);
+               for (a = 0; a < strlen(name); ++a) {
+                       if (name[a] == ')') {
+                               name[a] = 0;
+                       }
+               }
+       }
+       /* but if there are a set of quotes, that supersedes everything */
+       if (haschar(rfc822, 34) == 2) {
+               strcpy(name, rfc822);
+               while ((strlen(name) > 0) && (name[0] != 34)) {
+                       strcpy(&name[0], &name[1]);
+               }
+               strcpy(&name[0], &name[1]);
+               for (a = 0; a < strlen(name); ++a)
+                       if (name[a] == 34)
+                               name[a] = 0;
+       }
+       /* extract user id */
+       strcpy(user, rfc822);
+
+       /* first get rid of anything in parens */
+       for (a = 0; a < strlen(user); ++a)
+               if (user[a] == '(') {
+                       do {
+                               strcpy(&user[a], &user[a + 1]);
+                       } while ((strlen(user) > 0) && (user[a] != ')'));
+                       strcpy(&user[a], &user[a + 1]);
+               }
+       /* if there's a set of angle brackets, strip it down to that */
+       if ((haschar(user, '<') == 1) && (haschar(user, '>') == 1)) {
+               while ((strlen(user) > 0) && (user[0] != '<')) {
+                       strcpy(&user[0], &user[1]);
+               }
+               strcpy(&user[0], &user[1]);
+               for (a = 0; a < strlen(user); ++a)
+                       if (user[a] == '>')
+                               user[a] = 0;
+       }
+       /* strip anything to the left of a bang */
+       while ((strlen(user) > 0) && (haschar(user, '!') > 0))
+               strcpy(user, &user[1]);
+
+       /* and anything to the right of a @ or % */
+       for (a = 0; a < strlen(user); ++a) {
+               if (user[a] == '@')
+                       user[a] = 0;
+               if (user[a] == '%')
+                       user[a] = 0;
+       }
+
+
+       /* extract node name */
+       strcpy(node, rfc822);
+
+       /* first get rid of anything in parens */
+       for (a = 0; a < strlen(node); ++a)
+               if (node[a] == '(') {
+                       do {
+                               strcpy(&node[a], &node[a + 1]);
+                       } while ((strlen(node) > 0) && (node[a] != ')'));
+                       strcpy(&node[a], &node[a + 1]);
+               }
+       /* if there's a set of angle brackets, strip it down to that */
+       if ((haschar(node, '<') == 1) && (haschar(node, '>') == 1)) {
+               while ((strlen(node) > 0) && (node[0] != '<')) {
+                       strcpy(&node[0], &node[1]);
+               }
+               strcpy(&node[0], &node[1]);
+               for (a = 0; a < strlen(node); ++a)
+                       if (node[a] == '>')
+                               node[a] = 0;
+       }
+       /* strip anything to the left of a @ */
+       while ((strlen(node) > 0) && (haschar(node, '@') > 0))
+               strcpy(node, &node[1]);
+
+       /* strip anything to the left of a % */
+       while ((strlen(node) > 0) && (haschar(node, '%') > 0))
+               strcpy(node, &node[1]);
+
+       /* reduce multiple system bang paths to node!user */
+       while ((strlen(node) > 0) && (haschar(node, '!') > 1))
+               strcpy(node, &node[1]);
+
+       /* now get rid of the user portion of a node!user string */
+       for (a = 0; a < strlen(node); ++a)
+               if (node[a] == '!')
+                       node[a] = 0;
+
+       /* strip leading and trailing spaces in all strings */
+       striplt(user);
+       striplt(node);
+       striplt(name);
+}
+
+
+
+/*
+ * Back end for convert_internet_address()
+ * (Compares an internet name [buffer1] and stores in [buffer2] if found)
+ */
+void try_name(struct usersupp *us) {
+       
+       if (!strncasecmp(CC->buffer1, "cit", 3))
+               if (atol(&CC->buffer1[3]) == us->usernum)
+                       strcpy(CC->buffer2, us->fullname);
+
+       if (!collapsed_strcmp(CC->buffer1, us->fullname)) 
+                       strcpy(CC->buffer2, us->fullname);
+
+       if (us->uid != BBSUID)
+               if (!strcasecmp(CC->buffer1, getpwuid(us->uid)->pw_name))
+                       strcpy(CC->buffer2, us->fullname);
+}
+
+
+/*
+ * Convert an Internet email address to a Citadel user/host combination
+ */
+int convert_internet_address(char *destuser, char *desthost, char *source)
+{
+       char user[256];
+       char node[256];
+       char name[256];
+
+       /* Split it up */
+       process_rfc822_addr(source, user, node, name);
+
+       /* Map the FQDN to a Citadel node name
+        * FIX ... we have to check for all known aliases for the local
+        *         system, and also handle gateway domains, etc. etc.
+        */
+       if (!strcasecmp(node, config.c_fqdn)) {
+               strcpy(node, config.c_nodename);
+       }
+
+       /* Return an error condition if the node is not known.
+        * FIX ... make this work for non-local systems
+        */
+       if (strcasecmp(node, config.c_nodename)) {
+               return(1);
+       }
+       
+       /* Now try to resolve the name
+        * FIX ... do the multiple-addresses thing
+        */
+       if (!strcasecmp(node, config.c_nodename)) {
+               strcpy(destuser, user);
+               strcpy(desthost, config.c_nodename);
+               strcpy(CC->buffer1, user);
+               strcpy(CC->buffer2, "");
+               ForEachUser(try_name);
+               if (strlen(CC->buffer2) == 0) return(2);
+               strcpy(destuser, CC->buffer2);
+               return(0);
+       }
+
+       return(3);      /* unknown error */
+}
index 7a5841670084304336545c740c82ce91fc975ee7..917e227009c1cc76674973ff09ab32ac14691ced 100644 (file)
@@ -1 +1,3 @@
 int fuzzy_match(struct usersupp *us, char *matchstring);
+void process_rfc822_addr(char *rfc822, char *user, char *node, char *name);
+int convert_internet_address(char *destuser, char *desthost, char *source);
index 65e6d18cca42fa95eb11bf2fcd285b94eb09d14b..c5c556f2148eca1982b056e7b53bfd93c651d6e0 100644 (file)
@@ -1,19 +1,14 @@
-internet
-uum %s
-humannode Internet Gateway
-lastcontact 942634844 Sun Nov 14 22:00:44 1999
+test
+bin Mail
 
 uncnsrd
 bin Mail
 phonenum US 914 244 3252
 humannode Uncensored
-lastcontact 942634845 Sun Nov 14 22:00:45 1999
+lastcontact 944890658 Sat Dec 11 00:37:38 1999
 
-test
-bin Mail
-
-bin Mail
-bin Mail
-humannode 26711]: Adding neighbor system <test> to map
-lastcontact 940130533 Sat Oct 16 23:22:13 1999
+internet
+uum %s
+humannode Internet Gateway
+lastcontact 944890658 Sat Dec 11 00:37:38 1999
 
index 6361012502f33b958c363fe0ce7bb9824e2f00f7..903fe9ce162ae703e474df7a87e31f3c29333b65 100644 (file)
@@ -67,17 +67,6 @@ int struncmp(char *lstr, char *rstr, int len)
        }
 
 
-/* 
- * check for the presence of a character within a string (returns count)
- */
-int haschar(char *st, int ch)
-{
-       int a,b;
-       b=0;
-       for (a=0; a<strlen(st); ++a) if (st[a]==ch) ++b;
-       return(b);
-       }
-
 
 void back(int spaces) /* Destructive backspace */
             {
index 30a1daf1053560ac4da23c87a76521c7681e9a58..291f90064b12fc5883e08f1825ecd9fc091d1ef2 100644 (file)
@@ -36,6 +36,7 @@ struct citsmtp {
        struct usersupp vrfy_buffer;
        int vrfy_count;
        char vrfy_match[256];
+       char from[256];
 };
 
 enum {
@@ -88,6 +89,7 @@ void smtp_help(void) {
        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-    RSET\n");
@@ -247,11 +249,58 @@ void smtp_expn(char *argbuf) {
  */
 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) {
+               lprintf(9, "Me-checking <%s>\n", SMTP->from);
+               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;
+               }
+       }
+
+       cprintf("250 Sender ok.  Groovy.\n");
+}
+
+
+
 /* 
  * Main command loop for SMTP sessions.
  */
@@ -296,6 +345,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");
        }
index 662ef3ae963eb7353f5232f5f87b0de90651603f..0a7812c11829435df614289c6bf6c569c96dc870 100644 (file)
@@ -64,7 +64,7 @@ struct CitContext {
        char temp[32];          /* temp file name */
        int nologin;            /* not allowed to log in */
 
-       char net_node[32];
+       char net_node[32];      /* Is the client another Citadel server? */
        int client_socket;
        int cs_pid;             /* session ID */
        time_t cs_lastupdt;     /* time of last update */
@@ -72,7 +72,7 @@ struct CitContext {
        time_t lastidle;        /* For computing idle time */
        char lastcmdname[5];    /* name of last command executed */
        unsigned cs_flags;      /* miscellaneous flags */
-       void (*h_command_function) (void) ;     /* proto command function */
+       void (*h_command_function) (void) ;     /* service command function */
 
        /* feeping creaturisms... */
        int cs_clientdev;       /* client developer ID */
@@ -99,7 +99,10 @@ struct CitContext {
        char fake_roomname[ROOMNAMELEN];        /* Name of the fake room <bc> */
 
        int FloorBeingSearched; /* This is used by cmd_lrms() etc.   */
-       struct CtdlSessData *FirstSessData;
+
+       struct CtdlSessData *FirstSessData;     /* Allocated session data */
+       char buffer1[256];              /* General-purpose workspace */
+       char buffer2[256];              /* General-purpose workspace */
 };
 
 typedef struct CitContext t_context;
index 0a87ebd5f0f61e9fdfed6adfa35c758412725d04..8117cf1d26fce9ce9f18c8d8d06c8a65db49e105 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <ctype.h>
 #include <string.h>
 #include "tools.h"
 
@@ -214,3 +215,62 @@ void decode_base64(char *dest, char *source)
        }
     }
 }
+
+
+
+/*
+ * Strip leading and trailing spaces from a string
+ */
+void striplt(char *buf)
+{
+        while ((strlen(buf) > 0) && (buf[0] == 32))
+                strcpy(buf, &buf[1]);
+        while (buf[strlen(buf) - 1] == 32)
+                buf[strlen(buf) - 1] = 0;
+}
+
+
+
+
+
+/* 
+ * Return the number of occurances of character ch in string st
+ */ 
+int haschar(char *st, int ch)
+{
+       int a, b;
+       b = 0;
+       for (a = 0; a < strlen(st); ++a)
+               if (st[a] == ch)
+                       ++b;
+       return (b);
+}
+
+
+
+
+/*
+ * Compare two strings, insensitive to case, punctuation, and non-alnum chars
+ */
+int collapsed_strcmp(char *s1, char *s2) {
+       char *c1, *c2;
+       int i, ret;
+
+       c1 = strdup(s1);
+       c2 = strdup(s2);
+
+       for (i=0; i<strlen(c1); ++i) {
+               if (isupper(c1[i])) c1[i]=tolower(c1[i]);
+               while (!isalnum(c1[i])) strcpy(&c1[i], &c1[i+1]);
+       }
+
+       for (i=0; i<strlen(c2); ++i) {
+               if (isupper(c2[i])) c2[i]=tolower(c2[i]);
+               while (!isalnum(c2[i])) strcpy(&c2[i], &c2[i+1]);
+       }
+
+       ret = strcmp(c1, c2);
+       free(c1);
+       free(c2);
+       return(ret);
+}
index a2ae480985cef7ad0c9f36e1c3d494ec199c89f8..2286cd869b846102956915be2aa7bd78dd36ae93 100644 (file)
@@ -6,6 +6,9 @@ int extract_int (char *source, int parmnum);
 long int extract_long (char *source, long int parmnum);
 void encode_base64(char *dest, char *source);
 void decode_base64(char *dest, char *source);
+void striplt(char *);
+int haschar(char *st, int ch);
+int collapsed_strcmp(char *s1, char *s2);
 
 
 #define extract(dest,source,parmnum)   extract_token(dest,source,parmnum,'|')