[PATCH] autocreate inbox

Christos Soulios soulbros at noc.uoa.gr
Thu Apr 24 04:00:26 EDT 2003


Please find attached a patch that enables cyrus to automatically create user
Inboxes. 

In a few words this patch adds the functionality of automatic creation of the
users' INBOX folders into the Cyrus IMAP server. It is implemented as two
features, namely the  "create on login" and "create on post". 

Create on login
===============
This feauture provides automatic creation of a user's INBOX folder when all of
the following requirements are met:

i)  The user has succesfully passed the authentication procedure.
ii) The corresponding to the user's INBOX folder does not exist.

The user's first login is the most typical case when all four requirements are met. 

Create on post
==============
This feauture provides automatic creation of a user's INBOX folder when the
following requirements are met. 

i) An e-mail message addressed to the user has been received.  
ii) The recipient is not any of the imap_admins or admins accounts. 
iii) The recipient's INBOX does not exist.


For more information, as well as documentation etc, please visit
http://email.uoa.gr/autocreate

Regards, 
Christos

-- 
/**
 * Christos Soulios
 * Department of Informatics
 * University of Athens
 * e-mail : soulbros at noc.uoa.gr
 */




-------------- next part --------------
diff -Naur cyrus-imapd-2.1.12/README.autocreate cyrus-imapd-2.1.12.autocreate/README.autocreate
--- cyrus-imapd-2.1.12/README.autocreate	1970-01-01 02:00:00.000000000 +0200
+++ cyrus-imapd-2.1.12.autocreate/README.autocreate	2003-04-23 17:39:42.000000000 +0300
@@ -0,0 +1,124 @@
+Cyrus IMAP autocreate Inbox patch
+----------------------------------
+
+The design of Cyrus IMAP server does not predict the automatic creation of users'
+INBOX folders. The creation of a user's INBOX is considered to be an external task,
+that has to be completed as part of the user e-mail account creation procedure. 
+Hence, to create a new e-mail account the site administrator has to 
+a) Include the new account in the user database for the authentication procedure
+   (e.g. sasldb, shadow, mysql, ldap).
+b) Create the corresponding INBOX folder. 
+
+Alternatively, the user, if succesfully authenticated, may create his own INBOX folder,
+as long as the configuration of the site allows it (see "autocreatequota" in imapd.conf).
+Unlike what uncareful readers may think, enabling the "autocreatequota" option, doesn't 
+lead to the automatic INBOX folder creation by Cyrus IMAP server.
+In fact, "autocreate" means that the IMAP clients are allowed to automatically create 
+the user INBOX. 
+
+This patch adds the functionality of automatic creation of the users' INBOX folders into
+the Cyrus IMAP server. It is implemented as two features, namely the  "create on login"
+and "create on post".
+ 
+
+
+Create on login
+===============
+This feauture provides automatic creation of a user's INBOX folder when all of the 
+following requirements are met:
+
+i)  The user has succesfully passed the authentication procedure.
+
+ii) The user's authorization ID (typically the same as the user's
+authentication ID)  doesn't belong to the imap_admins or admins
+accounts (see imapd.conf).
+
+iii) The "autocreatequota" option in the imap configuration file 
+has been set to a non zero value. 
+
+iv) The corresponding to the user's authorizationID INBOX folder
+does not exist.
+
+The user's first login is the most typical case when all four requirements are met. 
+Note that if the authenticatedID is allowed to proxy to another account for which 
+all of the above requirements are met, the corresponding INBOX folder for that account 
+will be created.
+
+
+
+Create on post
+==============
+This feauture provides automatic creation of a user's INBOX folder when all of the 
+following requirements are met. 
+
+i) An e-mail message addressed to the user has been received.  
+
+ii) The recipient is not any of the imap_admins or admins accounts. 
+Note that passing e-mails to admins or imap_admins accounts from 
+the MTA to LMTP should be avoided in any case.
+
+iii) The recipient's INBOX does not exist.
+
+iv) The "autocreatequota" option in the imap configuration file 
+has been set to a non zero value. 
+
+v) The "createonpost" option in the imap configuration file 
+has been switched on. 
+
+
+One may argue that the above features are not actually "features"  but basic 
+requirements, part of the design considerations of any widely known e-mail system 
+and should be provided by the core code and not by a patch.
+My answer to them is that i certainly aggree.
+
+Besides the automatic creation of INBOX folders, additional functionalities are
+provided:
+
+A) Automatic creation of INBOX subfolders controlled by "autocreateinboxfolders"
+configuration option. e.g 
+
+autocreateinboxfolders: sent|drafts|spam|templates
+
+B) Automatic subscription of INBOX subfolders controlled by "autosubscribefolders"
+configuration option. e.g
+
+autosubscribefolders: sent|spam
+
+Obviously, only subscription to subfolders included in the "autocreateinboxfolder"
+list is meaningfull. 
+
+
+
+
+Issues to be considered 
+=======================
+
+I) In order to use the create on post feauture one should be absolutely sure that: 
+a) The MTA checks the validity of the e-mail recipient before sending the e-mail to
+LMTP. This is an RFC821 requirement. This usually expands to "the mta should be 
+able to use the account database as user mailbox database". 
+b) Only authorized accounts/services can talk to LMTP.
+
+II) Especially in the case of imap logins, the current patch implementation checks
+for the INBOX folder existence upon login, causing an extra mailbox lookup in most 
+of the cases. 
+A better approach would be to chase the "IMAP_MAILBOX_NONEXISTENT" error code and
+check if the error is associated with an INBOX folder. However, this would mess up
+Cyrus code. The way it was implemented may not have been the most performance
+optimized, but it produces a much cleaner and simple patch.
+
+
+
+
+Things to be done
+=================
+
+1. Support MURDER architecture. 
+2. Support virtual domains.
+3. Enable the automatic subscription of user to shared folders and bulletin boards.
+
+
+
+
+For more information and updates please visit http://email.uoa.gr/autocreate
+
diff -Naur cyrus-imapd-2.1.12/imap/imapd.c cyrus-imapd-2.1.12.autocreate/imap/imapd.c
--- cyrus-imapd-2.1.12/imap/imapd.c	2003-01-11 20:45:14.000000000 +0200
+++ cyrus-imapd-2.1.12.autocreate/imap/imapd.c	2003-04-21 21:18:18.000000000 +0300
@@ -143,6 +143,8 @@
 void shut_down(int code);
 void fatal(const char *s, int code);
 
