stable now but there are GIANT PIECES MISSING
[citadel.git] / citadel / modules / imap / serv_imap.c
index 4b3e146cef6f40ef696311400cf4627811fe8f28..1609f8ee5b3a819da4ddaa91ddb20288b44c51c7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * IMAP server for the Citadel system
  *
- * Copyright (C) 2000-2011 by Art Cancro and others.
+ * Copyright (C) 2000-2021 by Art Cancro and others.
  * This code is released under the terms of the GNU General Public License.
  *
  * WARNING: the IMAP protocol is badly designed.  No implementation of it
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include "sysdep.h"
 #include <pwd.h>
 #include <errno.h>
 #include <sys/types.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
+#include <time.h>
 #include <sys/wait.h>
 #include <ctype.h>
 #include <string.h>
@@ -69,7 +54,6 @@
 #include "imap_misc.h"
 
 #include "ctdl_module.h"
-int IMAPDebugEnabled = 0;
 HashList *ImapCmds = NULL;
 void registerImapCMD(const char *First, long FLen, 
                     const char *Second, long SLen,
@@ -95,10 +79,6 @@ void registerImapCMD(const char *First, long FLen,
        }
 }
 
-void imap_cleanup(void)
-{
-       DeleteHash(&ImapCmds);
-}
 
 const imap_handler_hook *imap_lookup(int num_parms, ConstStr *Params)
 {
@@ -113,33 +93,31 @@ const imap_handler_hook *imap_lookup(int num_parms, ConstStr *Params)
        StrBufPlain(Imap->Reply, CKEY(Params[1]));
        StrBufUpCase(Imap->Reply);
 
-       IMAP_syslog(LOG_DEBUG, "---- Looking up [%s] -----", 
-                     ChrPtr(Imap->Reply));
+       syslog(LOG_DEBUG, "---- Looking up [%s] -----", ChrPtr(Imap->Reply));
        if (GetHash(ImapCmds, SKEY(Imap->Reply), &v))
        {
-               IMAPM_syslog(LOG_DEBUG, "Found."); 
+               syslog(LOG_DEBUG, "Found."); 
                FlushStrBuf(Imap->Reply);
                return (imap_handler_hook *) v;
        }
 
        if (num_parms == 1)
        {
-               IMAPM_syslog(LOG_DEBUG, "NOT Found."); 
+               syslog(LOG_DEBUG, "NOT Found."); 
                FlushStrBuf(Imap->Reply);
                return NULL;
        }
        
-       IMAP_syslog(LOG_DEBUG, "---- Looking up [%s] -----", 
-                     ChrPtr(Imap->Reply));
+       syslog(LOG_DEBUG, "---- Looking up [%s] -----", ChrPtr(Imap->Reply));
        StrBufAppendBufPlain(Imap->Reply, CKEY(Params[2]), 0);
        StrBufUpCase(Imap->Reply);
        if (GetHash(ImapCmds, SKEY(Imap->Reply), &v))
        {
-               IMAPM_syslog(LOG_DEBUG, "Found."); 
+               syslog(LOG_DEBUG, "Found."); 
                FlushStrBuf(Imap->Reply);
                return (imap_handler_hook *) v;
        }
-       IMAPM_syslog(LOG_DEBUG, "NOT Found."); 
+       syslog(LOG_DEBUG, "NOT Found."); 
        FlushStrBuf(Imap->Reply);
                return NULL;
 }
@@ -311,7 +289,7 @@ void imap_load_msgids(void)
        citimap *Imap = CCCIMAP;
 
        if (Imap->selected == 0) {
-               IMAPM_syslog(LOG_ERR, "imap_load_msgids() can't run; no room selected");
+               syslog(LOG_ERR, "imap_load_msgids() can't run; no room selected");
                return;
        }
 
@@ -354,7 +332,7 @@ void imap_rescan_msgids(void)
        int num_recent = 0;
 
        if (Imap->selected == 0) {
-               IMAPM_syslog(LOG_ERR, "imap_load_msgids() can't run; no room selected");
+               syslog(LOG_ERR, "imap_load_msgids() can't run; no room selected");
                return;
        }
 