+void autocreate_inbox(void);
+
 void cmdloop(void);
 void cmd_login(char *tag, char *user);
 void cmd_authenticate(char *tag, char *authtype);
@@ -1691,6 +1693,48 @@
     }
 }
 
+
+/*
+ * Autocreate Inbox and subfolders upon login
+ */
+void autocreate_inbox()
+{
+    char inboxname[MAX_MAILBOX_NAME+1];
+    int autocreatequota;
+    int r;
+
+    /*
+     * Exlude admin's accounts
+     */
+    if (imapd_userisadmin || imapd_userisproxyadmin)
+        return;
+
+    /*
+     * Exclude anonymous
+     */
+    if (!strcmp(imapd_userid, "anonymous"))
+        return;
+
+     
+    if ((autocreatequota = config_getint("autocreatequota", 0))) {
+        /* This is actyally not required
+           as long as the lenght of userid is ok */
+           r = (*imapd_namespace.mboxname_tointernal) (&imapd_namespace,
+                                      "INBOX", imapd_userid, inboxname);
+           if (!r)
+               r = mboxlist_lookup(inboxname, NULL, NULL, NULL);
+
+           if (r == IMAP_MAILBOX_NONEXISTENT)
+               mboxlist_autocreateinbox(&imapd_namespace, imapd_userid,
+                         imapd_authstate, inboxname, autocreatequota);
+     }
+}
+
+
+
+
+
+
 /*
  * Perform a LOGIN command
  */
@@ -1823,6 +1867,9 @@
     mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid);
 
     freebuf(&passwdbuf);
+
+    autocreate_inbox();
+
     return;
 }
 
@@ -1943,6 +1990,9 @@
     /* Translate any separators in userid */
     mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid);
 
+    
+    autocreate_inbox();
+
     return;
 }
 
diff -Naur cyrus-imapd-2.1.12/imap/lmtpd.c cyrus-imapd-2.1.12.autocreate/imap/lmtpd.c
--- cyrus-imapd-2.1.12/imap/lmtpd.c	2002-11-28 19:47:15.000000000 +0200
+++ cyrus-imapd-2.1.12.autocreate/imap/lmtpd.c	2003-04-21 21:18:18.000000000 +0300
@@ -148,6 +148,8 @@
 struct lmtp_func mylmtp = { &deliver, &verify_user, &shut_down,
 			    &spoolfile, &removespool, 0, 1, 0 };
 
+static int autocreate_inbox(char *rcpt_userid);
+
 static void logdupelem();
 static void usage();
 static void setup_sieve();
@@ -1372,6 +1374,50 @@
     exit(code);
 }
 
+
+
+/*
+ * Autocreate Inbox and subfolders upon login
+ */
+int autocreate_inbox(char *rcpt_userid)
+{
+    struct auth_state *authstate;
+    char inboxname[MAX_MAILBOX_NAME+1];
+    int rcptisadmin ;
+    int autocreatequota;
+    int r;
+
+    /*
+     * Exclude anonymous
+     */
+    if (!strcmp(rcpt_userid, "anonymous"))
+        return IMAP_MAILBOX_NONEXISTENT;
+
+    /*
+     * Check for autocreatequota and createonpost
+     */
+    if (!(autocreatequota = config_getint("autocreatequota", 0)) ||
+        !(config_getswitch("createonpost", 0))) 
+        return IMAP_MAILBOX_NONEXISTENT;
+
+    /*
+     * Exclude admin's accounts
+     */
+     authstate = auth_newstate(rcpt_userid, NULL);
+     rcptisadmin = authisa(authstate, "imap", "admins");
+     if (rcptisadmin)
+          return IMAP_MAILBOX_NONEXISTENT;
+
+     r = (*lmtpd_namespace.mboxname_tointernal) (&lmtpd_namespace,
+                                "INBOX", rcpt_userid, inboxname); 
+     if (!r)
+     r = mboxlist_autocreateinbox(&lmtpd_namespace, rcpt_userid,
+                         authstate, inboxname, autocreatequota);
+     return r;
+}
+
+
+
 static int verify_user(const char *user, long quotacheck,
 		       struct auth_state *authstate)
 {
@@ -1405,6 +1451,8 @@
 	       - don't care about message size (1 msg over quota allowed) */
 	    r = append_check(buf, MAILBOX_FORMAT_NORMAL, authstate,
 			     0, quotacheck > 0 ? 0 : quotacheck);
+            if (r == IMAP_MAILBOX_NONEXISTENT)
+                r = autocreate_inbox(buf+5);
 	}
     }
 
diff -Naur cyrus-imapd-2.1.12/imap/mboxlist.c cyrus-imapd-2.1.12.autocreate/imap/mboxlist.c
--- cyrus-imapd-2.1.12/imap/mboxlist.c	2003-01-08 19:40:17.000000000 +0200
+++ cyrus-imapd-2.1.12.autocreate/imap/mboxlist.c	2003-04-21 21:18:18.000000000 +0300
@@ -2724,3 +2724,147 @@
     mboxlist_closesubs(subs);
     return r;
 }