@@ -486,7 +464,7 @@ void imap_cleanup_function(void)
                imap_do_expunge();
        }
 
-       IMAPM_syslog(LOG_DEBUG, "Performing IMAP cleanup hook");
+       syslog(LOG_DEBUG, "Performing IMAP cleanup hook");
        imap_free_msgids();
        imap_free_transmitted_message();
 
@@ -506,7 +484,7 @@ void imap_cleanup_function(void)
        FreeStrBuf(&Imap->Reply);
        if (Imap->Cmd.Params != NULL) free(Imap->Cmd.Params);
        free(Imap);
-       IMAPM_syslog(LOG_DEBUG, "Finished IMAP cleanup hook");
+       syslog(LOG_DEBUG, "Finished IMAP cleanup hook");
 }
 
 
@@ -599,7 +577,7 @@ void imap_greeting(void)
 
        IAPuts("* OK [");
        imap_output_capability_string();
-       IAPrintf("] %s IMAP4rev1 %s ready\r\n", config.c_fqdn, CITADEL);
+       IAPrintf("] %s IMAP4rev1 %s ready\r\n", CtdlGetConfigStr("c_fqdn"), CITADEL);
        IUnbuffer();
 }
 
@@ -635,7 +613,7 @@ void imap_login(int num_parms, ConstStr *Params)
                        return;
                }
        case 4:
-               if (CtdlLoginExistingUser(NULL, Params[2].Key) == login_ok) {
+               if (CtdlLoginExistingUser(Params[2].Key) == login_ok) {
                        if (CtdlTryPassword(Params[3].Key, Params[3].len) == pass_ok) {
                                /* hm, thats not doable by IReply :-( */
                                IAPrintf("%s OK [", Params[0].Key);
@@ -645,12 +623,13 @@ void imap_login(int num_parms, ConstStr *Params)
                        }
                        else
                        {
-                               IReplyPrintf("NO AUTHENTICATE %s failed",
-                                            Params[3].Key);
+                               IReplyPrintf("NO AUTHENTICATE %s failed", Params[3].Key);
+                               return;
                        }
                }
 
                IReply("BAD Login incorrect");
+               return;
        default:
                IReply("BAD incorrect number of parameters");
                return;
@@ -677,7 +656,11 @@ void imap_authenticate(int num_parms, ConstStr *Params)
        }
 
        if (!strcasecmp(Params[2].Key, "LOGIN")) {
-               CtdlEncodeBase64(UsrBuf, "Username:", 9, 0);
+               size_t len = CtdlEncodeBase64(UsrBuf, "Username:", 9, 0);
+               if (UsrBuf[len - 1] == '\n') {
+                       UsrBuf[len - 1] = '\0';
+               }
+
                IAPrintf("+ %s\r\n", UsrBuf);
                IMAP->authstate = imap_as_expecting_username;
                strcpy(IMAP->authseq, Params[0].Key);
@@ -685,7 +668,10 @@ void imap_authenticate(int num_parms, ConstStr *Params)
        }
 
        if (!strcasecmp(Params[2].Key, "PLAIN")) {
-               // CtdlEncodeBase64(UsrBuf, "Username:", 9, 0);
+               // size_t len = CtdlEncodeBase64(UsrBuf, "Username:", 9, 0);
+               // if (UsrBuf[len - 1] == '\n') {
+               //   UsrBuf[len - 1] = '\0';
+               // }
                // IAPuts("+ %s\r\n", UsrBuf);
                IAPuts("+ \r\n");
                IMAP->authstate = imap_as_expecting_plainauth;
@@ -708,38 +694,49 @@ void imap_auth_plain(void)
        char user[256] = "";
        char pass[256] = "";
        int result;
-       long len;
+       long decoded_len;
+       long len = 0;
+       long plen = 0;
 
        memset(pass, 0, sizeof(pass));
-       len = StrBufDecodeBase64(Imap->Cmd.CmdBuf);
+       decoded_len = StrBufDecodeBase64(Imap->Cmd.CmdBuf);
 
-       if (len > 0)
+       if (decoded_len > 0)
        {
                decoded_authstring = ChrPtr(Imap->Cmd.CmdBuf);
 
                len = safestrncpy(ident, decoded_authstring, sizeof ident);
 
+               decoded_len -= len - 1;
                decoded_authstring += len + 1;
 
-               len = safestrncpy(user, decoded_authstring, sizeof user);
+               if (decoded_len > 0)
+               {
+                       len = safestrncpy(user, decoded_authstring, sizeof user);
 
-               decoded_authstring += len + 1;
+                       decoded_authstring += len + 1;
+                       decoded_len -= len - 1;
+               }
 
-               len = safestrncpy(pass, decoded_authstring, sizeof pass);
-               if (len < 0)
-                       len = sizeof(pass) - 1;
+               if (decoded_len > 0)
+               {
+                       plen = safestrncpy(pass, decoded_authstring, sizeof pass);
+
+                       if (plen < 0)
+                               plen = sizeof(pass) - 1;
+               }
        }
        Imap->authstate = imap_as_normal;
 
        if (!IsEmptyStr(ident)) {
-               result = CtdlLoginExistingUser(user, ident);
+               result = CtdlLoginExistingUser(ident);
        }
        else {
-               result = CtdlLoginExistingUser(NULL, user);
+               result = CtdlLoginExistingUser(user);
        }
 
        if (result == login_ok) {
-               if (CtdlTryPassword(pass, len) == pass_ok) {
+               if (CtdlTryPassword(pass, plen) == pass_ok) {
                        IAPrintf("%s OK authentication succeeded\r\n", Imap->authseq);
                        return;
                }
@@ -756,15 +753,19 @@ void imap_auth_login_user(long state)
        switch (state){
        case imap_as_expecting_username:
                StrBufDecodeBase64(Imap->Cmd.CmdBuf);
-               CtdlLoginExistingUser(NULL, ChrPtr(Imap->Cmd.CmdBuf));
-               CtdlEncodeBase64(PWBuf, "Password:", 9, 0);
+               CtdlLoginExistingUser(ChrPtr(Imap->Cmd.CmdBuf));
+               size_t len = CtdlEncodeBase64(PWBuf, "Password:", 9, 0);
+               if (PWBuf[len - 1] == '\n') {
+                       PWBuf[len - 1] = '\0';
+               }
+
                IAPrintf("+ %s\r\n", PWBuf);
                
                Imap->authstate = imap_as_expecting_password;
                return;
        case imap_as_expecting_multilineusername:
                extract_token(PWBuf, ChrPtr(Imap->Cmd.CmdBuf), 1, ' ', sizeof(PWBuf));
-               CtdlLoginExistingUser(NULL, ChrPtr(Imap->Cmd.CmdBuf));
+               CtdlLoginExistingUser(ChrPtr(Imap->Cmd.CmdBuf));
                IAPuts("+ go ahead\r\n");
                Imap->authstate = imap_as_expecting_multilinepassword;
                return;
@@ -879,7 +880,7 @@ void imap_select(int num_parms, ConstStr *Params)
         * the number of messages and number of new messages.
         */
        memcpy(&CC->room, &QRscratch, sizeof(struct ctdlroom));
-       CtdlUserGoto(NULL, 0, 0, &msgs, &new);
+       CtdlUserGoto(NULL, 0, 0, &msgs, &new, NULL, NULL);
        Imap->selected = 1;
 
        if (!strcasecmp(Params[1].Key, "EXAMINE")) {
@@ -895,7 +896,7 @@ void imap_select(int num_parms, ConstStr *Params)
        IAPrintf("* %d RECENT\r\n", new);
 
        IAPrintf("* OK [UIDVALIDITY %ld] UID validity status\r\n", GLOBAL_UIDVALIDITY_VALUE);
-       IAPrintf("* OK [UIDNEXT %ld] Predicted next UID\r\n", CitControl.MMhighest + 1);
+       IAPrintf("* OK [UIDNEXT %ld] Predicted next UID\r\n", CtdlGetConfigLong("MMhighest") + 1);
 
        /* Technically, \Deleted is a valid flag, but not a permanent flag,
         * because we don't maintain its state across sessions.  Citadel
@@ -927,7 +928,7 @@ int imap_do_expunge(void)
        long *delmsgs = NULL;
        int num_delmsgs = 0;
 
-       IMAPM_syslog(LOG_DEBUG, "imap_do_expunge() called");
+       syslog(LOG_DEBUG, "imap_do_expunge() called");
        if (Imap->selected == 0) {
                return (0);
        }
@@ -950,7 +951,7 @@ int imap_do_expunge(void)
                imap_rescan_msgids();
        }
 
-       IMAP_syslog(LOG_DEBUG, "Expunged %d messages from <%s>", num_expunged, CC->room.QRname);
+       syslog(LOG_DEBUG, "Expunged %d messages from <%s>", num_expunged, CC->room.QRname);
        return (num_expunged);
 }
 
@@ -1031,7 +1032,6 @@ void imap_namespace(int num_parms, ConstStr *Params)
  */
 void imap_create(int num_parms, ConstStr *Params)
 {
-       struct CitContext *CCC = CC;
        int ret;
        char roomname[ROOMNAMELEN];
        int floornum;
@@ -1047,14 +1047,14 @@ void imap_create(int num_parms, ConstStr *Params)
 
        if (strchr(Params[2].Key, '\\') != NULL) {
                IReply("NO Invalid character in folder name");
-               IMAPM_syslog(LOG_ERR, "invalid character in folder name");
+               syslog(LOG_ERR, "invalid character in folder name");
                return;
        }
 
        ret = imap_roomname(roomname, sizeof roomname, Params[2].Key);
        if (ret < 0) {
                IReply("NO Invalid mailbox name or location");
-               IMAPM_syslog(LOG_ERR, "invalid mailbox name or location");
+               syslog(LOG_ERR, "invalid mailbox name or location");
                return;
        }
        floornum = (ret & 0x00ff);      /* lower 8 bits = floor number */
@@ -1063,7 +1063,7 @@ void imap_create(int num_parms, ConstStr *Params)
        if (flags & IR_MAILBOX) {
                if (strncasecmp(Params[2].Key, "INBOX/", 6)) {
                        IReply("NO Personal folders must be created under INBOX");
-                       IMAPM_syslog(LOG_ERR, "not subordinate to inbox");
+                       syslog(LOG_ERR, "not subordinate to inbox");
                        return;
                }
        }
@@ -1076,7 +1076,7 @@ void imap_create(int num_parms, ConstStr *Params)
                newroomview = VIEW_BBS;
        }
 
-       IMAP_syslog(LOG_INFO, "Create new room <%s> on floor <%d> with type <%d>",
+       syslog(LOG_INFO, "Create new room <%s> on floor <%d> with type <%d>",
                    roomname, floornum, newroomtype);
 
        ret = CtdlCreateRoom(roomname, newroomtype, "", floornum, 1, 0, newroomview);
@@ -1098,7 +1098,7 @@ void imap_create(int num_parms, ConstStr *Params)
                CtdlAideMessage(notification_message, "Room Creation Message");
                free(notification_message);
        }
-       IMAPM_syslog(LOG_DEBUG, "imap_create() completed");
+       syslog(LOG_DEBUG, "imap_create() completed");
 }
 
 
@@ -1185,7 +1185,7 @@ void imap_status(int num_parms, ConstStr *Params)
        if (IMAP->selected) {
                strcpy(savedroom, CC->room.QRname);
        }
-       CtdlUserGoto(roomname, 0, 0, &msgs, &new);
+       CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);
 
        /*
         * Tell the client what it wants to know.  In fact, tell it *more* than
@@ -1198,7 +1198,7 @@ void imap_status(int num_parms, ConstStr *Params)
        IPutStr(imaproomname, len);
        IAPrintf(" (MESSAGES %d ", msgs);
        IAPrintf("RECENT %d ", new);    /* Initially, new==recent */
-       IAPrintf("UIDNEXT %ld ", CitControl.MMhighest + 1);
+       IAPrintf("UIDNEXT %ld ", CtdlGetConfigLong("MMhighest") + 1);
        IAPrintf("UNSEEN %d)\r\n", new);
        
        /*
@@ -1206,7 +1206,7 @@ void imap_status(int num_parms, ConstStr *Params)
         * our happy day without violent explosions.
         */
        if (IMAP->selected) {
-               CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
+               CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
        }
 
        /*
@@ -1244,14 +1244,14 @@ void imap_subscribe(int num_parms, ConstStr *Params)
        if (IMAP->selected) {
                strcpy(savedroom, CC->room.QRname);
        }
-       CtdlUserGoto(roomname, 0, 0, &msgs, &new);
+       CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);
 
        /*
         * If another folder is selected, go back to that room so we can resume
         * our happy day without violent explosions.
         */
        if (IMAP->selected) {
-               CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
+               CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
        }
 
        IReply("OK SUBSCRIBE completed");
@@ -1281,7 +1281,7 @@ void imap_unsubscribe(int num_parms, ConstStr *Params)
        if (IMAP->selected) {
                strcpy(savedroom, CC->room.QRname);
        }
-       CtdlUserGoto(roomname, 0, 0, &msgs, &new);
+       CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);
 
        /* 
         * Now make the API call to zap the room
@@ -1297,7 +1297,7 @@ void imap_unsubscribe(int num_parms, ConstStr *Params)
         * our happy day without violent explosions.
         */
        if (IMAP->selected) {
-               CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
+               CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
        }
 }
 
@@ -1327,7 +1327,7 @@ void imap_delete(int num_parms, ConstStr *Params)
        if (IMAP->selected) {
                strcpy(savedroom, CC->room.QRname);
        }
-       CtdlUserGoto(roomname, 0, 0, &msgs, &new);
+       CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);
 
        /*
         * Now delete the room.
@@ -1344,7 +1344,7 @@ void imap_delete(int num_parms, ConstStr *Params)
         * our happy day without violent explosions.
         */
        if (IMAP->selected) {
-               CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
+               CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
        }
 }
 