+
+#define SEP '|'
+
+int mboxlist_autocreateinbox(struct namespace *namespace,
+                        char *userid,
+                        struct auth_state *auth_state,
+                        char *mailboxname, int autocreatequota) {
+    char name [MAX_MAILBOX_NAME+1];
+    char folder [MAX_MAILBOX_NAME+1];
+    char *partition = NULL;
+    const char *crt ;
+    const char *sub ;
+    char *p, *q, *next_crt, *next_sub;
+    int len;
+    int r = 0;
+    int numcrt = 0;
+    int numsub = 0;
+
+#if 0
+        /*
+         * Get Partition info or return.
+         * (Here you should propably use
+         * you own "get_partition(char *userid)"
+         * function. Otherwise all new INBOXes will be
+         * created into whatever partition has been declared
+         * as default in your imapd.conf)
+         */
+
+        partition = get_partition(userid);
+
+        if (partition == NULL) {
+            /*
+             * Couldnt get partition info 
+             */
+            syslog(LOG_WARNING,
+                   "Could not get imapPartition info for user %s", userid);
+            return IMAP_PARTITION_UNKNOWN;
+        }
+#endif
+
+     
+    r = mboxlist_createmailbox(mailboxname, MAILBOX_FORMAT_NORMAL, NULL,
+                                      1, userid, auth_state, 0, 0);
+
+    if (!r && autocreatequota > 0) 
+        r = mboxlist_setquota(mailboxname, autocreatequota, 0);
+
+    if (!r) 
+        r = mboxlist_changesub(mailboxname, userid,
+                              auth_state, 1, 1);
+
+    syslog(r == 0 ? LOG_NOTICE : LOG_ERR,
+          "User %s, INBOX autocreate in partition-%s: %s", userid,
+           partition == NULL ? "default" : partition,
+           r == 0 ? "succeeded" : error_message(r));
+
+    if (r) return r;
+
+    /* INBOX's subfolders */ 
+
+    if ((crt=config_getstring("autocreateinboxfolders", "")));
+        sub=config_getstring("autosubscribeinboxfolders", "");
+
+    /* Roll through crt */
+    next_crt = (char *)crt;
+    while (*next_crt) {
+          for (p = next_crt ; isspace((int) *p) || *p == SEP ; p++);
+	  for (next_crt = p ; *next_crt && *next_crt != SEP ; next_crt++);
+          for (q = next_crt ; q > p && (isspace((int) *q) || *q == SEP); q--);
+
+          if (!*p) continue;
+
+          len = q - p + 1;
+
+          /* First time we check for length */
+          if (len  > sizeof(folder) - 5)
+              r = IMAP_MAILBOX_BADNAME;
+
+          if (!r) {
+	          strncpy(folder, p, len);
+       	          folder[len] = '\0';
+
+                  strlcpy(name, namespace->prefix[NAMESPACE_INBOX], sizeof(name));
+                  len = strlcat(name, folder, sizeof(name));
+
+                  /* Second time we check length */
+		  /* 
+		   * The second check is not needed, because strlcat always
+		   * returns a number <= len. (christos)
+		   */
+		/*
+                  if (len > sizeof(name))
+                   	r = IMAP_MAILBOX_BADNAME;
+		*/
+          }
+          
+          if (!r)
+            r = (namespace->mboxname_tointernal) (namespace, name, userid,
+                                                 mailboxname);
+          if (!r)
+             r = mboxlist_createmailbox(mailboxname, MAILBOX_FORMAT_NORMAL, NULL,
+                                            1, userid, auth_state, 0, 0);
+          if (!r) numcrt++;
+
+          syslog(r == 0 ? LOG_DEBUG : LOG_ERR,
+                  "User %s, %s autocreate: %s", userid, mailboxname,
+                       r == 0 ? "succeeded" : error_message(r));
+          if (r) {
+             r=0;
+             continue;
+          }
+
+          /* Roll through sub */
+          next_sub = (char *)sub; 
+          while (*next_sub) {
+                for (p = next_sub ; isspace((int) *p) || *p == SEP ; p++);
+                for (next_sub = p ; *next_sub && *next_sub != SEP ; next_sub++);
+                for (q = next_sub ; q > p && (isspace((int) *q) || *q == SEP) ; q--);
+                if (!*p ) continue;
+      
+                len = q - p + 1;
+      
+                if (strncmp(folder, p, len))
+                    continue;
+
+                r = mboxlist_changesub(mailboxname, userid, auth_state, 1, 1);
+        
+                if (!r) numsub++;
+          
+                syslog(r == 0 ? LOG_DEBUG : LOG_ERR,
+                       "User %s, %s subscribe: %s", userid, mailboxname,
+                          r == 0 ? "succeeded" : error_message(r));
+        
+                 break;
+            }
+    }
+
+    if (*crt) 
+    syslog(LOG_INFO,
+           "User %s, Inbox subfolders, created %d, subscribed %d", userid, 
+                 numcrt,numsub);
+
+    return r;
+}
diff -Naur cyrus-imapd-2.1.12/imap/mboxlist.h cyrus-imapd-2.1.12.autocreate/imap/mboxlist.h
--- cyrus-imapd-2.1.12/imap/mboxlist.h	2002-05-29 19:49:16.000000000 +0300
+++ cyrus-imapd-2.1.12.autocreate/imap/mboxlist.h	2003-04-21 21:18:18.000000000 +0300
@@ -189,4 +189,9 @@
 /* done with database stuff */
 void mboxlist_done(void);
 
+int mboxlist_autocreateinbox(struct namespace *namespace,
+			char *userid,
+			struct auth_state *auth_state, 
+                        char *mailboxname, int autocreatequota);
+
 #endif