@@ -1463,9 +1463,8 @@ void imap_rename(int num_parms, ConstStr *Params)
                                           irl->irl_newroom,
                                           irl->irl_newfloor);
                        if (r != crr_ok) {
-                               struct CitContext *CCC = CC;
                                /* FIXME handle error returns better */
-                               IMAP_syslog(LOG_ERR, "CtdlRenameRoom() error %d", r);
+                               syslog(LOG_ERR, "CtdlRenameRoom() error %d", r);
                        }
                        irlp = irl;
                        irl = irl->next;
@@ -1507,23 +1506,23 @@ void imap_command_loop(void)
                FlushStrBuf(Imap->Cmd.CmdBuf);
 
        if (CtdlClientGetLine(Imap->Cmd.CmdBuf) < 1) {
-               IMAPM_syslog(LOG_ERR, "client disconnected: ending session.");
+               syslog(LOG_ERR, "client disconnected: ending session.");
                CC->kill_me = KILLME_CLIENT_DISCONNECTED;
                return;
        }
 
        if (Imap->authstate == imap_as_expecting_password) {
-               IMAPM_syslog(LOG_INFO, "<password>");
+               syslog(LOG_INFO, "<password>");
        }
        else if (Imap->authstate == imap_as_expecting_plainauth) {
-               IMAPM_syslog(LOG_INFO, "<plain_auth>");
+               syslog(LOG_INFO, "<plain_auth>");
        }
        else if ((Imap->authstate == imap_as_expecting_multilineusername) || 
                 cbmstrcasestr(ChrPtr(Imap->Cmd.CmdBuf), " LOGIN ")) {
-               IMAPM_syslog(LOG_INFO, "LOGIN...");
+               syslog(LOG_INFO, "LOGIN...");
        }
        else {
-               IMAP_syslog(LOG_DEBUG, "%s", ChrPtr(Imap->Cmd.CmdBuf));
+               syslog(LOG_DEBUG, "%s", ChrPtr(Imap->Cmd.CmdBuf));
        }
 
        pchs = ChrPtr(Imap->Cmd.CmdBuf);