diff -Naur cyrus-imapd-2.1.12/imap/pop3d.c cyrus-imapd-2.1.12.autocreate/imap/pop3d.c
--- cyrus-imapd-2.1.12/imap/pop3d.c	2003-01-10 23:53:08.000000000 +0200
+++ cyrus-imapd-2.1.12.autocreate/imap/pop3d.c	2003-04-21 21:18:18.000000000 +0300
@@ -138,6 +138,8 @@
 static int apop_enabled(void);
 static char popd_apop_chal[45 + MAXHOSTNAMELEN + 1]; /* <rand.time at hostname> */
 
+static int autocreate_inbox(char *inboxname);
+
 static void cmd_auth();
 static void cmd_capa();
 static void cmd_pass();
@@ -1316,6 +1318,44 @@
     }
 }
 
+
+
+/*
+ * Autocreate Inbox and subfolders upon login
+ */
+int autocreate_inbox(char *inboxname)
+{
+    struct auth_state *authstate;
+    int userisadmin ;
+    int autocreatequota;
+    int r;
+
+    /*
+     * Exclude anonymous
+     */
+    if (!strcmp(popd_userid, "anonymous"))
+        return IMAP_MAILBOX_NONEXISTENT;
+
+    /*
+     * Check for autocreatequota
+     */
+    if (!(autocreatequota = config_getint("autocreatequota", 0))) 
+        return IMAP_MAILBOX_NONEXISTENT;
+
+    /*
+     * Exclude admin's accounts
+     */
+     authstate = auth_newstate(popd_userid, NULL);
+     userisadmin = authisa(authstate, "imap", "admins");    
+     if (userisadmin)
+          return IMAP_MAILBOX_NONEXISTENT;
+
+     r = mboxlist_autocreateinbox(&popd_namespace, popd_userid,
+                         authstate, inboxname, autocreatequota);
+     return r;
+}
+
+
 /*
  * Complete the login process by opening and locking the user's inbox
  */
@@ -1334,6 +1374,14 @@
     strcpy(inboxname, "user.");
     strcat(inboxname, popd_userid);
     r = mailbox_open_header(inboxname, 0, &mboxstruct);
+
+
+
+    /* Try once again after autocreate_inbox */
+    if (r == IMAP_MAILBOX_NONEXISTENT &&
+        !(r = autocreate_inbox(inboxname)))
+        r = mailbox_open_header(inboxname, 0, &mboxstruct);
+
     if (r) {
 	free(popd_userid);
 	popd_userid = 0;
diff -Naur cyrus-imapd-2.1.12/man/imapd.conf.5 cyrus-imapd-2.1.12.autocreate/man/imapd.conf.5
--- cyrus-imapd-2.1.12/man/imapd.conf.5	2002-11-20 06:15:22.000000000 +0200
+++ cyrus-imapd-2.1.12.autocreate/man/imapd.conf.5	2003-04-24 08:25:13.000000000 +0300
@@ -190,6 +190,20 @@
 If nonzero, normal users may create their own IMAP accounts by
 creating the mailbox INBOX.  The user's quota is set to the value if
 it is positive, otherwise the user has unlimited quota.
+.IP "\fBcreateonpost:\fR no" 5
+If yes, when lmtpd receives an incoming mail for an INBOX that does not exist,
+then the INBOX is automatically created by lmtpd.
+.IP "\fBautocreateinboxfolders:\fR <none>" 5
+If a user does not have an INBOX created then the INBOX as well as some INBOX subfolders are
+created under two conditions.
+ 1. The user logins via the IMAP or the POP3 protocol. (autocreatequota option must have a nonzero value)
+ 2. A message arrives for the user through the LMTPD protocol.(createonpost option must yes)
+ autocreateinboxfolders is a list of INBOX's subfolders separated by a "|", that are automatically created by the server
+under the previous two situations.
+.IP "\fBautosubscribeinboxfolders:\fR <none>" 5
+A list of folder names, separated by "|" that the users get automatically subscribed to, when their INBOX
+is created.
+These folder names must have been included in the autocreateinboxfolders option of the imapd.conf.
 .IP "\fBlogtimestamps:\fR no" 5
 Include notations in the protocol telemetry logs indicating the number of
 seconds since the last command or response.


More information about the Info-cyrus mailing list