@@ -1575,16 +1574,16 @@ void imap_command_loop(void)
 /* debug output the parsed vector */
        {
                int i;
-               IMAP_syslog(LOG_DEBUG, "----- %ld params", Imap->Cmd.num_parms);
+               syslog(LOG_DEBUG, "----- %ld params", Imap->Cmd.num_parms);
 
        for (i=0; i < Imap->Cmd.num_parms; i++) {
                if (Imap->Cmd.Params[i].len != strlen(Imap->Cmd.Params[i].Key))
-                       IMAP_syslog(LOG_DEBUG, "*********** %ld != %ld : %s",
+                       syslog(LOG_DEBUG, "*********** %ld != %ld : %s",
                                    Imap->Cmd.Params[i].len, 
                                    strlen(Imap->Cmd.Params[i].Key),
                                      Imap->Cmd.Params[i].Key);
                else
-                       IMAP_syslog(LOG_DEBUG, "%ld : %s",
+                       syslog(LOG_DEBUG, "%ld : %s",
                                    Imap->Cmd.Params[i].len, 
                                    Imap->Cmd.Params[i].Key);
        }}
@@ -1637,7 +1636,7 @@ BAIL:
 
        gettimeofday(&tv2, NULL);
        total_time = (tv2.tv_usec + (tv2.tv_sec * 1000000)) - (tv1.tv_usec + (tv1.tv_sec * 1000000));
-       IMAP_syslog(LOG_DEBUG, "IMAP command completed in %ld.%ld seconds",
+       syslog(LOG_DEBUG, "IMAP command completed in %ld.%ld seconds",
                    (total_time / 1000000),
                    (total_time % 1000000)
                );
@@ -1653,7 +1652,7 @@ void imap_logout(int num_parms, ConstStr *Params)
        if (IMAP->selected) {
                imap_do_expunge();      /* yes, we auto-expunge at logout */
        }
-       IAPrintf("* BYE %s logging out\r\n", config.c_fqdn);
+       IAPrintf("* BYE %s logging out\r\n", CtdlGetConfigStr("c_fqdn"));
        IReply("OK Citadel IMAP session ended.");
        CC->kill_me = KILLME_CLIENT_LOGGED_OUT;
        return;
@@ -1662,10 +1661,6 @@ void imap_logout(int num_parms, ConstStr *Params)
 const char *CitadelServiceIMAP="IMAP";
 const char *CitadelServiceIMAPS="IMAPS";
 
-void SetIMAPDebugEnabled(const int n)
-{
-       IMAPDebugEnabled = n;
-}
 /*
  * This function is called to register the IMAP extension with Citadel.
  */
@@ -1721,15 +1716,13 @@ CTDL_MODULE_INIT(imap)
 
        if (!threading)
        {
-               CtdlRegisterDebugFlagHook(HKEY("imapsrv"), SetIMAPDebugEnabled, &IMAPDebugEnabled);
-               CtdlRegisterServiceHook(config.c_imap_port,
+               CtdlRegisterServiceHook(CtdlGetConfigInt("c_imap_port"),
                                        NULL, imap_greeting, imap_command_loop, NULL, CitadelServiceIMAP);
 #ifdef HAVE_OPENSSL
-               CtdlRegisterServiceHook(config.c_imaps_port,
+               CtdlRegisterServiceHook(CtdlGetConfigInt("c_imaps_port"),
                                        NULL, imaps_greeting, imap_command_loop, NULL, CitadelServiceIMAPS);
 #endif
                CtdlRegisterSessionHook(imap_cleanup_function, EVT_STOP, PRIO_STOP + 30);
-               CtdlRegisterCleanupHook(imap_cleanup);
        }
        
        /* return our module name for the log */