Continue phase 2 of modules stuff.
authorDave West <davew@uncensored.citadel.org>
Fri, 3 Aug 2007 22:12:16 +0000 (22:12 +0000)
committerDave West <davew@uncensored.citadel.org>
Fri, 3 Aug 2007 22:12:16 +0000 (22:12 +0000)
Moved some more stuff into the relevant modules/* directories.
modified Makefile.in to build them.
Created a new Hook to register a maintenance thread. This allowed serv_fulltext.c to become independant of one file.
New thread hook should be easy enough to extend to other threads and get stats of threads.
Still more to be done on this to seperate the rest of the modules so they don't rely on specifics of each other.
Moved db checkpoint thread to new hook mechanism.
Remind me to document that mechanism 8-)

57 files changed:
citadel/Makefile.in
citadel/citserver.c
citadel/database_sleepycat.c
citadel/imap_acl.c [deleted file]
citadel/imap_acl.h [deleted file]
citadel/imap_fetch.c [deleted file]
citadel/imap_fetch.h [deleted file]
citadel/imap_list.c [deleted file]
citadel/imap_list.h [deleted file]
citadel/imap_metadata.c [deleted file]
citadel/imap_metadata.h [deleted file]
citadel/imap_misc.c [deleted file]
citadel/imap_misc.h [deleted file]
citadel/imap_search.c [deleted file]
citadel/imap_search.h [deleted file]
citadel/imap_store.c [deleted file]
citadel/imap_store.h [deleted file]
citadel/imap_tools.c [deleted file]
citadel/include/ctdl_module.h
citadel/journaling.c
citadel/modules/crypto/serv_crypto.h [new file with mode: 0644]
citadel/modules/expire/serv_expire.c
citadel/modules/fulltext/serv_fulltext.c
citadel/modules/imap/imap_acl.c [new file with mode: 0644]
citadel/modules/imap/imap_acl.h [new file with mode: 0644]
citadel/modules/imap/imap_fetch.c [new file with mode: 0644]
citadel/modules/imap/imap_fetch.h [new file with mode: 0644]
citadel/modules/imap/imap_list.c [new file with mode: 0644]
citadel/modules/imap/imap_list.h [new file with mode: 0644]
citadel/modules/imap/imap_metadata.c [new file with mode: 0644]
citadel/modules/imap/imap_metadata.h [new file with mode: 0644]
citadel/modules/imap/imap_misc.c [new file with mode: 0644]
citadel/modules/imap/imap_misc.h [new file with mode: 0644]
citadel/modules/imap/imap_search.c [new file with mode: 0644]
citadel/modules/imap/imap_search.h [new file with mode: 0644]
citadel/modules/imap/imap_store.c [new file with mode: 0644]
citadel/modules/imap/imap_store.h [new file with mode: 0644]
citadel/modules/imap/imap_tools.c [new file with mode: 0644]
citadel/modules/imap/serv_imap.c [new file with mode: 0644]
citadel/modules/imap/serv_imap.h [new file with mode: 0644]
citadel/modules/ldap/serv_ldap.h [new file with mode: 0644]
citadel/modules/listsub/serv_listsub.c
citadel/modules/managesieve/serv_managesieve.c
citadel/modules/netfilter/serv_netfilter.c
citadel/modules/pop3/serv_pop3.c
citadel/modules/smtp/serv_smtp.c
citadel/modules/vcard/serv_vcard.c
citadel/msgbase.c
citadel/serv_crypto.h [deleted file]
citadel/serv_extensions.c
citadel/serv_imap.c [deleted file]
citadel/serv_imap.h [deleted file]
citadel/serv_ldap.h [deleted file]
citadel/server.h
citadel/server_main.c
citadel/sysdep.c
citadel/sysdep_decls.h

index f2c63032e8d5447954a18457356cf75d0210bfc8..3c4e6511a94e748f5d172a93573a6d2c739bce53 100644 (file)
@@ -36,15 +36,15 @@ SERV_MODULES=modules/chat/serv_chat.o \
        modules/vcard/serv_vcard.o \
        vcard.o \
        modules/mrtg/serv_mrtg.o \
-       serv_imap.o \
-       imap_fetch.o \
-       imap_misc.o \
-       imap_search.o \
-       imap_store.o \
-       imap_acl.o \
-       imap_metadata.o \
-       imap_tools.o \
-       imap_list.o \
+       modules/imap/serv_imap.o \
+       modules/imap/imap_fetch.o \
+       modules/imap/imap_misc.o \
+       modules/imap/imap_search.o \
+       modules/imap/imap_store.o \
+       modules/imap/imap_acl.o \
+       modules/imap/imap_metadata.o \
+       modules/imap/imap_tools.o \
+       modules/imap/imap_list.o \
        modules/fulltext/serv_fulltext.o \
        modules/fulltext/ft_wordbreaker.o \
        crc16.o \
@@ -107,8 +107,8 @@ SOURCES=aidepost.c auth.c base64.c chkpwd.c chkpw.c citadel.c citadel_ipc.c \
        citmail.c citserver.c client_chat.c client_passwords.c \
        clientsocket.c commands.c config.c control.c $(DATABASE) \
        domain.c serv_extensions.c file_ops.c genstamp.c getutline.c \
-       housekeeping.c html.c ical_dezonify.c imap_fetch.c imap_misc.c \
-       imap_search.c imap_store.c imap_tools.c internet_addressing.c \
+       housekeeping.c html.c ical_dezonify.c \
+       internet_addressing.c \
        ipc_c_tcp.c locate_host.c md5.c messages.c  \
        modules/autocompletion/serv_autocompletion.c \
        mime_parser.c msgbase.c msgform.c parsedate.c policy.c \
@@ -119,7 +119,15 @@ SOURCES=aidepost.c auth.c base64.c chkpwd.c chkpw.c citadel.c citadel_ipc.c \
        modules/chat/serv_chat.c \
        modules/crypto/serv_crypto.c \
        modules/expire/serv_expire.c \
-       serv_imap.c \
+       modules/imap/serv_imap.c \
+       modules/imap/imap_search.c \
+       modules/imap/imap_store.c \
+       modules/imap/imap_tools.c \
+       modules/imap/imap_fetch.c \
+       modules/imap/imap_misc.c \
+       modules/imap/imap_acl.c \
+       modules/imap/imap_list.c \
+       modules/imap/imap_metadata.c \
        modules/inetcfg/serv_inetcfg.c \
        modules/listsub/serv_listsub.c \
        modules/mrtg/serv_mrtg.c \
@@ -139,13 +147,13 @@ SOURCES=aidepost.c auth.c base64.c chkpwd.c chkpw.c citadel.c citadel_ipc.c \
        server_main.c \
        modules/sieve/serv_sieve.c \
        modules/funambol/serv_funambol.c \
-       setup.c snprintf.c imap_acl.c \
+       setup.c snprintf.c \
        stress.c support.c sysdep.c tools.c user_ops.c userlist.c \
        whobbs.c vcard.c \
        modules/notes/serv_notes.c \
        modules/fulltext/serv_fulltext.c \
        modules/fulltext/ft_wordbreaker.c \
-       crc16.c journaling.c citadel_dirs.c imap_list.c imap_metadata.c \
+       crc16.c journaling.c citadel_dirs.c \
        modules/test/serv_test.c
 
 DEP_FILES=$(SOURCES:.c=.d) modules_init.d
index 262cb2b324ba6d3ad0106845978f0097b82920e9..90a7f4f969005530ed4dcb2a323fc4251f45de30 100644 (file)
@@ -53,7 +53,7 @@
 #include "control.h"
 #include "tools.h"
 #include "euidindex.h"
-#include "serv_network.h"
+#include "serv_network.h"      /* Needed for destroy_network_queue_room called from master_cleanup */
 
 #ifndef HAVE_SNPRINTF
 #include "snprintf.h"
@@ -125,6 +125,7 @@ void master_startup(void) {
  */
 void master_cleanup(int exitcode) {
        struct CleanupFunctionHook *fcn;
+       struct MaintenanceThreadHook *m_fcn;
        static int already_cleaning_up = 0;
 
        if (already_cleaning_up) while(1) sleep(1);
@@ -138,13 +139,11 @@ void master_cleanup(int exitcode) {
        /* Close the AdjRefCount queue file */
        AdjRefCount(-1, 0);
 
-       /* Shut down the indexer thread */
-       lprintf(CTDL_INFO, "Waiting for the indexer thread to shut down\n");
-       pthread_join(indexer_thread_tid, NULL);
-
-       /* Shut down the checkpoint thread */
-       lprintf(CTDL_INFO, "Waiting for the checkpoint thread to shut down\n");
-       pthread_join(checkpoint_thread_tid, NULL);
+       for (m_fcn = MaintenanceThreadHookTable; m_fcn != NULL; m_fcn = m_fcn->next) {
+               lprintf(CTDL_INFO, "Waiting for maintenance thread \"%s\" to shut down\n", m_fcn->name);
+               pthread_join(m_fcn->MaintenanceThread_tid, NULL);
+       }
+       
 
        /* Close databases */
        lprintf(CTDL_INFO, "Closing databases\n");
index e01c0adadc3667bc812fa437e9c46f5f82ca4cef..bf11656884b409fd53fa3f8b7890594970938d8c 100644 (file)
@@ -451,6 +451,8 @@ void open_databases(void)
        }
 
        cdb_allocate_tsd();
+       
+       CtdlRegisterMaintenanceThread ("checkpoint", checkpoint_thread);
 }
 
 
diff --git a/citadel/imap_acl.c b/citadel/imap_acl.c
deleted file mode 100644 (file)
index 1632532..0000000
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * $Id$
- *
- * Functions which implement RFC2086 (and maybe RFC4314) (IMAP ACL extension)
- *
- */
-
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.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 <sys/wait.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-#include "citadel.h"
-#include "server.h"
-#include "sysdep_decls.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "room_ops.h"
-#include "user_ops.h"
-#include "policy.h"
-#include "database.h"
-#include "msgbase.h"
-#include "tools.h"
-#include "internet_addressing.h"
-#include "serv_imap.h"
-#include "imap_tools.h"
-#include "imap_fetch.h"
-#include "imap_misc.h"
-#include "genstamp.h"
-
-
-
-/*
- * Implements the SETACL command.
- */
-void imap_setacl(int num_parms, char *parms[]) {
-
-       cprintf("%s BAD not yet implemented FIXME\r\n", parms[0]);
-       return;
-}
-
-
-/*
- * Implements the DELETEACL command.
- */
-void imap_deleteacl(int num_parms, char *parms[]) {
-
-       cprintf("%s BAD not yet implemented FIXME\r\n", parms[0]);
-       return;
-}
-
-
-/*
- * Given the bits returned by CtdlRoomAccess(), populate a string buffer
- * with IMAP ACL format flags.   This code is common to GETACL and MYRIGHTS.
- */
-void imap_acl_flags(char *rights, int ra)
-{
-       strcpy(rights, "");
-
-       /* l - lookup (mailbox is visible to LIST/LSUB commands)
-        * r - read (SELECT the mailbox, perform STATUS et al)
-        * s - keep seen/unseen information across sessions (STORE SEEN flag)
-        */
-       if (    (ra & UA_KNOWN)                                 /* known rooms */
-          ||   ((ra & UA_GOTOALLOWED) && (ra & UA_ZAPPED))     /* zapped rooms */
-          ) {
-               strcat(rights, "l");
-               strcat(rights, "r");
-               strcat(rights, "s");
-
-               /* Only output the remaining flags if the room is known */
-
-               /* w - write (set or clear flags other than SEEN or DELETED, not supported in Citadel */
-
-               /* i - insert (perform APPEND, COPY into mailbox) */
-               /* p - post (send mail to submission address for mailbox - not enforced) */
-               /* c - create (CREATE new sub-mailboxes) */
-               if (ra & UA_POSTALLOWED) {
-                       strcat(rights, "i");
-                       strcat(rights, "p");
-                       strcat(rights, "c");
-               }
-
-               /* d - delete messages (STORE DELETED flag, perform EXPUNGE) */
-               if (ra & UA_DELETEALLOWED) {
-                       strcat(rights, "d");
-               }
-
-               /* a - administer (perform SETACL/DELETEACL/GETACL/LISTRIGHTS) */
-               if (ra & UA_ADMINALLOWED) {
-                       /*
-                        * This is the correct place to put the "a" flag.  We are leaving
-                        * it commented out for now, because it implies that we could
-                        * perform any of SETACL/DELETEACL/GETACL/LISTRIGHTS.  Since these
-                        * commands are not yet implemented, omitting the flag should
-                        * theoretically prevent compliant clients from attempting to
-                        * perform them.
-                        */
-                       /* strcat(rights, "a"); * commented out */
-               }
-       }
-}
-
-
-/*
- * Implements the GETACL command.
- */
-void imap_getacl(int num_parms, char *parms[]) {
-       char roomname[ROOMNAMELEN];
-       char savedroom[ROOMNAMELEN];
-       int msgs, new;
-       int ret;
-       struct ctdluser temp;
-       struct cdbdata *cdbus;
-       int ra;
-       char rights[32];
-
-       if (num_parms != 3) {
-               cprintf("%s BAD usage error\r\n", parms[0]);
-               return;
-       }
-
-       /*
-        * Search for the specified room or folder
-        */
-       ret = imap_grabroom(roomname, parms[2], 0);
-       if (ret != 0) {
-               cprintf("%s NO Invalid mailbox name or access denied\r\n",
-                       parms[0]);
-               return;
-       }
-
-       /*
-        * usergoto() formally takes us to the desired room.  (If another
-        * folder is selected, save its name so we can return there!!!!!)
-        */
-       if (IMAP->selected) {
-               strcpy(savedroom, CC->room.QRname);
-       }
-       usergoto(roomname, 0, 0, &msgs, &new);
-
-       cprintf("* ACL");
-       cprintf(" ");
-       imap_strout(parms[2]);
-
-       /*
-        * Traverse the userlist
-        */
-       cdb_rewind(CDB_USERS);
-       while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
-               memset(&temp, 0, sizeof temp);
-               memcpy(&temp, cdbus->ptr, sizeof temp);
-               cdb_free(cdbus);
-
-               CtdlRoomAccess(&CC->room, &temp, &ra, NULL);
-               if (strlen(temp.fullname) > 0) {
-                       imap_acl_flags(rights, ra);
-                       if (strlen(rights) > 0) {
-                               cprintf(" ");
-                               imap_strout(temp.fullname);
-                               cprintf(" %s", rights);
-                       }
-               }
-       }
-
-       cprintf("\r\n");
-
-       /*
-        * If another folder is selected, go back to that room so we can resume
-        * our happy day without violent explosions.
-        */
-       if (IMAP->selected) {
-               usergoto(savedroom, 0, 0, &msgs, &new);
-       }
-
-       cprintf("%s OK GETACL completed\r\n", parms[0]);
-}
-
-
-/*
- * Implements the LISTRIGHTS command.
- */
-void imap_listrights(int num_parms, char *parms[]) {
-       char roomname[ROOMNAMELEN];
-       char savedroom[ROOMNAMELEN];
-       int msgs, new;
-       int ret;
-       struct recptypes *valid;
-       struct ctdluser temp;
-
-       if (num_parms != 4) {
-               cprintf("%s BAD usage error\r\n", parms[0]);
-               return;
-       }
-
-       /*
-        * Search for the specified room/folder
-        */
-       ret = imap_grabroom(roomname, parms[2], 0);
-       if (ret != 0) {
-               cprintf("%s NO Invalid mailbox name or access denied\r\n",
-                       parms[0]);
-               return;
-       }
-
-       /*
-        * Search for the specified user
-        */
-       ret = (-1);
-       valid = validate_recipients(parms[3]);
-       if (valid != NULL) {
-               if (valid->num_local == 1) {
-                       ret = getuser(&temp, valid->recp_local);
-               }
-               free_recipients(valid);
-       }
-       if (ret != 0) {
-               cprintf("%s NO Invalid user name or access denied\r\n",
-                       parms[0]);
-               return;
-       }
-
-       /*
-        * usergoto() formally takes us to the desired room.  (If another
-        * folder is selected, save its name so we can return there!!!!!)
-        */
-       if (IMAP->selected) {
-               strcpy(savedroom, CC->room.QRname);
-       }
-       usergoto(roomname, 0, 0, &msgs, &new);
-
-
-       /*
-        * Now output the list of rights
-        */
-       cprintf("* LISTRIGHTS ");
-       imap_strout(parms[2]);
-       cprintf(" ");
-       imap_strout(parms[3]);
-       cprintf(" ");
-       imap_strout("");                /* FIXME ... do something here */
-       cprintf("\r\n");
-
-
-       /*
-        * If another folder is selected, go back to that room so we can resume
-        * our happy day without violent explosions.
-        */
-       if (IMAP->selected) {
-               usergoto(savedroom, 0, 0, &msgs, &new);
-       }
-
-       cprintf("%s OK LISTRIGHTS completed\r\n", parms[0]);
-       return;
-}
-
-
-/*
- * Implements the MYRIGHTS command.
- */
-void imap_myrights(int num_parms, char *parms[]) {
-       char roomname[ROOMNAMELEN];
-       char savedroom[ROOMNAMELEN];
-       int msgs, new;
-       int ret;
-       int ra;
-       char rights[32];
-
-       if (num_parms != 3) {
-               cprintf("%s BAD usage error\r\n", parms[0]);
-               return;
-       }
-
-       ret = imap_grabroom(roomname, parms[2], 0);
-       if (ret != 0) {
-               cprintf("%s NO Invalid mailbox name or access denied\r\n",
-                       parms[0]);
-               return;
-       }
-
-       /*
-        * usergoto() formally takes us to the desired room.  (If another
-        * folder is selected, save its name so we can return there!!!!!)
-        */
-       if (IMAP->selected) {
-               strcpy(savedroom, CC->room.QRname);
-       }
-       usergoto(roomname, 0, 0, &msgs, &new);
-
-       CtdlRoomAccess(&CC->room, &CC->user, &ra, NULL);
-       imap_acl_flags(rights, ra);
-
-       cprintf("* MYRIGHTS ");
-       imap_strout(parms[2]);
-       cprintf(" %s\r\n", rights);
-
-       /*
-        * If a different folder was previously selected, return there now.
-        */
-       if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
-               usergoto(savedroom, 0, 0, &msgs, &new);
-       }
-
-       cprintf("%s OK MYRIGHTS completed\r\n", parms[0]);
-       return;
-}
-
-
diff --git a/citadel/imap_acl.h b/citadel/imap_acl.h
deleted file mode 100644 (file)
index 5d8a840..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * $Id:  $
- *
- */
-
-void imap_setacl(int num_parms, char *parms[]);
-void imap_deleteacl(int num_parms, char *parms[]);
-void imap_getacl(int num_parms, char *parms[]);
-void imap_listrights(int num_parms, char *parms[]);
-void imap_myrights(int num_parms, char *parms[]);
-
diff --git a/citadel/imap_fetch.c b/citadel/imap_fetch.c
deleted file mode 100644 (file)
index 66d5b44..0000000
+++ /dev/null
@@ -1,1331 +0,0 @@
-/*
- * $Id$
- *
- * Implements the FETCH command in IMAP.
- * This is a good example of the protocol's gratuitous complexity.
- *
- */
-
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.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 <sys/wait.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-#include "citadel.h"
-#include "server.h"
-#include "sysdep_decls.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "room_ops.h"
-#include "user_ops.h"
-#include "policy.h"
-#include "database.h"
-#include "msgbase.h"
-#include "tools.h"
-#include "internet_addressing.h"
-#include "mime_parser.h"
-#include "serv_imap.h"
-#include "imap_tools.h"
-#include "imap_fetch.h"
-#include "genstamp.h"
-
-
-
-/*
- * Individual field functions for imap_do_fetch_msg() ...
- */
-
-void imap_fetch_uid(int seq) {
-       cprintf("UID %ld", IMAP->msgids[seq-1]);
-}
-
-void imap_fetch_flags(int seq) {
-       int num_flags_printed = 0;
-       cprintf("FLAGS (");
-       if (IMAP->flags[seq] & IMAP_DELETED) {
-               if (num_flags_printed > 0) cprintf(" ");
-               cprintf("\\Deleted");
-               ++num_flags_printed;
-       }
-       if (IMAP->flags[seq] & IMAP_SEEN) {
-               if (num_flags_printed > 0) cprintf(" ");
-               cprintf("\\Seen");
-               ++num_flags_printed;
-       }
-       if (IMAP->flags[seq] & IMAP_ANSWERED) {
-               if (num_flags_printed > 0) cprintf(" ");
-               cprintf("\\Answered");
-               ++num_flags_printed;
-       }
-       if (IMAP->flags[seq] & IMAP_RECENT) {
-               if (num_flags_printed > 0) cprintf(" ");
-               cprintf("\\Recent");
-               ++num_flags_printed;
-       }
-       cprintf(")");
-}
-
-void imap_fetch_internaldate(struct CtdlMessage *msg) {
-       char buf[SIZ];
-       time_t msgdate;
-
-       if (!msg) return;
-       if (msg->cm_fields['T'] != NULL) {
-               msgdate = atol(msg->cm_fields['T']);
-       }
-       else {
-               msgdate = time(NULL);
-       }
-
-       datestring(buf, sizeof buf, msgdate, DATESTRING_IMAP);
-       cprintf("INTERNALDATE \"%s\"", buf);
-}
-
-
-/*
- * Fetch RFC822-formatted messages.
- *
- * 'whichfmt' should be set to one of:
- *     "RFC822"        entire message
- *     "RFC822.HEADER" headers only (with trailing blank line)
- *     "RFC822.SIZE"   size of translated message
- *     "RFC822.TEXT"   body only (without leading blank line)
- */
-void imap_fetch_rfc822(long msgnum, char *whichfmt) {
-       char buf[SIZ];
-       char *ptr = NULL;
-       size_t headers_size, text_size, total_size;
-       size_t bytes_to_send = 0;
-       struct MetaData smi;
-       int need_to_rewrite_metadata = 0;
-       int need_body = 0;
-
-       /* Determine whether this particular fetch operation requires
-        * us to fetch the message body from disk.  If not, we can save
-        * on some disk operations...
-        */
-       if ( (!strcasecmp(whichfmt, "RFC822"))
-          || (!strcasecmp(whichfmt, "RFC822.TEXT")) ) {
-               need_body = 1;
-       }
-
-       /* If this is an RFC822.SIZE fetch, first look in the message's
-        * metadata record to see if we've saved that information.
-        */
-       if (!strcasecmp(whichfmt, "RFC822.SIZE")) {
-               GetMetaData(&smi, msgnum);
-               if (smi.meta_rfc822_length > 0L) {
-                       cprintf("RFC822.SIZE %ld", smi.meta_rfc822_length);
-                       return;
-               }
-               need_to_rewrite_metadata = 1;
-               need_body = 1;
-       }
-       
-       /* Cache the most recent RFC822 FETCH because some clients like to
-        * fetch in pieces, and we don't want to have to go back to the
-        * message store for each piece.  We also burn the cache if the
-        * client requests something that involves reading the message
-        * body, but we haven't fetched the body yet.
-        */
-       if ((IMAP->cached_rfc822_data != NULL)
-          && (IMAP->cached_rfc822_msgnum == msgnum)
-          && (IMAP->cached_rfc822_withbody || (!need_body)) ) {
-               /* Good to go! */
-       }
-       else if (IMAP->cached_rfc822_data != NULL) {
-               /* Some other message is cached -- free it */
-               free(IMAP->cached_rfc822_data);
-               IMAP->cached_rfc822_data = NULL;
-               IMAP->cached_rfc822_msgnum = (-1);
-               IMAP->cached_rfc822_len = 0;
-       }
-
-       /* At this point, we now can fetch and convert the message iff it's not
-        * the one we had cached.
-        */
-       if (IMAP->cached_rfc822_data == NULL) {
-               /*
-                * Load the message into memory for translation & measurement
-                */
-               CC->redirect_buffer = malloc(SIZ);
-               CC->redirect_len = 0;
-               CC->redirect_alloc = SIZ;
-               CtdlOutputMsg(msgnum, MT_RFC822,
-                       (need_body ? HEADERS_ALL : HEADERS_ONLY),
-                       0, 1, NULL);
-               if (!need_body) cprintf("\r\n");        /* extra trailing newline */
-               IMAP->cached_rfc822_data = CC->redirect_buffer;
-               IMAP->cached_rfc822_len = CC->redirect_len;
-               IMAP->cached_rfc822_msgnum = msgnum;
-               IMAP->cached_rfc822_withbody = need_body;
-               CC->redirect_buffer = NULL;
-               CC->redirect_len = 0;
-               CC->redirect_alloc = 0;
-               if ( (need_to_rewrite_metadata) && (IMAP->cached_rfc822_len > 0) ) {
-                       smi.meta_rfc822_length = (long)IMAP->cached_rfc822_len;
-                       PutMetaData(&smi);
-               }
-       }
-
-       /*
-        * Now figure out where the headers/text break is.  IMAP considers the
-        * intervening blank line to be part of the headers, not the text.
-        */
-       headers_size = 0;
-       text_size = 0;
-       total_size = 0;
-
-       if (need_body) {
-               ptr = IMAP->cached_rfc822_data;
-               do {
-                       ptr = memreadline(ptr, buf, sizeof buf);
-                       if (*ptr != 0) {
-                               striplt(buf);
-                               if (strlen(buf) == 0) {
-                                       headers_size = ptr - IMAP->cached_rfc822_data;
-                               }
-                       }
-               } while ( (headers_size == 0) && (*ptr != 0) );
-
-               total_size = IMAP->cached_rfc822_len;
-               text_size = total_size - headers_size;
-       }
-       else {
-               headers_size = IMAP->cached_rfc822_len;
-               total_size = IMAP->cached_rfc822_len;
-               text_size = 0;
-       }
-
-       lprintf(CTDL_DEBUG, "RFC822: headers=%d, text=%d, total=%d\n",
-               headers_size, text_size, total_size);
-
-       if (!strcasecmp(whichfmt, "RFC822.SIZE")) {
-               cprintf("RFC822.SIZE %d", total_size);
-               return;
-       }
-
-       else if (!strcasecmp(whichfmt, "RFC822")) {
-               ptr = IMAP->cached_rfc822_data;
-               bytes_to_send = total_size;
-       }
-
-       else if (!strcasecmp(whichfmt, "RFC822.HEADER")) {
-               ptr = IMAP->cached_rfc822_data;
-               bytes_to_send = headers_size;
-       }
-
-       else if (!strcasecmp(whichfmt, "RFC822.TEXT")) {
-               ptr = &IMAP->cached_rfc822_data[headers_size];
-               bytes_to_send = text_size;
-       }
-
-       cprintf("%s {%d}\r\n", whichfmt, bytes_to_send);
-       client_write(ptr, bytes_to_send);
-}
-
-
-
-/*
- * Load a specific part of a message into the temp file to be output to a
- * client.  FIXME we can handle parts like "2" and "2.1" and even "2.MIME"
- * but we still can't handle "2.HEADER" (which might not be a problem).
- *
- * Note: mime_parser() was called with dont_decode set to 1, so we have the
- * luxury of simply spewing without having to re-encode.
- */
-void imap_load_part(char *name, char *filename, char *partnum, char *disp,
-                   void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
-                   void *cbuserdata)
-{
-       char mbuf2[SIZ];
-       char *desired_section;
-
-       desired_section = (char *)cbuserdata;
-
-       if (!strcasecmp(partnum, desired_section)) {
-               client_write(content, length);
-       }
-
-       snprintf(mbuf2, sizeof mbuf2, "%s.MIME", partnum);
-
-       if (!strcasecmp(desired_section, mbuf2)) {
-               cprintf("Content-type: %s", cbtype);
-               if (strlen(cbcharset) > 0)
-                       cprintf("; charset=\"%s\"", cbcharset);
-               if (strlen(name) > 0)
-                       cprintf("; name=\"%s\"", name);
-               cprintf("\r\n");
-               if (strlen(encoding) > 0)
-                       cprintf("Content-Transfer-Encoding: %s\r\n", encoding);
-               if (strlen(encoding) > 0) {
-                       cprintf("Content-Disposition: %s", disp);
-                       if (strlen(filename) > 0) {
-                               cprintf("; filename=\"%s\"", filename);
-                       }
-                       cprintf("\r\n");
-               }
-               cprintf("Content-Length: %ld\r\n", (long)length);
-               cprintf("\r\n");
-       }
-                       
-
-}
-
-
-/* 
- * Called by imap_fetch_envelope() to output the "From" field.
- * This is in its own function because its logic is kind of complex.  We
- * really need to make this suck less.
- */
-void imap_output_envelope_from(struct CtdlMessage *msg) {
-       char user[SIZ], node[SIZ], name[SIZ];
-
-       if (!msg) return;
-
-       /* For anonymous messages, it's so easy! */
-       if (!is_room_aide() && (msg->cm_anon_type == MES_ANONONLY)) {
-               cprintf("((\"----\" NIL \"x\" \"x.org\")) ");
-               return;
-       }
-       if (!is_room_aide() && (msg->cm_anon_type == MES_ANONOPT)) {
-               cprintf("((\"anonymous\" NIL \"x\" \"x.org\")) ");
-               return;
-       }
-
-       /* For everything else, we do stuff. */
-       cprintf("((");                          /* open double-parens */
-       imap_strout(msg->cm_fields['A']);       /* personal name */
-       cprintf(" NIL ");                       /* source route (not used) */
-
-
-       if (msg->cm_fields['F'] != NULL) {
-               process_rfc822_addr(msg->cm_fields['F'], user, node, name);
-               imap_strout(user);              /* mailbox name (user id) */
-               cprintf(" ");
-               if (!strcasecmp(node, config.c_nodename)) {
-                       imap_strout(config.c_fqdn);
-               }
-               else {
-                       imap_strout(node);              /* host name */
-               }
-       }
-       else {
-               imap_strout(msg->cm_fields['A']); /* mailbox name (user id) */
-               cprintf(" ");
-               imap_strout(msg->cm_fields['N']);       /* host name */
-       }
-       
-       cprintf(")) ");                         /* close double-parens */
-}
-
-
-
-/*
- * Output an envelope address (or set of addresses) in the official,
- * convuluted, braindead format.  (Note that we can't use this for
- * the "From" address because its data may come from a number of different
- * fields.  But we can use it for "To" and possibly others.
- */
-void imap_output_envelope_addr(char *addr) {
-       char individual_addr[256];
-       int num_addrs;
-       int i;
-       char user[256];
-       char node[256];
-       char name[256];
-
-       if (addr == NULL) {
-               cprintf("NIL ");
-               return;
-       }
-
-       if (strlen(addr) == 0) {
-               cprintf("NIL ");
-               return;
-       }
-
-       cprintf("(");
-
-       /* How many addresses are listed here? */
-       num_addrs = num_tokens(addr, ',');
-
-       /* Output them one by one. */
-       for (i=0; i<num_addrs; ++i) {
-               extract_token(individual_addr, addr, i, ',', sizeof individual_addr);
-               striplt(individual_addr);
-               process_rfc822_addr(individual_addr, user, node, name);
-               cprintf("(");
-               imap_strout(name);
-               cprintf(" NIL ");
-               imap_strout(user);
-               cprintf(" ");
-               imap_strout(node);
-               cprintf(")");
-               if (i < (num_addrs-1)) cprintf(" ");
-       }
-
-       cprintf(") ");
-}
-
-
-/*
- * Implements the ENVELOPE fetch item
- * 
- * Note that the imap_strout() function can cleverly output NULL fields as NIL,
- * so we don't have to check for that condition like we do elsewhere.
- */
-void imap_fetch_envelope(struct CtdlMessage *msg) {
-       char datestringbuf[SIZ];
-       time_t msgdate;
-       char *fieldptr = NULL;
-
-       if (!msg) return;
-
-       /* Parse the message date into an IMAP-format date string */
-       if (msg->cm_fields['T'] != NULL) {
-               msgdate = atol(msg->cm_fields['T']);
-       }
-       else {
-               msgdate = time(NULL);
-       }
-       datestring(datestringbuf, sizeof datestringbuf,
-               msgdate, DATESTRING_IMAP);
-
-       /* Now start spewing data fields.  The order is important, as it is
-        * defined by the protocol specification.  Nonexistent fields must
-        * be output as NIL, existent fields must be quoted or literalled.
-        * The imap_strout() function conveniently does all this for us.
-        */
-       cprintf("ENVELOPE (");
-
-       /* Date */
-       imap_strout(datestringbuf);
-       cprintf(" ");
-
-       /* Subject */
-       imap_strout(msg->cm_fields['U']);
-       cprintf(" ");
-
-       /* From */
-       imap_output_envelope_from(msg);
-
-       /* Sender (default to same as 'From' if not present) */
-       fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Sender");
-       if (fieldptr != NULL) {
-               imap_output_envelope_addr(fieldptr);
-               free(fieldptr);
-       }
-       else {
-               imap_output_envelope_from(msg);
-       }
-
-       /* Reply-to */
-       fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Reply-to");
-       if (fieldptr != NULL) {
-               imap_output_envelope_addr(fieldptr);
-               free(fieldptr);
-       }
-       else {
-               imap_output_envelope_from(msg);
-       }
-
-       /* To */
-       imap_output_envelope_addr(msg->cm_fields['R']);
-
-       /* Cc (we do it this way because there might be a legacy non-Citadel Cc: field present) */
-       fieldptr = msg->cm_fields['Y'];
-       if (fieldptr != NULL) {
-               imap_output_envelope_addr(fieldptr);
-       }
-       else {
-               fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Cc");
-               imap_output_envelope_addr(fieldptr);
-               if (fieldptr != NULL) free(fieldptr);
-       }
-
-       /* Bcc */
-       fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Bcc");
-       imap_output_envelope_addr(fieldptr);
-       if (fieldptr != NULL) free(fieldptr);
-
-       /* In-reply-to */
-       fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "In-reply-to");
-       imap_strout(fieldptr);
-       cprintf(" ");
-       if (fieldptr != NULL) free(fieldptr);
-
-       /* message ID */
-       imap_strout(msg->cm_fields['I']);
-
-       cprintf(")");
-}
-
-/*
- * This function is called only when CC->redirect_buffer contains a set of
- * RFC822 headers with no body attached.  Its job is to strip that set of
- * headers down to *only* the ones we're interested in.
- */
-void imap_strip_headers(char *section) {
-       char buf[SIZ];
-       char *which_fields = NULL;
-       int doing_headers = 0;
-       int headers_not = 0;
-       char *parms[SIZ];
-        int num_parms = 0;
-       int i;
-       char *boiled_headers = NULL;
-       int ok = 0;
-       int done_headers = 0;
-       char *ptr = NULL;
-
-       if (CC->redirect_buffer == NULL) return;
-
-       which_fields = strdup(section);
-
-       if (!strncasecmp(which_fields, "HEADER.FIELDS", 13))
-               doing_headers = 1;
-       if (!strncasecmp(which_fields, "HEADER.FIELDS.NOT", 17))
-               headers_not = 1;
-
-       for (i=0; i<strlen(which_fields); ++i) {
-               if (which_fields[i]=='(')
-                       strcpy(which_fields, &which_fields[i+1]);
-       }
-       for (i=0; i<strlen(which_fields); ++i) {
-               if (which_fields[i]==')')
-                       which_fields[i] = 0;
-       }
-       num_parms = imap_parameterize(parms, which_fields);
-
-       boiled_headers = malloc(CC->redirect_alloc);
-       strcpy(boiled_headers, "");
-
-       ptr = CC->redirect_buffer;
-       ok = 0;
-       while ( (done_headers == 0) && (ptr = memreadline(ptr, buf, sizeof buf), *ptr != 0) ) {
-               if (!isspace(buf[0])) {
-                       ok = 0;
-                       if (doing_headers == 0) ok = 1;
-                       else {
-                               if (headers_not) ok = 1;
-                               else ok = 0;
-                               for (i=0; i<num_parms; ++i) {
-                                       if ( (!strncasecmp(buf, parms[i],
-                                          strlen(parms[i]))) &&
-                                          (buf[strlen(parms[i])]==':') ) {
-                                               if (headers_not) ok = 0;
-                                               else ok = 1;
-                                       }
-                               }
-                       }
-               }
-
-               if (ok) {
-                       strcat(boiled_headers, buf);
-                       strcat(boiled_headers, "\r\n");
-               }
-
-               if (strlen(buf) == 0) done_headers = 1;
-               if (buf[0]=='\r') done_headers = 1;
-               if (buf[0]=='\n') done_headers = 1;
-       }
-
-       strcat(boiled_headers, "\r\n");
-
-       /* Now save it back (it'll always be smaller) */
-       strcpy(CC->redirect_buffer, boiled_headers);
-       CC->redirect_len = strlen(boiled_headers);
-
-       free(which_fields);
-       free(boiled_headers);
-}
-
-
-/*
- * Implements the BODY and BODY.PEEK fetch items
- */
-void imap_fetch_body(long msgnum, char *item, int is_peek) {
-       struct CtdlMessage *msg = NULL;
-       char section[SIZ];
-       char partial[SIZ];
-       int is_partial = 0;
-       size_t pstart, pbytes;
-       int loading_body_now = 0;
-       int need_body = 1;
-       int burn_the_cache = 0;
-
-       /* extract section */
-       safestrncpy(section, item, sizeof section);
-       if (strchr(section, '[') != NULL) {
-               stripallbut(section, '[', ']');
-       }
-       lprintf(CTDL_DEBUG, "Section is: %s%s\n", section, ((strlen(section)==0) ? "(empty)" : "") );
-       if (!strncasecmp(section, "HEADER", 6)) {
-               need_body = 0;
-       }
-
-       /* Burn the cache if we don't have the same section of the 
-        * same message again.
-        */
-       if (IMAP->cached_body != NULL) {
-               if (IMAP->cached_bodymsgnum != msgnum) {
-                       burn_the_cache = 1;
-               }
-               else if ( (!IMAP->cached_body_withbody) && (need_body) ) {
-                       burn_the_cache = 1;
-               }
-               else if (strcasecmp(IMAP->cached_bodypart, section)) {
-                       burn_the_cache = 1;
-               }
-               if (burn_the_cache) {
-                       /* Yup, go ahead and burn the cache. */
-                       free(IMAP->cached_body);
-                       IMAP->cached_body_len = 0;
-                       IMAP->cached_body = NULL;
-                       IMAP->cached_bodymsgnum = (-1);
-                       strcpy(IMAP->cached_bodypart, "");
-               }
-       }
-
-       /* extract partial */
-       safestrncpy(partial, item, sizeof partial);
-       if (strchr(partial, '<') != NULL) {
-               stripallbut(partial, '<', '>');
-               is_partial = 1;
-       }
-       if (is_partial == 0) strcpy(partial, "");
-       /* if (strlen(partial) > 0) lprintf(CTDL_DEBUG, "Partial is %s\n", partial); */
-
-       if (IMAP->cached_body == NULL) {
-               CC->redirect_buffer = malloc(SIZ);
-               CC->redirect_len = 0;
-               CC->redirect_alloc = SIZ;
-               loading_body_now = 1;
-               msg = CtdlFetchMessage(msgnum, (need_body ? 1 : 0));
-       }
-
-       /* Now figure out what the client wants, and get it */
-
-       if (!loading_body_now) {
-               /* What we want is already in memory */
-       }
-
-       else if ( (!strcmp(section, "1")) && (msg->cm_format_type != 4) ) {
-               CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_NONE, 0, 1);
-       }
-
-       else if (!strcmp(section, "")) {
-               CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1);
-       }
-
-       /*
-        * If the client asked for just headers, or just particular header
-        * fields, strip it down.
-        */
-       else if (!strncasecmp(section, "HEADER", 6)) {
-               CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ONLY, 0, 1);
-               imap_strip_headers(section);
-       }
-
-       /*
-        * Strip it down if the client asked for everything _except_ headers.
-        */
-       else if (!strncasecmp(section, "TEXT", 4)) {
-               CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_NONE, 0, 1);
-       }
-
-       /*
-        * Anything else must be a part specifier.
-        * (Note value of 1 passed as 'dont_decode' so client gets it encoded)
-        */
-       else {
-               mime_parser(msg->cm_fields['M'], NULL,
-                               *imap_load_part, NULL, NULL,
-                               section,
-                               1);
-       }
-
-       if (loading_body_now) {
-               IMAP->cached_body = CC->redirect_buffer;
-               IMAP->cached_body_len = CC->redirect_len;
-               IMAP->cached_bodymsgnum = msgnum;
-               IMAP->cached_body_withbody = need_body;
-               strcpy(IMAP->cached_bodypart, section);
-               CC->redirect_buffer = NULL;
-               CC->redirect_len = 0;
-               CC->redirect_alloc = 0;
-       }
-
-       if (is_partial == 0) {
-               cprintf("BODY[%s] {%d}\r\n", section, IMAP->cached_body_len);
-               pstart = 0;
-               pbytes = IMAP->cached_body_len;
-       }
-       else {
-               sscanf(partial, "%d.%d", &pstart, &pbytes);
-               if (pbytes > (IMAP->cached_body_len - pstart)) {
-                       pbytes = IMAP->cached_body_len - pstart;
-               }
-               cprintf("BODY[%s]<%d> {%d}\r\n", section, pstart, pbytes);
-       }
-
-       /* Here we go -- output it */
-       client_write(&IMAP->cached_body[pstart], pbytes);
-
-       if (msg != NULL) {
-               CtdlFreeMessage(msg);
-       }
-
-       /* Mark this message as "seen" *unless* this is a "peek" operation */
-       if (is_peek == 0) {
-               CtdlSetSeen(&msgnum, 1, 1, ctdlsetseen_seen, NULL, NULL);
-       }
-}
-
-/*
- * Called immediately before outputting a multipart bodystructure
- */
-void imap_fetch_bodystructure_pre(
-               char *name, char *filename, char *partnum, char *disp,
-               void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
-               void *cbuserdata
-               ) {
-
-       cprintf("(");
-}
-
-
-
-/*
- * Called immediately after outputting a multipart bodystructure
- */
-void imap_fetch_bodystructure_post(
-               char *name, char *filename, char *partnum, char *disp,
-               void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
-               void *cbuserdata
-               ) {
-
-       char subtype[128];
-
-       cprintf(" ");
-
-       /* disposition */
-       extract_token(subtype, cbtype, 1, '/', sizeof subtype);
-       imap_strout(subtype);
-
-       /* body language */
-       cprintf(" NIL");
-
-       cprintf(")");
-}
-
-
-
-/*
- * Output the info for a MIME part in the format required by BODYSTRUCTURE.
- *
- */
-void imap_fetch_bodystructure_part(
-               char *name, char *filename, char *partnum, char *disp,
-               void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
-               void *cbuserdata
-               ) {
-
-       int have_cbtype = 0;
-       int have_encoding = 0;
-       int lines = 0;
-       size_t i;
-       char cbmaintype[128];
-       char cbsubtype[128];
-
-       if (cbtype != NULL) if (strlen(cbtype)>0) have_cbtype = 1;
-       if (have_cbtype) {
-               extract_token(cbmaintype, cbtype, 0, '/', sizeof cbmaintype);
-               extract_token(cbsubtype, cbtype, 1, '/', sizeof cbsubtype);
-       }
-       else {
-               strcpy(cbmaintype, "TEXT");
-               strcpy(cbsubtype, "PLAIN");
-       }
-
-       cprintf("(");
-       imap_strout(cbmaintype);
-       cprintf(" ");
-       imap_strout(cbsubtype);
-       cprintf(" ");
-
-       if (cbcharset == NULL) {
-               cprintf("(\"CHARSET\" \"US-ASCII\"");
-       }
-       else if (strlen(cbcharset) == 0) {
-               cprintf("(\"CHARSET\" \"US-ASCII\"");
-       }
-       else {
-               cprintf("(\"CHARSET\" ");
-               imap_strout(cbcharset);
-       }
-
-       if (name != NULL) if (strlen(name)>0) {
-               cprintf(" \"NAME\" ");
-               imap_strout(name);
-       }
-
-       cprintf(") ");
-
-       cprintf("NIL ");        /* Body ID */
-       cprintf("NIL ");        /* Body description */
-
-       if (encoding != NULL) if (strlen(encoding) > 0)  have_encoding = 1;
-       if (have_encoding) {
-               imap_strout(encoding);
-       }
-       else {
-               imap_strout("7BIT");
-       }
-       cprintf(" ");
-
-       /* The next field is the size of the part in bytes. */
-       cprintf("%ld ", (long)length);  /* bytes */
-
-       /* The next field is the number of lines in the part, if and only
-        * if the part is TEXT.  More gratuitous complexity.
-        */
-       if (!strcasecmp(cbmaintype, "TEXT")) {
-               if (length) for (i=0; i<length; ++i) {
-                       if (((char *)content)[i] == '\n') ++lines;
-               }
-               cprintf("%d ", lines);
-       }
-
-       /* More gratuitous complexity */
-       if ((!strcasecmp(cbmaintype, "MESSAGE"))
-          && (!strcasecmp(cbsubtype, "RFC822"))) {
-               /* FIXME
-                     A body type of type MESSAGE and subtype RFC822
-                     contains, immediately after the basic fields, the
-                     envelope structure, body structure, and size in
-                     text lines of the encapsulated message.
-               */
-       }
-
-       /* MD5 value of body part; we can get away with NIL'ing this */
-       cprintf("NIL ");
-
-       /* Disposition */
-       if (disp == NULL) {
-               cprintf("NIL");
-       }
-       else if (strlen(disp) == 0) {
-               cprintf("NIL");
-       }
-       else {
-               cprintf("(");
-               imap_strout(disp);
-               if (filename != NULL) if (strlen(filename)>0) {
-                       cprintf(" (\"FILENAME\" ");
-                       imap_strout(filename);
-                       cprintf(")");
-               }
-               cprintf(")");
-       }
-
-       /* Body language (not defined yet) */
-       cprintf(" NIL)");
-}
-
-
-
-/*
- * Spew the BODYSTRUCTURE data for a message.
- *
- */
-void imap_fetch_bodystructure (long msgnum, char *item,
-               struct CtdlMessage *msg) {
-       char *rfc822 = NULL;
-       char *rfc822_body = NULL;
-       size_t rfc822_len;
-       size_t rfc822_headers_len;
-       size_t rfc822_body_len;
-       char *ptr = NULL;
-       char buf[SIZ];
-       int lines = 0;
-
-       /* Handle NULL message gracefully */
-       if (msg == NULL) {
-               cprintf("BODYSTRUCTURE (\"TEXT\" \"PLAIN\" "
-                       "(\"CHARSET\" \"US-ASCII\") NIL NIL "
-                       "\"7BIT\" 0 0)");
-               return;
-       }
-
-       /* For non-RFC822 (ordinary Citadel) messages, this is short and
-        * sweet...
-        */
-       if (msg->cm_format_type != FMT_RFC822) {
-
-               /* *sigh* We have to RFC822-format the message just to be able
-                * to measure it.  FIXME use smi cached fields if possible
-                */
-
-               CC->redirect_buffer = malloc(SIZ);
-               CC->redirect_len = 0;
-               CC->redirect_alloc = SIZ;
-               CtdlOutputPreLoadedMsg(msg, MT_RFC822, 0, 0, 1);
-               rfc822 = CC->redirect_buffer;
-               rfc822_len = CC->redirect_len;
-               CC->redirect_buffer = NULL;
-               CC->redirect_len = 0;
-               CC->redirect_alloc = 0;
-
-               ptr = rfc822;
-               while (ptr = memreadline(ptr, buf, sizeof buf), *ptr != 0) {
-                       ++lines;
-                       if ((strlen(buf) == 0) && (rfc822_body == NULL)) {
-                               rfc822_body = ptr;
-                       }
-               }
-
-               rfc822_headers_len = rfc822_body - rfc822;
-               rfc822_body_len = rfc822_len - rfc822_headers_len;
-               free(rfc822);
-
-               cprintf("BODYSTRUCTURE (\"TEXT\" \"PLAIN\" "
-                       "(\"CHARSET\" \"US-ASCII\") NIL NIL "
-                       "\"7BIT\" %d %d)", rfc822_body_len, lines);
-
-               return;
-       }
-
-       /* For messages already stored in RFC822 format, we have to parse. */
-       cprintf("BODYSTRUCTURE ");
-       mime_parser(msg->cm_fields['M'],
-                       NULL,
-                       *imap_fetch_bodystructure_part, /* part */
-                       *imap_fetch_bodystructure_pre,  /* pre-multi */
-                       *imap_fetch_bodystructure_post, /* post-multi */
-                       NULL,
-                       1);     /* don't decode -- we want it as-is */
-}
-
-
-/*
- * imap_do_fetch() calls imap_do_fetch_msg() to output the data of an
- * individual message, once it has been selected for output.
- */
-void imap_do_fetch_msg(int seq, int num_items, char **itemlist) {
-       int i;
-       struct CtdlMessage *msg = NULL;
-       int body_loaded = 0;
-
-       /* Don't attempt to fetch bogus messages or UID's */
-       if (seq < 1) return;
-       if (IMAP->msgids[seq-1] < 1L) return;
-
-       buffer_output();
-       cprintf("* %d FETCH (", seq);
-
-       for (i=0; i<num_items; ++i) {
-
-               /* Fetchable without going to the message store at all */
-               if (!strcasecmp(itemlist[i], "UID")) {
-                       imap_fetch_uid(seq);
-               }
-               else if (!strcasecmp(itemlist[i], "FLAGS")) {
-                       imap_fetch_flags(seq-1);
-               }
-
-               /* Potentially fetchable from cache, if the client requests
-                * stuff from the same message several times in a row.
-                */
-               else if (!strcasecmp(itemlist[i], "RFC822")) {
-                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]);
-               }
-               else if (!strcasecmp(itemlist[i], "RFC822.HEADER")) {
-                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]);
-               }
-               else if (!strcasecmp(itemlist[i], "RFC822.SIZE")) {
-                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]);
-               }
-               else if (!strcasecmp(itemlist[i], "RFC822.TEXT")) {
-                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]);
-               }
-
-               /* BODY fetches do their own fetching and caching too. */
-               else if (!strncasecmp(itemlist[i], "BODY[", 5)) {
-                       imap_fetch_body(IMAP->msgids[seq-1], itemlist[i], 0);
-               }
-               else if (!strncasecmp(itemlist[i], "BODY.PEEK[", 10)) {
-                       imap_fetch_body(IMAP->msgids[seq-1], itemlist[i], 1);
-               }
-
-               /* Otherwise, load the message into memory.
-                */
-               else if (!strcasecmp(itemlist[i], "BODYSTRUCTURE")) {
-                       if ((msg != NULL) && (!body_loaded)) {
-                               CtdlFreeMessage(msg);   /* need the whole thing */
-                               msg = NULL;
-                       }
-                       if (msg == NULL) {
-                               msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                               body_loaded = 1;
-                       }
-                       imap_fetch_bodystructure(IMAP->msgids[seq-1],
-                                       itemlist[i], msg);
-               }
-               else if (!strcasecmp(itemlist[i], "ENVELOPE")) {
-                       if (msg == NULL) {
-                               msg = CtdlFetchMessage(IMAP->msgids[seq-1], 0);
-                               body_loaded = 0;
-                       }
-                       imap_fetch_envelope(msg);
-               }
-               else if (!strcasecmp(itemlist[i], "INTERNALDATE")) {
-                       if (msg == NULL) {
-                               msg = CtdlFetchMessage(IMAP->msgids[seq-1], 0);
-                               body_loaded = 0;
-                       }
-                       imap_fetch_internaldate(msg);
-               }
-
-               if (i != num_items-1) cprintf(" ");
-       }
-
-       cprintf(")\r\n");
-       unbuffer_output();
-       if (msg != NULL) {
-               CtdlFreeMessage(msg);
-       }
-}
-
-
-
-/*
- * imap_fetch() calls imap_do_fetch() to do its actual work, once it's
- * validated and boiled down the request a bit.
- */
-void imap_do_fetch(int num_items, char **itemlist) {
-       int i;
-
-       if (IMAP->num_msgs > 0) {
-               for (i = 0; i < IMAP->num_msgs; ++i) {
-
-                       /* Abort the fetch loop if the session breaks.
-                        * This is important for users who keep mailboxes
-                        * that are too big *and* are too impatient to
-                        * let them finish loading.  :)
-                        */
-                       if (CC->kill_me) return;
-
-                       /* Get any message marked for fetch. */
-                       if (IMAP->flags[i] & IMAP_SELECTED) {
-                               imap_do_fetch_msg(i+1, num_items, itemlist);
-                       }
-               }
-       }
-}
-
-
-
-/*
- * Back end for imap_handle_macros()
- * Note that this function *only* looks at the beginning of the string.  It
- * is not a generic search-and-replace function.
- */
-void imap_macro_replace(char *str, char *find, char *replace) {
-       char holdbuf[SIZ];
-
-       if (!strncasecmp(str, find, strlen(find))) {
-               if (str[strlen(find)]==' ') {
-                       strcpy(holdbuf, &str[strlen(find)+1]);
-                       strcpy(str, replace);
-                       strcat(str, " ");
-                       strcat(str, holdbuf);
-               }
-               if (str[strlen(find)]==0) {
-                       strcpy(holdbuf, &str[strlen(find)+1]);
-                       strcpy(str, replace);
-               }
-       }
-}
-
-
-
-/*
- * Handle macros embedded in FETCH data items.
- * (What the heck are macros doing in a wire protocol?  Are we trying to save
- * the computer at the other end the trouble of typing a lot of characters?)
- */
-void imap_handle_macros(char *str) {
-       int i;
-       int nest = 0;
-
-       for (i=0; i<strlen(str); ++i) {
-               if (str[i]=='(') ++nest;
-               if (str[i]=='[') ++nest;
-               if (str[i]=='<') ++nest;
-               if (str[i]=='{') ++nest;
-               if (str[i]==')') --nest;
-               if (str[i]==']') --nest;
-               if (str[i]=='>') --nest;
-               if (str[i]=='}') --nest;
-
-               if (nest <= 0) {
-                       imap_macro_replace(&str[i],
-                               "ALL",
-                               "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE"
-                       );
-                       imap_macro_replace(&str[i],
-                               "BODY",
-                               "BODYSTRUCTURE"
-                       );
-                       imap_macro_replace(&str[i],
-                               "FAST",
-                               "FLAGS INTERNALDATE RFC822.SIZE"
-                       );
-                       imap_macro_replace(&str[i],
-                               "FULL",
-                               "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY"
-                       );
-               }
-       }
-}
-
-
-/*
- * Break out the data items requested, possibly a parenthesized list.
- * Returns the number of data items, or -1 if the list is invalid.
- * NOTE: this function alters the string it is fed, and uses it as a buffer
- * to hold the data for the pointers it returns.
- */
-int imap_extract_data_items(char **argv, char *items) {
-       int num_items = 0;
-       int nest = 0;
-       int i, initial_len;
-       char *start;
-
-       /* Convert all whitespace to ordinary space characters. */
-       for (i=0; i<strlen(items); ++i) {
-               if (isspace(items[i])) items[i]=' ';
-       }
-
-       /* Strip leading and trailing whitespace, then strip leading and
-        * trailing parentheses if it's a list
-        */
-       striplt(items);
-       if ( (items[0]=='(') && (items[strlen(items)-1]==')') ) {
-               items[strlen(items)-1] = 0;
-               strcpy(items, &items[1]);
-               striplt(items);
-       }
-
-       /* Parse any macro data items */
-       imap_handle_macros(items);
-
-       /*
-        * Now break out the data items.  We throw in one trailing space in
-        * order to avoid having to break out the last one manually.
-        */
-       strcat(items, " ");
-       start = items;
-       initial_len = strlen(items);
-       for (i=0; i<initial_len; ++i) {
-               if (items[i]=='(') ++nest;
-               if (items[i]=='[') ++nest;
-               if (items[i]=='<') ++nest;
-               if (items[i]=='{') ++nest;
-               if (items[i]==')') --nest;
-               if (items[i]==']') --nest;
-               if (items[i]=='>') --nest;
-               if (items[i]=='}') --nest;
-
-               if (nest <= 0) if (items[i]==' ') {
-                       items[i] = 0;
-                       argv[num_items++] = start;
-                       start = &items[i+1];
-               }
-       }
-
-       return(num_items);
-
-}
-
-
-/*
- * One particularly hideous aspect of IMAP is that we have to allow the client
- * to specify arbitrary ranges and/or sets of messages to fetch.  Citadel IMAP
- * handles this by setting the IMAP_SELECTED flag for each message specified in
- * the ranges/sets, then looping through the message array, outputting messages
- * with the flag set.  We don't bother returning an error if an out-of-range
- * number is specified (we just return quietly) because any client braindead
- * enough to request a bogus message number isn't going to notice the
- * difference anyway.
- *
- * This function clears out the IMAP_SELECTED bits, then sets that bit for each
- * message included in the specified range.
- *
- * Set is_uid to 1 to fetch by UID instead of sequence number.
- */
-void imap_pick_range(char *supplied_range, int is_uid) {
-       int i;
-       int num_sets;
-       int s;
-       char setstr[SIZ], lostr[SIZ], histr[SIZ];
-       long lo, hi;
-       char actual_range[SIZ];
-
-       /* 
-        * Handle the "ALL" macro
-        */
-       if (!strcasecmp(supplied_range, "ALL")) {
-               safestrncpy(actual_range, "1:*", sizeof actual_range);
-       }
-       else {
-               safestrncpy(actual_range, supplied_range, sizeof actual_range);
-       }
-
-       /*
-        * Clear out the IMAP_SELECTED flags for all messages.
-        */
-       for (i = 0; i < IMAP->num_msgs; ++i) {
-               IMAP->flags[i] = IMAP->flags[i] & ~IMAP_SELECTED;
-       }
-
-       /*
-        * Now set it for all specified messages.
-        */
-       num_sets = num_tokens(actual_range, ',');
-       for (s=0; s<num_sets; ++s) {
-               extract_token(setstr, actual_range, s, ',', sizeof setstr);
-
-               extract_token(lostr, setstr, 0, ':', sizeof lostr);
-               if (num_tokens(setstr, ':') >= 2) {
-                       extract_token(histr, setstr, 1, ':', sizeof histr);
-                       if (!strcmp(histr, "*")) snprintf(histr, sizeof histr, "%ld", LONG_MAX);
-               } 
-               else {
-                       safestrncpy(histr, lostr, sizeof histr);
-               }
-               lo = atol(lostr);
-               hi = atol(histr);
-
-               /* Loop through the array, flipping bits where appropriate */
-               for (i = 1; i <= IMAP->num_msgs; ++i) {
-                       if (is_uid) {   /* fetch by sequence number */
-                               if ( (IMAP->msgids[i-1]>=lo)
-                                  && (IMAP->msgids[i-1]<=hi)) {
-                                       IMAP->flags[i-1] |= IMAP_SELECTED;
-                               }
-                       }
-                       else {          /* fetch by uid */
-                               if ( (i>=lo) && (i<=hi)) {
-                                       IMAP->flags[i-1] |= IMAP_SELECTED;
-                               }
-                       }
-               }
-       }
-
-}
-
-
-
-/*
- * This function is called by the main command loop.
- */
-void imap_fetch(int num_parms, char *parms[]) {
-       char items[SIZ];
-       char *itemlist[512];
-       int num_items;
-       int i;
-
-       if (num_parms < 4) {
-               cprintf("%s BAD invalid parameters\r\n", parms[0]);
-               return;
-       }
-
-       imap_pick_range(parms[2], 0);
-
-       strcpy(items, "");
-       for (i=3; i<num_parms; ++i) {
-               strcat(items, parms[i]);
-               if (i < (num_parms-1)) strcat(items, " ");
-       }
-
-       num_items = imap_extract_data_items(itemlist, items);
-       if (num_items < 1) {
-               cprintf("%s BAD invalid data item list\r\n", parms[0]);
-               return;
-       }
-
-       imap_do_fetch(num_items, itemlist);
-       cprintf("%s OK FETCH completed\r\n", parms[0]);
-}
-
-/*
- * This function is called by the main command loop.
- */
-void imap_uidfetch(int num_parms, char *parms[]) {
-       char items[SIZ];
-       char *itemlist[512];
-       int num_items;
-       int i;
-       int have_uid_item = 0;
-
-       if (num_parms < 5) {
-               cprintf("%s BAD invalid parameters\r\n", parms[0]);
-               return;
-       }
-
-       imap_pick_range(parms[3], 1);
-
-       strcpy(items, "");
-       for (i=4; i<num_parms; ++i) {
-               strcat(items, parms[i]);
-               if (i < (num_parms-1)) strcat(items, " ");
-       }
-
-       num_items = imap_extract_data_items(itemlist, items);
-       if (num_items < 1) {
-               cprintf("%s BAD invalid data item list\r\n", parms[0]);
-               return;
-       }
-
-       /* If the "UID" item was not included, we include it implicitly
-        * (at the beginning) because this is a UID FETCH command
-        */
-       for (i=0; i<num_items; ++i) {
-               if (!strcasecmp(itemlist[i], "UID")) ++have_uid_item;
-       }
-       if (have_uid_item == 0) {
-               memmove(&itemlist[1], &itemlist[0], (sizeof(itemlist[0]) * num_items));
-               ++num_items;
-               itemlist[0] = "UID";
-       }
-
-       imap_do_fetch(num_items, itemlist);
-       cprintf("%s OK UID FETCH completed\r\n", parms[0]);
-}
-
-
diff --git a/citadel/imap_fetch.h b/citadel/imap_fetch.h
deleted file mode 100644 (file)
index f8ed5f1..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * $Id$
- *
- */
-
-void imap_pick_range(char *range, int is_uid);
-void imap_fetch(int num_parms, char *parms[]);
-void imap_uidfetch(int num_parms, char *parms[]);
-void imap_fetch_flags(int seq);
-int imap_extract_data_items(char **argv, char *items);
diff --git a/citadel/imap_list.c b/citadel/imap_list.c
deleted file mode 100644 (file)
index be01c99..0000000
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * $Id$
- *
- * Implements the LIST and LSUB commands.
- *
- * Copyright (C) 2000-2007 by Art Cancro and others.
- * This code is released under the terms of the GNU General Public License.
- *
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.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 <sys/wait.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-#include "citadel.h"
-#include "server.h"
-#include "sysdep_decls.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "room_ops.h"
-#include "user_ops.h"
-#include "policy.h"
-#include "database.h"
-#include "msgbase.h"
-#include "tools.h"
-#include "internet_addressing.h"
-#include "serv_imap.h"
-#include "imap_tools.h"
-#include "imap_fetch.h"
-#include "imap_search.h"
-#include "imap_store.h"
-#include "imap_acl.h"
-#include "imap_misc.h"
-#include "imap_list.h"
-
-#ifdef HAVE_OPENSSL
-#include "serv_crypto.h"
-#endif
-
-/*
- * Used by LIST and LSUB to show the floors in the listing
- */
-void imap_list_floors(char *verb, int num_patterns, char **patterns)
-{
-       int i;
-       struct floor *fl;
-       int j = 0;
-       int match = 0;
-
-       for (i = 0; i < MAXFLOORS; ++i) {
-               fl = cgetfloor(i);
-               if (fl->f_flags & F_INUSE) {
-                       match = 0;
-                       for (j=0; j<num_patterns; ++j) {
-                               if (imap_mailbox_matches_pattern (patterns[j], fl->f_name)) {
-                                       match = 1;
-                               }
-                       }
-                       if (match) {
-                               cprintf("* %s (\\NoSelect \\HasChildren) \"/\" ", verb);
-                               imap_strout(fl->f_name);
-                               cprintf("\r\n");
-                       }
-               }
-       }
-}
-
-
-/*
- * Back end for imap_list()
- *
- * Implementation note: IMAP "subscribed folder" is equivalent to Citadel "known room"
- *
- * The "user data" field is actually an array of pointers; see below for the breakdown
- *
- */
-void imap_listroom(struct ctdlroom *qrbuf, void *data)
-{
-       char buf[SIZ];
-       char return_options[256];
-       int ra;
-       int yes_output_this_room;
-
-       char **data_for_callback;
-       char *verb;
-       int subscribed_rooms_only;
-       int num_patterns;
-       char **patterns;
-       int return_subscribed;
-       int return_children;
-       int return_metadata;
-       int i = 0;
-       int match = 0;
-
-       /* Here's how we break down the array of pointers passed to us */
-       data_for_callback = data;
-       verb = data_for_callback[0];
-       subscribed_rooms_only = (int) data_for_callback[1];
-       num_patterns = (int) data_for_callback[2];
-       patterns = (char **) data_for_callback[3];
-       return_subscribed = (int) data_for_callback[4];
-       return_children = (int) data_for_callback[5];
-       return_metadata = (int) data_for_callback[6];
-
-       /* Only list rooms to which the user has access!! */
-       yes_output_this_room = 0;
-       strcpy(return_options, "");
-       CtdlRoomAccess(qrbuf, &CC->user, &ra, NULL);
-
-       if (return_subscribed) {
-               if (ra & UA_KNOWN) {
-                       strcat(return_options, "\\Subscribed");
-               }
-       }
-
-       /* Warning: ugly hack.
-        * We don't have any way to determine the presence of child mailboxes
-        * without refactoring this entire module.  So we're just going to return
-        * the \HasChildren attribute for every room.
-        * We'll fix this later when we have time.
-        */
-       if (return_children) {
-               if (strlen(return_options) > 0) {
-                       strcat(return_options, " ");
-               }
-               strcat(return_options, "\\HasChildren");
-       }
-
-       if (subscribed_rooms_only) {
-               if (ra & UA_KNOWN) {
-                       yes_output_this_room = 1;
-               }
-       }
-       else {
-               if ((ra & UA_KNOWN) || ((ra & UA_GOTOALLOWED) && (ra & UA_ZAPPED))) {
-                       yes_output_this_room = 1;
-               }
-       }
-
-       if (yes_output_this_room) {
-               imap_mailboxname(buf, sizeof buf, qrbuf);
-               match = 0;
-               for (i=0; i<num_patterns; ++i) {
-                       if (imap_mailbox_matches_pattern(patterns[i], buf)) {
-                               match = 1;
-                       }
-               }
-               if (match) {
-                       cprintf("* %s (%s) \"/\" ", verb, return_options);
-                       imap_strout(buf);
-
-                       if (return_metadata) {
-                               cprintf(" (METADATA ())");      /* FIXME */
-                       }
-
-                       cprintf("\r\n");
-               }
-       }
-}
-
-
-/*
- * Implements the LIST and LSUB commands
- */
-void imap_list(int num_parms, char *parms[])
-{
-       int subscribed_rooms_only = 0;
-       char verb[16];
-       int i, j, paren_nest;
-       char *data_for_callback[7];
-       int num_patterns = 1;
-       char *patterns[MAX_PATTERNS];
-       int selection_left = (-1);
-       int selection_right = (-1);
-       int return_left = (-1);
-       int return_right = (-1);
-       int root_pos = 2;
-       int patterns_left = 3;
-       int patterns_right = 3;
-       int extended_list_in_use = 0;
-       int return_subscribed = 0;
-       int return_children = 0;
-       int return_metadata = 0;
-       int select_metadata_left = (-1);
-       int select_metadata_right = (-1);
-       int select_metadata_nest = 0;
-
-       if (num_parms < 4) {
-               cprintf("%s BAD arguments invalid\r\n", parms[0]);
-               return;
-       }
-
-       /* parms[1] is the IMAP verb being used (e.g. LIST or LSUB)
-        * This tells us how to behave, and what verb to return back to the caller
-        */
-       safestrncpy(verb, parms[1], sizeof verb);
-       j = strlen(verb);
-       for (i=0; i<j; ++i) {
-               verb[i] = toupper(verb[i]);
-       }
-
-       if (!strcasecmp(verb, "LSUB")) {
-               subscribed_rooms_only = 1;
-       }
-
-       /*
-        * In order to implement draft-ietf-imapext-list-extensions-18
-        * ("LIST Command Extensions") we need to:
-        *
-        * 1. Extract "selection options"
-        *                              (Extraction: done
-        *                              SUBSCRIBED option: done
-        *                              RECURSIVEMATCH option: not done yet
-        *                              REMOTE: safe to silently ignore)
-        *
-        * 2. Extract "return options"
-        *                              (Extraction: done
-        *                              SUBSCRIBED option: done
-        *                              CHILDREN option: done, but needs a non-ugly rewrite)
-        *
-        * 3. Determine whether there is more than one match pattern (done)
-        */
-
-       /*
-        * If parameter 2 begins with a '(' character, the client is specifying
-        * selection options.  Extract their exact position, and then modify our
-        * expectation of where the root folder will be specified.
-        */
-       if (parms[2][0] == '(') {
-               extended_list_in_use = 1;
-               selection_left = 2;
-               paren_nest = 0;
-               for (i=2; i<num_parms; ++i) {
-                       for (j=0; j<strlen(parms[i]); ++j) {
-                               if (parms[i][j] == '(') ++paren_nest;
-                               if (parms[i][j] == ')') --paren_nest;
-                       }
-                       if (paren_nest == 0) {
-                               selection_right = i;    /* found end of selection options */
-                               root_pos = i+1;         /* folder root appears after selection options */
-                               i = num_parms + 1;      /* break out of the loop */
-                       }
-               }
-       }
-
-       /* If selection options were found, do something with them.
-        */
-       if ((selection_left > 0) && (selection_right >= selection_left)) {
-
-               /* Strip off the outer parentheses */
-               if (parms[selection_left][0] == '(') {
-                       strcpy(parms[selection_left], &parms[selection_left][1]);
-               }
-               if (parms[selection_right][strlen(parms[selection_right])-1] == ')') {
-                       parms[selection_right][strlen(parms[selection_right])-1] = 0;
-               }
-
-               for (i=selection_left; i<=selection_right; ++i) {
-
-                       /* are we in the middle of a metadata select block? */
-                       if ((select_metadata_left >= 0) && (select_metadata_right < 0)) {
-                               select_metadata_nest += haschar(parms[i], '(') - haschar(parms[i], ')') ;
-                               if (select_metadata_nest == 0) {
-                                       select_metadata_right = i;
-                               }
-                       }
-
-                       else if (!strcasecmp(parms[i], "METADATA")) {
-                               select_metadata_left = i+1;
-                               select_metadata_nest = 0;
-                       }
-
-                       else if (!strcasecmp(parms[i], "SUBSCRIBED")) {
-                               subscribed_rooms_only = 1;
-                       }
-
-                       else if (!strcasecmp(parms[i], "RECURSIVEMATCH")) {
-                               /* FIXME - do this! */
-                       }
-
-               }
-
-       }
-
-       lprintf(CTDL_DEBUG, "select metadata: %d to %d\n", select_metadata_left, select_metadata_right);
-       /* FIXME blah, we have to do something with this */
-
-       /* The folder root appears immediately after the selection options,
-        * or in position 2 if no selection options were specified.
-        */
-       patterns_left = root_pos + 1;
-       patterns_right = root_pos + 1;
-
-       if (parms[patterns_left][0] == '(') {
-               extended_list_in_use = 1;
-               paren_nest = 0;
-               for (i=patterns_left; i<num_parms; ++i) {
-                       for (j=0; j<strlen(parms[i]); ++j) {
-                               if (parms[i][j] == '(') ++paren_nest;
-                               if (parms[i][j] == ')') --paren_nest;
-                       }
-                       if (paren_nest == 0) {
-                               patterns_right = i;     /* found end of patterns */
-                               i = num_parms + 1;      /* break out of the loop */
-                       }
-               }
-               num_patterns = patterns_right - patterns_left + 1;
-               for (i=0; i<num_patterns; ++i) {
-                       if (i < MAX_PATTERNS) {
-                               patterns[i] = malloc(512);
-                               snprintf(patterns[i], 512, "%s%s", parms[root_pos], parms[patterns_left+i]);
-                               if (i == 0) {
-                                       strcpy(patterns[i], &patterns[i][1]);
-                               }
-                               if (i == num_patterns-1) {
-                                       patterns[i][strlen(patterns[i])-1] = 0;
-                               }
-                       }
-               }
-       }
-       else {
-               num_patterns = 1;
-               patterns[0] = malloc(512);
-               snprintf(patterns[0], 512, "%s%s", parms[root_pos], parms[patterns_left]);
-       }
-
-       /* If the word "RETURN" appears after the folder pattern list, then the client
-        * is specifying return options.
-        */
-       if (num_parms - patterns_right > 2) if (!strcasecmp(parms[patterns_right+1], "RETURN")) {
-               return_left = patterns_right + 2;
-               extended_list_in_use = 1;
-               paren_nest = 0;
-               for (i=return_left; i<num_parms; ++i) {
-                       for (j=0; j<strlen(parms[i]); ++j) {
-                               if (parms[i][j] == '(') ++paren_nest;
-                               if (parms[i][j] == ')') --paren_nest;
-                       }
-
-                       /* Might as well look for these while we're in here... */
-                       if (parms[i][0] == '(') strcpy(parms[i], &parms[i][1]);
-                       if (parms[i][strlen(parms[i])-1] == ')') parms[i][strlen(parms[i])-1] = 0;
-                       lprintf(9, "evaluating <%s>\n", parms[i]);
-
-                       if (!strcasecmp(parms[i], "SUBSCRIBED")) {
-                               return_subscribed = 1;
-                       }
-
-                       else if (!strcasecmp(parms[i], "CHILDREN")) {
-                               return_children = 1;
-                       }
-
-                       else if (!strcasecmp(parms[i], "METADATA")) {
-                               return_metadata = 1;
-                       }
-
-                       if (paren_nest == 0) {
-                               return_right = i;       /* found end of patterns */
-                               i = num_parms + 1;      /* break out of the loop */
-                       }
-               }
-       }
-
-       /* Now start setting up the data we're going to send to the ForEachRoom() callback.
-        */
-       data_for_callback[0] = (char *) verb;
-       data_for_callback[1] = (char *) subscribed_rooms_only;
-       data_for_callback[2] = (char *) num_patterns;
-       data_for_callback[3] = (char *) patterns;
-       data_for_callback[4] = (char *) return_subscribed;
-       data_for_callback[5] = (char *) return_children;
-       data_for_callback[6] = (char *) return_metadata;
-
-       /* The non-extended LIST command is required to treat an empty
-        * ("" string) mailbox name argument as a special request to return the
-        * hierarchy delimiter and the root name of the name given in the
-        * reference parameter.
-        */
-       if ( (strlen(patterns[0]) == 0) && (extended_list_in_use == 0) ) {
-               cprintf("* %s (\\Noselect) \"/\" \"\"\r\n", verb);
-       }
-
-       /* Non-empty mailbox names, and any form of the extended LIST command,
-        * is handled by this loop.
-        */
-       else {
-               imap_list_floors(verb, num_patterns, patterns);
-               ForEachRoom(imap_listroom, data_for_callback);
-       }
-
-       /* 
-        * Free the pattern buffers we allocated above.
-        */
-       for (i=0; i<num_patterns; ++i) {
-               free(patterns[i]);
-       }
-
-       cprintf("%s OK %s completed\r\n", parms[0], verb);
-}
diff --git a/citadel/imap_list.h b/citadel/imap_list.h
deleted file mode 100644 (file)
index e65d855..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * $Id$
- */
-
-/*
- * In the extended form of LIST the client is allowed to specify
- * multiple match patterns.  How many will we allow?
- */
-#define MAX_PATTERNS 20
-
-void imap_list(int num_parms, char *parms[]);
diff --git a/citadel/imap_metadata.c b/citadel/imap_metadata.c
deleted file mode 100644 (file)
index 4dde983..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * $Id$
- *
- * IMAP METADATA extension
- *
- * This is an implementation of the Bynari variant of the METADATA extension.
- *
- */
-
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.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 <sys/wait.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-#include "citadel.h"
-#include "server.h"
-#include "sysdep_decls.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "room_ops.h"
-#include "user_ops.h"
-#include "policy.h"
-#include "database.h"
-#include "msgbase.h"
-#include "tools.h"
-#include "internet_addressing.h"
-#include "serv_imap.h"
-#include "imap_tools.h"
-#include "imap_fetch.h"
-#include "imap_misc.h"
-#include "genstamp.h"
-
-
-
-/*
- * Implements the SETMETADATA command.
- *
- * Again, the only thing we're interested in setting here is the folder type.
- *
- * Attempting to set anything else calls a stub which fools the client into
- * thinking that there is no remaining space available to store annotations.
- */
-void imap_setmetadata(int num_parms, char *parms[]) {
-       char roomname[ROOMNAMELEN];
-       char savedroom[ROOMNAMELEN];
-       int msgs, new;
-       int ret;
-       int setting_user_value = 0;
-       char set_value[32];
-       int set_view = VIEW_BBS;
-       struct visit vbuf;
-
-       if (num_parms != 6) {
-               cprintf("%s BAD usage error\r\n", parms[0]);
-               return;
-       }
-
-       /*
-        * Don't allow other types of metadata to be set
-        */
-       if (strcasecmp(parms[3], "/vendor/kolab/folder-type")) {
-               cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", parms[0]);
-               return;
-       }
-
-       if (!strcasecmp(parms[4], "(value.shared")) {
-               setting_user_value = 0;                         /* global view */
-       }
-       else if (!strcasecmp(parms[4], "(value.priv")) {
-               setting_user_value = 1;                         /* per-user view */
-       }
-       else {
-               cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", parms[0]);
-               return;
-       }
-
-       /*
-        * Extract the folder type without any parentheses.  Then learn
-        * the Citadel view type based on the supplied folder type.
-        */
-       extract_token(set_value, parms[5], 0, ')', sizeof set_value);
-       if (!strncasecmp(set_value, "mail", 4)) {
-               set_view = VIEW_MAILBOX;
-       }
-       else if (!strncasecmp(set_value, "event", 5)) {
-               set_view = VIEW_CALENDAR;
-       }
-       else if (!strncasecmp(set_value, "contact", 7)) {
-               set_view = VIEW_ADDRESSBOOK;
-       }
-       else if (!strncasecmp(set_value, "journal", 7)) {
-               set_view = VIEW_JOURNAL;
-       }
-       else if (!strncasecmp(set_value, "note", 4)) {
-               set_view = VIEW_NOTES;
-       }
-       else if (!strncasecmp(set_value, "task", 4)) {
-               set_view = VIEW_TASKS;
-       }
-       else {
-               set_view = VIEW_MAILBOX;
-       }
-
-       ret = imap_grabroom(roomname, parms[2], 0);
-       if (ret != 0) {
-               cprintf("%s NO Invalid mailbox name or access denied\r\n",
-                       parms[0]);
-               return;
-       }
-
-       /*
-        * usergoto() formally takes us to the desired room.  (If another
-        * folder is selected, save its name so we can return there!!!!!)
-        */
-       if (IMAP->selected) {
-               strcpy(savedroom, CC->room.QRname);
-       }
-       usergoto(roomname, 0, 0, &msgs, &new);
-
-       /*
-        * Always set the per-user view to the requested one.
-        */
-       CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
-       vbuf.v_view = set_view;
-       CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
-
-       /* If this is a "value.priv" set operation, we're done. */
-
-       if (setting_user_value)
-       {
-               cprintf("%s OK SETANNOTATION complete\r\n", parms[0]);
-       }
-
-       /* If this is a "value.shared" set operation, we are allowed to perform it
-        * under certain conditions.
-        */
-       else if (       (is_room_aide())                                        /* aide or room aide */
-               ||      (       (CC->room.QRflags & QR_MAILBOX)
-                       &&      (CC->user.usernum == atol(CC->room.QRname))     /* mailbox owner */
-                       )
-               ||      (msgs == 0)             /* hack: if room is empty, assume we just created it */
-       ) {
-               lgetroom(&CC->room, CC->room.QRname);
-               CC->room.QRdefaultview = set_view;
-               lputroom(&CC->room);
-               cprintf("%s OK SETANNOTATION complete\r\n", parms[0]);
-       }
-
-       /* If we got to this point, we don't have permission to set the default view. */
-       else {
-               cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", parms[0]);
-       }
-
-       /*
-        * If a different folder was previously selected, return there now.
-        */
-       if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
-               usergoto(savedroom, 0, 0, &msgs, &new);
-       }
-       return;
-}
-
-
-/*
- * Implements the GETMETADATA command.
- *
- * Regardless of what the client asked for, we are going to supply them with
- * the folder type.  It's the only metadata we have anyway.
- */
-void imap_getmetadata(int num_parms, char *parms[]) {
-       char roomname[ROOMNAMELEN];
-       char savedroom[ROOMNAMELEN];
-       int msgs, new;
-       int ret;
-
-       if (num_parms > 5) {
-               cprintf("%s BAD usage error\r\n", parms[0]);
-               return;
-       }
-
-       ret = imap_grabroom(roomname, parms[2], 0);
-       if (ret != 0) {
-               cprintf("%s NO Invalid mailbox name or access denied\r\n",
-                       parms[0]);
-               return;
-       }
-
-       /*
-        * usergoto() formally takes us to the desired room.  (If another
-        * folder is selected, save its name so we can return there!!!!!)
-        */
-       if (IMAP->selected) {
-               strcpy(savedroom, CC->room.QRname);
-       }
-       usergoto(roomname, 0, 0, &msgs, &new);
-
-       cprintf("* METADATA ");
-       imap_strout(parms[2]);
-       cprintf(" \"/vendor/kolab/folder-type\" (\"value.shared\" \"");
-
-       /* If it's one of our hard-coded default rooms, we know what to do... */
-
-       if (!strcasecmp(&CC->room.QRname[11], MAILROOM)) {
-               cprintf("mail.inbox");
-       }
-       else if (!strcasecmp(&CC->room.QRname[11], SENTITEMS)) {
-               cprintf("mail.sentitems");
-       }
-       else if (!strcasecmp(&CC->room.QRname[11], USERCALENDARROOM)) {
-               cprintf("event.default");
-       }
-       else if (!strcasecmp(&CC->room.QRname[11], USERCONTACTSROOM)) {
-               cprintf("contact.default");
-       }
-       else if (!strcasecmp(&CC->room.QRname[11], USERNOTESROOM)) {
-               cprintf("note.default");
-       }
-       else if (!strcasecmp(&CC->room.QRname[11], USERTASKSROOM)) {
-               cprintf("task.default");
-       }
-
-       /* Otherwise, use the view for this room to determine the type of data.
-        * We are going with the default view rather than the user's view, because
-        * the default view almost always defines the actual contents, while the
-        * user's view might only make changes to presentation.  It also saves us
-        * an extra database access because we don't need to load the visit record.
-        */
-
-       else if (CC->room.QRdefaultview == VIEW_CALENDAR) {
-               cprintf("event");
-       }
-       else if (CC->room.QRdefaultview == VIEW_ADDRESSBOOK) {
-               cprintf("contact");
-       }
-       else if (CC->room.QRdefaultview == VIEW_TASKS) {
-               cprintf("task");
-       }
-       else if (CC->room.QRdefaultview == VIEW_NOTES) {
-               cprintf("note");
-       }
-       else if (CC->room.QRdefaultview == VIEW_JOURNAL) {
-               cprintf("journal");
-       }
-
-       /* If none of the above conditions were met, consider it an ordinary mailbox. */
-       else {
-               cprintf("mail");
-       }
-
-       /* "mail.outbox" and "junkemail" are not implemented. */
-
-       cprintf("\")\r\n");
-
-       /*
-        * If a different folder was previously selected, return there now.
-        */
-       if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
-               usergoto(savedroom, 0, 0, &msgs, &new);
-       }
-
-       cprintf("%s OK GETMETADATA complete\r\n", parms[0]);
-       return;
-}
-
diff --git a/citadel/imap_metadata.h b/citadel/imap_metadata.h
deleted file mode 100644 (file)
index 2ce656b..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * $Id: imap_acl.h 5148 2007-05-08 15:40:16Z ajc $
- *
- */
-
-void imap_getmetadata(int num_parms, char *parms[]);
-void imap_setmetadata(int num_parms, char *parms[]);
diff --git a/citadel/imap_misc.c b/citadel/imap_misc.c
deleted file mode 100644 (file)
index 350f9ad..0000000
+++ /dev/null
@@ -1,472 +0,0 @@
-/*
- * $Id$
- *
- *
- */
-
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.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 <sys/wait.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-#include "citadel.h"
-#include "server.h"
-#include "sysdep_decls.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "room_ops.h"
-#include "user_ops.h"
-#include "policy.h"
-#include "database.h"
-#include "msgbase.h"
-#include "tools.h"
-#include "internet_addressing.h"
-#include "serv_imap.h"
-#include "imap_tools.h"
-#include "imap_fetch.h"
-#include "imap_misc.h"
-#include "genstamp.h"
-
-
-
-
-
-
-/*
- * imap_copy() calls imap_do_copy() to do its actual work, once it's
- * validated and boiled down the request a bit.  (returns 0 on success)
- */
-int imap_do_copy(char *destination_folder) {
-       int i;
-       char roomname[ROOMNAMELEN];
-       struct ctdlroom qrbuf;
-       long *selected_msgs = NULL;
-       int num_selected = 0;
-
-       if (IMAP->num_msgs < 1) {
-               return(0);
-       }
-
-       i = imap_grabroom(roomname, destination_folder, 0);
-       if (i != 0) return(i);
-
-       /*
-        * Copy all the message pointers in one shot.
-        */
-       selected_msgs = malloc(sizeof(long) * IMAP->num_msgs);
-       if (selected_msgs == NULL) return(-1);
-
-       for (i = 0; i < IMAP->num_msgs; ++i) {
-               if (IMAP->flags[i] & IMAP_SELECTED) {
-                       selected_msgs[num_selected++] = IMAP->msgids[i];
-               }
-       }
-
-       if (num_selected > 0) {
-               CtdlCopyMsgsToRoom(selected_msgs, num_selected, roomname);
-       }
-       free(selected_msgs);
-
-       /* Don't bother wasting any more time if there were no messages. */
-       if (num_selected == 0) {
-               return(0);
-       }
-
-       /* Enumerate lists of messages for which flags are toggled */
-       long *seen_yes = NULL;
-       int num_seen_yes = 0;
-       long *seen_no = NULL;
-       int num_seen_no = 0;
-       long *answ_yes = NULL;
-       int num_answ_yes = 0;
-       long *answ_no = NULL;
-       int num_answ_no = 0;
-
-       seen_yes = malloc(num_selected * sizeof(long));
-       seen_no = malloc(num_selected * sizeof(long));
-       answ_yes = malloc(num_selected * sizeof(long));
-       answ_no = malloc(num_selected * sizeof(long));
-
-       for (i = 0; i < IMAP->num_msgs; ++i) {
-               if (IMAP->flags[i] & IMAP_SELECTED) {
-                       if (IMAP->flags[i] & IMAP_SEEN) {
-                               seen_yes[num_seen_yes++] = IMAP->msgids[i];
-                       }
-                       if ((IMAP->flags[i] & IMAP_SEEN) == 0) {
-                               seen_no[num_seen_no++] = IMAP->msgids[i];
-                       }
-                       if (IMAP->flags[i] & IMAP_ANSWERED) {
-                               answ_yes[num_answ_yes++] = IMAP->msgids[i];
-                       }
-                       if ((IMAP->flags[i] & IMAP_ANSWERED) == 0) {
-                               answ_no[num_answ_no++] = IMAP->msgids[i];
-                       }
-               }
-       }
-
-       /* Set the flags... */
-       i = getroom(&qrbuf, roomname);
-       if (i == 0) {
-               CtdlSetSeen(seen_yes, num_seen_yes, 1, ctdlsetseen_seen, NULL, &qrbuf);
-               CtdlSetSeen(seen_no, num_seen_no, 0, ctdlsetseen_seen, NULL, &qrbuf);
-               CtdlSetSeen(answ_yes, num_answ_yes, 1, ctdlsetseen_answered, NULL, &qrbuf);
-               CtdlSetSeen(answ_no, num_answ_no, 0, ctdlsetseen_answered, NULL, &qrbuf);
-       }
-
-       free(seen_yes);
-       free(seen_no);
-       free(answ_yes);
-       free(answ_no);
-
-       return(0);
-}
-
-
-
-/*
- * This function is called by the main command loop.
- */
-void imap_copy(int num_parms, char *parms[]) {
-       int ret;
-
-       if (num_parms != 4) {
-               cprintf("%s BAD invalid parameters\r\n", parms[0]);
-               return;
-       }
-
-       if (imap_is_message_set(parms[2])) {
-               imap_pick_range(parms[2], 0);
-       }
-       else {
-               cprintf("%s BAD invalid parameters\r\n", parms[0]);
-               return;
-       }
-
-       ret = imap_do_copy(parms[3]);
-       if (!ret) {
-               cprintf("%s OK COPY completed\r\n", parms[0]);
-       }
-       else {
-               cprintf("%s NO COPY failed (error %d)\r\n", parms[0], ret);
-       }
-}
-
-/*
- * This function is called by the main command loop.
- */
-void imap_uidcopy(int num_parms, char *parms[]) {
-
-       if (num_parms != 5) {
-               cprintf("%s BAD invalid parameters\r\n", parms[0]);
-               return;
-       }
-
-       if (imap_is_message_set(parms[3])) {
-               imap_pick_range(parms[3], 1);
-       }
-       else {
-               cprintf("%s BAD invalid parameters\r\n", parms[0]);
-               return;
-       }
-
-       if (imap_do_copy(parms[4]) == 0) {
-               cprintf("%s OK UID COPY completed\r\n", parms[0]);
-       }
-       else {
-               cprintf("%s NO UID COPY failed\r\n", parms[0]);
-       }
-}
-
-
-/*
- * Poll for instant messages (yeah, we can do this in IMAP ... I think)
- */
-void imap_print_instant_messages(void) {
-       struct ExpressMessage *ptr, *holdptr;
-       char *dumpomatic = NULL;
-       char tmp[SIZ];
-       int i;
-       size_t size, size2;
-       struct tm stamp;
-
-       if (CC->FirstExpressMessage == NULL) {
-               return;
-       }
-       begin_critical_section(S_SESSION_TABLE);
-       ptr = CC->FirstExpressMessage;
-       CC->FirstExpressMessage = NULL;
-       end_critical_section(S_SESSION_TABLE);
-
-       while (ptr != NULL) {
-               localtime_r(&(ptr->timestamp), &stamp);
-               size = strlen(ptr->text) + SIZ;
-               dumpomatic = malloc(size);
-               strcpy(dumpomatic, "");
-               if (ptr->flags && EM_BROADCAST)
-                       strcat(dumpomatic, "Broadcast message ");
-               else if (ptr->flags && EM_CHAT)
-                       strcat(dumpomatic, "Chat request ");
-               else if (ptr->flags && EM_GO_AWAY)
-                       strcat(dumpomatic, "Please logoff now, as requested ");
-               else
-                       strcat(dumpomatic, "Message ");
-
-               /* Timestamp.  Can this be improved? */
-               if (stamp.tm_hour == 0 || stamp.tm_hour == 12)
-                       sprintf(tmp, "at 12:%02d%cm",
-                               stamp.tm_min, 
-                               stamp.tm_hour ? 'p' : 'a');
-               else if (stamp.tm_hour > 12)            /* pm */
-                       sprintf(tmp, "at %d:%02dpm",
-                               stamp.tm_hour - 12,
-                               stamp.tm_min);
-               else                                    /* am */
-                       sprintf(tmp, "at %d:%02dam",
-                               stamp.tm_hour, stamp.tm_min);
-               strcat(dumpomatic, tmp);
-
-               size2 = strlen(dumpomatic);
-               snprintf(&dumpomatic[size2], size - size2,
-                       " from %s:\n", ptr->sender);
-               if (ptr->text != NULL)
-                       strcat(dumpomatic, ptr->text);
-
-               holdptr = ptr->next;
-               if (ptr->text != NULL) free(ptr->text);
-               free(ptr);
-               ptr = holdptr;
-
-               for (i=0; i<strlen(dumpomatic); ++i) {
-                       if (!isprint(dumpomatic[i])) dumpomatic[i] = ' ';
-                       if (dumpomatic[i]=='\\') dumpomatic[i]='/';
-                       if (dumpomatic[i]=='\"') dumpomatic[i]='\'';
-               }
-
-               cprintf("* OK [ALERT] %s\r\n", dumpomatic);
-               free(dumpomatic);
-       }
-       cprintf("000\n");
-}
-
-
-/*
- * imap_do_append_flags() is called by imap_append() to set any flags that
- * the client specified at append time.
- *
- * FIXME find a way to do these in bulk so we don't max out our db journal
- */
-void imap_do_append_flags(long new_msgnum, char *new_message_flags) {
-       char flags[32];
-       char this_flag[sizeof flags];
-       int i;
-
-       if (new_message_flags == NULL) return;
-       if (strlen(new_message_flags) == 0) return;
-
-       safestrncpy(flags, new_message_flags, sizeof flags);
-
-       for (i=0; i<num_tokens(flags, ' '); ++i) {
-               extract_token(this_flag, flags, i, ' ', sizeof this_flag);
-               if (this_flag[0] == '\\') strcpy(this_flag, &this_flag[1]);
-               if (!strcasecmp(this_flag, "Seen")) {
-                       CtdlSetSeen(&new_msgnum, 1, 1, ctdlsetseen_seen,
-                               NULL, NULL);
-               }
-               if (!strcasecmp(this_flag, "Answered")) {
-                       CtdlSetSeen(&new_msgnum, 1, 1, ctdlsetseen_answered,
-                               NULL, NULL);
-               }
-       }
-}
-
-
-/*
- * This function is called by the main command loop.
- */
-void imap_append(int num_parms, char *parms[]) {
-       long literal_length;
-       long bytes_transferred;
-       long stripped_length = 0;
-       struct CtdlMessage *msg = NULL;
-       long new_msgnum = (-1L);
-       int ret = 0;
-       char roomname[ROOMNAMELEN];
-       char buf[SIZ];
-       char savedroom[ROOMNAMELEN];
-       int msgs, new;
-       int i;
-       char new_message_flags[SIZ];
-
-       if (num_parms < 4) {
-               cprintf("%s BAD usage error\r\n", parms[0]);
-               return;
-       }
-
-       if ( (parms[num_parms-1][0] != '{')
-          || (parms[num_parms-1][strlen(parms[num_parms-1])-1] != '}') )  {
-               cprintf("%s BAD no message literal supplied\r\n", parms[0]);
-               return;
-       }
-
-       strcpy(new_message_flags, "");
-       if (num_parms >= 5) {
-               for (i=3; i<num_parms; ++i) {
-                       strcat(new_message_flags, parms[i]);
-                       strcat(new_message_flags, " ");
-               }
-               stripallbut(new_message_flags, '(', ')');
-       }
-
-       /* This is how we'd do this if it were relevant in our data store.
-        * if (num_parms >= 6) {
-        *  new_message_internaldate = parms[4];
-        * }
-        */
-
-       literal_length = atol(&parms[num_parms-1][1]);
-       if (literal_length < 1) {
-               cprintf("%s BAD Message length must be at least 1.\r\n",
-                       parms[0]);
-               return;
-       }
-
-       imap_free_transmitted_message();        /* just in case. */
-       IMAP->transmitted_message = malloc(literal_length + 1);
-       if (IMAP->transmitted_message == NULL) {
-               cprintf("%s NO Cannot allocate memory.\r\n", parms[0]);
-               return;
-       }
-       IMAP->transmitted_length = literal_length;
-
-       cprintf("+ Transmit message now.\r\n");
-
-       bytes_transferred = 0;
-
-       ret = client_read(IMAP->transmitted_message, literal_length);
-       IMAP->transmitted_message[literal_length] = 0;
-
-       if (ret != 1) {
-               cprintf("%s NO Read failed.\r\n", parms[0]);
-               return;
-       }
-
-       /* Client will transmit a trailing CRLF after the literal (the message
-        * text) is received.  This call to client_getln() absorbs it.
-        */
-       flush_output();
-       client_getln(buf, sizeof buf);
-
-       /* Convert RFC822 newlines (CRLF) to Unix newlines (LF) */
-       lprintf(CTDL_DEBUG, "Converting CRLF to LF\n");
-       stripped_length = 0;
-       for (i=0; i<literal_length; ++i) {
-               if (strncmp(&IMAP->transmitted_message[i], "\r\n", 2)) {
-                       IMAP->transmitted_message[stripped_length++] =
-                               IMAP->transmitted_message[i];
-               }
-       }
-       literal_length = stripped_length;
-       IMAP->transmitted_message[literal_length] = 0;  /* reterminate it */
-
-       lprintf(CTDL_DEBUG, "Converting message format\n");
-       msg = convert_internet_message(IMAP->transmitted_message);
-       IMAP->transmitted_message = NULL;
-       IMAP->transmitted_length = 0;
-
-       ret = imap_grabroom(roomname, parms[2], 0);
-       if (ret != 0) {
-               cprintf("%s NO Invalid mailbox name or access denied\r\n",
-                       parms[0]);
-               return;
-       }
-
-       /*
-        * usergoto() formally takes us to the desired room.  (If another
-        * folder is selected, save its name so we can return there!!!!!)
-        */
-       if (IMAP->selected) {
-               strcpy(savedroom, CC->room.QRname);
-       }
-       usergoto(roomname, 0, 0, &msgs, &new);
-
-       /* If the user is locally authenticated, FORCE the From: header to
-        * show up as the real sender.  FIXME do we really want to do this?
-        * Probably should make it site-definable or even room-definable.
-        *
-        * For now, we allow "forgeries" if the room is one of the user's
-        * private mailboxes.
-        */
-       if (CC->logged_in) {
-          if ( (CC->room.QRflags & QR_MAILBOX) == 0) {
-               if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
-               if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
-               if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
-               msg->cm_fields['A'] = strdup(CC->user.fullname);
-               msg->cm_fields['N'] = strdup(config.c_nodename);
-               msg->cm_fields['H'] = strdup(config.c_humannode);
-           }
-       }
-
-       /* 
-        * Can we post here?
-        */
-       ret = CtdlDoIHavePermissionToPostInThisRoom(buf, sizeof buf);
-
-       if (ret) {
-               /* Nope ... print an error message */
-               cprintf("%s NO %s\r\n", parms[0], buf);
-       }
-
-       else {
-               /* Yes ... go ahead and post! */
-               if (msg != NULL) {
-                       new_msgnum = CtdlSubmitMsg(msg, NULL, "");
-               }
-               if (new_msgnum >= 0L) {
-                       cprintf("%s OK APPEND completed\r\n", parms[0]);
-               }
-               else {
-                       cprintf("%s BAD Error %ld saving message to disk.\r\n",
-                               parms[0], new_msgnum);
-               }
-       }
-
-       /*
-        * IMAP protocol response to client has already been sent by now.
-        *
-        * If another folder is selected, go back to that room so we can resume
-        * our happy day without violent explosions.
-        */
-       if (IMAP->selected) {
-               usergoto(savedroom, 0, 0, &msgs, &new);
-       }
-
-       /* We don't need this buffer anymore */
-       CtdlFreeMessage(msg);
-
-       if (new_message_flags != NULL) {
-               imap_do_append_flags(new_msgnum, new_message_flags);
-       }
-}
diff --git a/citadel/imap_misc.h b/citadel/imap_misc.h
deleted file mode 100644 (file)
index 7fec936..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * $Id$
- *
- */
-
-void imap_copy(int num_parms, char *parms[]);
-void imap_uidcopy(int num_parms, char *parms[]);
-void imap_print_instant_messages(void);
-void imap_append(int num_parms, char *parms[]);
diff --git a/citadel/imap_search.c b/citadel/imap_search.c
deleted file mode 100644 (file)
index 1c64777..0000000
+++ /dev/null
@@ -1,637 +0,0 @@
-/*
- * $Id$
- *
- * Implements IMAP's gratuitously complex SEARCH command.
- *
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.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 <sys/wait.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-#include "citadel.h"
-#include "server.h"
-#include "sysdep_decls.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "room_ops.h"
-#include "user_ops.h"
-#include "policy.h"
-#include "database.h"
-#include "msgbase.h"
-#include "tools.h"
-#include "internet_addressing.h"
-#include "serv_imap.h"
-#include "imap_tools.h"
-#include "imap_fetch.h"
-#include "imap_search.h"
-#include "genstamp.h"
-#include "serv_fulltext.h"
-
-
-/*
- * imap_do_search() calls imap_do_search_msg() to search an individual
- * message after it has been fetched from the disk.  This function returns
- * nonzero if there is a match.
- *
- * supplied_msg MAY be used to pass a pointer to the message in memory,
- * if for some reason it's already been loaded.  If not, the message will
- * be loaded only if one or more search criteria require it.
- */
-int imap_do_search_msg(int seq, struct CtdlMessage *supplied_msg,
-                       int num_items, char **itemlist, int is_uid) {
-
-       int match = 0;
-       int is_not = 0;
-       int is_or = 0;
-       int pos = 0;
-       int i;
-       char *fieldptr;
-       struct CtdlMessage *msg = NULL;
-       int need_to_free_msg = 0;
-
-       if (num_items == 0) {
-               return(0);
-       }
-       msg = supplied_msg;
-
-       /* Initially we start at the beginning. */
-       pos = 0;
-
-       /* Check for the dreaded NOT criterion. */
-       if (!strcasecmp(itemlist[0], "NOT")) {
-               is_not = 1;
-               pos = 1;
-       }
-
-       /* Check for the dreaded OR criterion. */
-       if (!strcasecmp(itemlist[0], "OR")) {
-               is_or = 1;
-               pos = 1;
-       }
-
-       /* Now look for criteria. */
-       if (!strcasecmp(itemlist[pos], "ALL")) {
-               match = 1;
-               ++pos;
-       }
-       
-       else if (!strcasecmp(itemlist[pos], "ANSWERED")) {
-               if (IMAP->flags[seq-1] & IMAP_ANSWERED) {
-                       match = 1;
-               }
-               ++pos;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "BCC")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Bcc");
-                       if (fieldptr != NULL) {
-                               if (bmstrcasestr(fieldptr, itemlist[pos+1])) {
-                                       match = 1;
-                               }
-                               free(fieldptr);
-                       }
-               }
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "BEFORE")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       if (msg->cm_fields['T'] != NULL) {
-                               if (imap_datecmp(itemlist[pos+1],
-                                               atol(msg->cm_fields['T'])) < 0) {
-                                       match = 1;
-                               }
-                       }
-               }
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "BODY")) {
-
-               /* If fulltext indexing is active, on this server,
-                *  all messages have already been qualified.
-                */
-               if (config.c_enable_fulltext) {
-                       match = 1;
-               }
-
-               /* Otherwise, we have to do a slow search. */
-               else {
-                       if (msg == NULL) {
-                               msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                               need_to_free_msg = 1;
-                       }
-                       if (msg != NULL) {
-                               if (bmstrcasestr(msg->cm_fields['M'], itemlist[pos+1])) {
-                                       match = 1;
-                               }
-                       }
-               }
-
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "CC")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       fieldptr = msg->cm_fields['Y'];
-                       if (fieldptr != NULL) {
-                               if (bmstrcasestr(fieldptr, itemlist[pos+1])) {
-                                       match = 1;
-                               }
-                       }
-                       else {
-                               fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Cc");
-                               if (fieldptr != NULL) {
-                                       if (bmstrcasestr(fieldptr, itemlist[pos+1])) {
-                                               match = 1;
-                                       }
-                                       free(fieldptr);
-                               }
-                       }
-               }
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "DELETED")) {
-               if (IMAP->flags[seq-1] & IMAP_DELETED) {
-                       match = 1;
-               }
-               ++pos;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "DRAFT")) {
-               if (IMAP->flags[seq-1] & IMAP_DRAFT) {
-                       match = 1;
-               }
-               ++pos;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "FLAGGED")) {
-               if (IMAP->flags[seq-1] & IMAP_FLAGGED) {
-                       match = 1;
-               }
-               ++pos;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "FROM")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       if (bmstrcasestr(msg->cm_fields['A'], itemlist[pos+1])) {
-                               match = 1;
-                       }
-                       if (bmstrcasestr(msg->cm_fields['F'], itemlist[pos+1])) {
-                               match = 1;
-                       }
-               }
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "HEADER")) {
-
-               /* We've got to do a slow search for this because the client
-                * might be asking for an RFC822 header field that has not been
-                * converted into a Citadel header field.  That requires
-                * examining the message body.
-                */
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-
-               if (msg != NULL) {
-       
-                       CC->redirect_buffer = malloc(SIZ);
-                       CC->redirect_len = 0;
-                       CC->redirect_alloc = SIZ;
-                       CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ONLY, 0, 1);
-       
-                       fieldptr = rfc822_fetch_field(CC->redirect_buffer, itemlist[pos+1]);
-                       if (fieldptr != NULL) {
-                               if (bmstrcasestr(fieldptr, itemlist[pos+2])) {
-                                       match = 1;
-                               }
-                               free(fieldptr);
-                       }
-       
-                       free(CC->redirect_buffer);
-                       CC->redirect_buffer = NULL;
-                       CC->redirect_len = 0;
-                       CC->redirect_alloc = 0;
-               }
-
-               pos += 3;       /* Yes, three */
-       }
-
-       else if (!strcasecmp(itemlist[pos], "KEYWORD")) {
-               /* not implemented */
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "LARGER")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       if (strlen(msg->cm_fields['M']) > atoi(itemlist[pos+1])) {
-                               match = 1;
-                       }
-               }
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "NEW")) {
-               if ( (IMAP->flags[seq-1] & IMAP_RECENT) && (!(IMAP->flags[seq-1] & IMAP_SEEN))) {
-                       match = 1;
-               }
-               ++pos;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "OLD")) {
-               if (!(IMAP->flags[seq-1] & IMAP_RECENT)) {
-                       match = 1;
-               }
-               ++pos;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "ON")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       if (msg->cm_fields['T'] != NULL) {
-                               if (imap_datecmp(itemlist[pos+1],
-                                               atol(msg->cm_fields['T'])) == 0) {
-                                       match = 1;
-                               }
-                       }
-               }
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "RECENT")) {
-               if (IMAP->flags[seq-1] & IMAP_RECENT) {
-                       match = 1;
-               }
-               ++pos;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "SEEN")) {
-               if (IMAP->flags[seq-1] & IMAP_SEEN) {
-                       match = 1;
-               }
-               ++pos;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "SENTBEFORE")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       if (msg->cm_fields['T'] != NULL) {
-                               if (imap_datecmp(itemlist[pos+1],
-                                               atol(msg->cm_fields['T'])) < 0) {
-                                       match = 1;
-                               }
-                       }
-               }
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "SENTON")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       if (msg->cm_fields['T'] != NULL) {
-                               if (imap_datecmp(itemlist[pos+1],
-                                               atol(msg->cm_fields['T'])) == 0) {
-                                       match = 1;
-                               }
-                       }
-               }
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "SENTSINCE")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       if (msg->cm_fields['T'] != NULL) {
-                               if (imap_datecmp(itemlist[pos+1],
-                                               atol(msg->cm_fields['T'])) >= 0) {
-                                       match = 1;
-                               }
-                       }
-               }
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "SINCE")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       if (msg->cm_fields['T'] != NULL) {
-                               if (imap_datecmp(itemlist[pos+1],
-                                               atol(msg->cm_fields['T'])) >= 0) {
-                                       match = 1;
-                               }
-                       }
-               }
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "SMALLER")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       if (strlen(msg->cm_fields['M']) < atoi(itemlist[pos+1])) {
-                               match = 1;
-                       }
-               }
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "SUBJECT")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       if (bmstrcasestr(msg->cm_fields['U'], itemlist[pos+1])) {
-                               match = 1;
-                       }
-               }
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "TEXT")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       for (i='A'; i<='Z'; ++i) {
-                               if (bmstrcasestr(msg->cm_fields[i], itemlist[pos+1])) {
-                                       match = 1;
-                               }
-                       }
-               }
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "TO")) {
-               if (msg == NULL) {
-                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
-                       need_to_free_msg = 1;
-               }
-               if (msg != NULL) {
-                       if (bmstrcasestr(msg->cm_fields['R'], itemlist[pos+1])) {
-                               match = 1;
-                       }
-               }
-               pos += 2;
-       }
-
-       /* FIXME this is b0rken.  fix it. */
-       else if (imap_is_message_set(itemlist[pos])) {
-               if (is_msg_in_sequence_set(itemlist[pos], seq)) {
-                       match = 1;
-               }
-               pos += 1;
-       }
-
-       /* FIXME this is b0rken.  fix it. */
-       else if (!strcasecmp(itemlist[pos], "UID")) {
-               if (is_msg_in_sequence_set(itemlist[pos+1], IMAP->msgids[seq-1])) {
-                       match = 1;
-               }
-               pos += 2;
-       }
-
-       /* Now here come the 'UN' criteria.  Why oh why do we have to
-        * implement *both* the 'UN' criteria *and* the 'NOT' keyword?  Why
-        * can't there be *one* way to do things?  More gratuitous complexity.
-        */
-
-       else if (!strcasecmp(itemlist[pos], "UNANSWERED")) {
-               if ((IMAP->flags[seq-1] & IMAP_ANSWERED) == 0) {
-                       match = 1;
-               }
-               ++pos;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "UNDELETED")) {
-               if ((IMAP->flags[seq-1] & IMAP_DELETED) == 0) {
-                       match = 1;
-               }
-               ++pos;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "UNDRAFT")) {
-               if ((IMAP->flags[seq-1] & IMAP_DRAFT) == 0) {
-                       match = 1;
-               }
-               ++pos;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "UNFLAGGED")) {
-               if ((IMAP->flags[seq-1] & IMAP_FLAGGED) == 0) {
-                       match = 1;
-               }
-               ++pos;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "UNKEYWORD")) {
-               /* FIXME */
-               pos += 2;
-       }
-
-       else if (!strcasecmp(itemlist[pos], "UNSEEN")) {
-               if ((IMAP->flags[seq-1] & IMAP_SEEN) == 0) {
-                       match = 1;
-               }
-               ++pos;
-       }
-
-       /* Remember to negate if we were told to */
-       if (is_not) {
-               match = !match;
-       }
-
-       /* Keep going if there are more criteria! */
-       if (pos < num_items) {
-
-               if (is_or) {
-                       match = (match || imap_do_search_msg(seq, msg,
-                               num_items - pos, &itemlist[pos], is_uid));
-               }
-               else {
-                       match = (match && imap_do_search_msg(seq, msg,
-                               num_items - pos, &itemlist[pos], is_uid));
-               }
-
-       }
-
-       if (need_to_free_msg) {
-               CtdlFreeMessage(msg);
-       }
-       return(match);
-}
-
-
-/*
- * imap_search() calls imap_do_search() to do its actual work, once it's
- * validated and boiled down the request a bit.
- */
-void imap_do_search(int num_items, char **itemlist, int is_uid) {
-       int i, j, k;
-       int fts_num_msgs = 0;
-       long *fts_msgs = NULL;
-       int is_in_list = 0;
-       int num_results = 0;
-
-       /* If there is a BODY search criterion in the query, use our full
-        * text index to disqualify messages that don't have any chance of
-        * matching.  (Only do this if the index is enabled!!)
-        */
-       if (config.c_enable_fulltext) for (i=0; i<(num_items-1); ++i) {
-               if (!strcasecmp(itemlist[i], "BODY")) {
-                       ft_search(&fts_num_msgs, &fts_msgs, itemlist[i+1]);
-                       if (fts_num_msgs > 0) {
-                               for (j=0; j < IMAP->num_msgs; ++j) {
-                                       if (IMAP->flags[j] & IMAP_SELECTED) {
-                                               is_in_list = 0;
-                                               for (k=0; k<fts_num_msgs; ++k) {
-                                                       if (IMAP->msgids[j] == fts_msgs[k]) {
-                                                               ++is_in_list;
-                                                       }
-                                               }
-                                       }
-                                       if (!is_in_list) {
-                                               IMAP->flags[j] = IMAP->flags[j] & ~IMAP_SELECTED;
-                                       }
-                               }
-                       }
-                       else {          /* no hits on the index; disqualify every message */
-                               for (j=0; j < IMAP->num_msgs; ++j) {
-                                       IMAP->flags[j] = IMAP->flags[j] & ~IMAP_SELECTED;
-                               }
-                       }
-                       if (fts_msgs) {
-                               free(fts_msgs);
-                       }
-               }
-       }
-
-       /* Now go through the messages and apply all search criteria. */
-       buffer_output();
-       cprintf("* SEARCH ");
-       if (IMAP->num_msgs > 0)
-        for (i = 0; i < IMAP->num_msgs; ++i)
-         if (IMAP->flags[i] & IMAP_SELECTED) {
-               if (imap_do_search_msg(i+1, NULL, num_items, itemlist, is_uid)) {
-                       if (num_results != 0) {
-                               cprintf(" ");
-                       }
-                       if (is_uid) {
-                               cprintf("%ld", IMAP->msgids[i]);
-                       }
-                       else {
-                               cprintf("%d", i+1);
-                       }
-                       ++num_results;
-               }
-       }
-       cprintf("\r\n");
-       unbuffer_output();
-}
-
-
-/*
- * This function is called by the main command loop.
- */
-void imap_search(int num_parms, char *parms[]) {
-       int i;
-
-       if (num_parms < 3) {
-               cprintf("%s BAD invalid parameters\r\n", parms[0]);
-               return;
-       }
-
-       for (i = 0; i < IMAP->num_msgs; ++i) {
-               IMAP->flags[i] |= IMAP_SELECTED;
-       }
-
-       imap_do_search(num_parms-2, &parms[2], 0);
-       cprintf("%s OK SEARCH completed\r\n", parms[0]);
-}
-
-/*
- * This function is called by the main command loop.
- */
-void imap_uidsearch(int num_parms, char *parms[]) {
-       int i;
-
-       if (num_parms < 4) {
-               cprintf("%s BAD invalid parameters\r\n", parms[0]);
-               return;
-       }
-
-       for (i = 0; i < IMAP->num_msgs; ++i) {
-               IMAP->flags[i] |= IMAP_SELECTED;
-       }
-
-       imap_do_search(num_parms-3, &parms[3], 1);
-       cprintf("%s OK UID SEARCH completed\r\n", parms[0]);
-}
-
-
diff --git a/citadel/imap_search.h b/citadel/imap_search.h
deleted file mode 100644 (file)
index dd0195c..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * $Id$
- *
- */
-
-void imap_search(int num_parms, char *parms[]);
-void imap_uidsearch(int num_parms, char *parms[]);
diff --git a/citadel/imap_store.c b/citadel/imap_store.c
deleted file mode 100644 (file)
index 7e16378..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * $Id$
- *
- * Implements the STORE command in IMAP.
- *
- */
-
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.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 <sys/wait.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-#include "citadel.h"
-#include "server.h"
-#include "sysdep_decls.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "room_ops.h"
-#include "user_ops.h"
-#include "policy.h"
-#include "database.h"
-#include "msgbase.h"
-#include "tools.h"
-#include "internet_addressing.h"
-#include "serv_imap.h"
-#include "imap_tools.h"
-#include "imap_fetch.h"
-#include "imap_store.h"
-#include "genstamp.h"
-
-
-
-
-
-
-/*
- * imap_do_store() calls imap_do_store_msg() to tweak the settings of
- * an individual message.
- *
- * We also implement the ".SILENT" protocol option here.  :(
- */
-void imap_do_store_msg(int seq, char *oper, unsigned int bits_to_twiddle) {
-
-
-       if (!strncasecmp(oper, "FLAGS", 5)) {
-               IMAP->flags[seq] &= IMAP_MASK_SYSTEM;
-               IMAP->flags[seq] |= bits_to_twiddle;
-       }
-       else if (!strncasecmp(oper, "+FLAGS", 6)) {
-               IMAP->flags[seq] |= bits_to_twiddle;
-       }
-       else if (!strncasecmp(oper, "-FLAGS", 6)) {
-               IMAP->flags[seq] &= (~bits_to_twiddle);
-       }
-}
-
-
-
-/*
- * imap_store() calls imap_do_store() to perform the actual bit twiddling
- * on the flags.
- */
-void imap_do_store(int num_items, char **itemlist) {
-       int i, j;
-       unsigned int bits_to_twiddle = 0;
-       char *oper;
-       char flag[32];
-       char whichflags[256];
-       char num_flags;
-       int silent = 0;
-       long *ss_msglist;
-       int num_ss = 0;
-       int last_item_twiddled = (-1);
-
-       if (num_items < 2) return;
-       oper = itemlist[0];
-       if (bmstrcasestr(oper, ".SILENT")) {
-               silent = 1;
-       }
-
-       /*
-        * ss_msglist is an array of message numbers to manipulate.  We
-        * are going to supply this array to CtdlSetSeen() later.
-        */
-       ss_msglist = malloc(IMAP->num_msgs * sizeof(long));
-       if (ss_msglist == NULL) return;
-
-       /*
-        * Ok, go ahead and parse the flags.
-        */
-       for (i=1; i<num_items; ++i) {
-               strcpy(whichflags, itemlist[i]);
-               if (whichflags[0]=='(') {
-                       safestrncpy(whichflags, &whichflags[1], 
-                               sizeof whichflags);
-               }
-               if (whichflags[strlen(whichflags)-1]==')') {
-                       whichflags[strlen(whichflags)-1]=0;
-               }
-               striplt(whichflags);
-
-               /* A client might twiddle more than one bit at a time.
-                * Note that we check for the flag names without the leading
-                * backslash because imap_parameterize() strips them out.
-                */
-               num_flags = num_tokens(whichflags, ' ');
-               for (j=0; j<num_flags; ++j) {
-                       extract_token(flag, whichflags, j, ' ', sizeof flag);
-
-                       if ((!strcasecmp(flag, "\\Deleted"))
-                          || (!strcasecmp(flag, "Deleted"))) {
-                               if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom()) {
-                                       bits_to_twiddle |= IMAP_DELETED;
-                               }
-                       }
-                       if ((!strcasecmp(flag, "\\Seen"))
-                          || (!strcasecmp(flag, "Seen"))) {
-                               bits_to_twiddle |= IMAP_SEEN;
-                       }
-                       if ((!strcasecmp(flag, "\\Answered")) 
-                          || (!strcasecmp(flag, "\\Answered"))) {
-                               bits_to_twiddle |= IMAP_ANSWERED;
-                       }
-               }
-       }
-
-       if (IMAP->num_msgs > 0) {
-               for (i = 0; i < IMAP->num_msgs; ++i) {
-                       if (IMAP->flags[i] & IMAP_SELECTED) {
-                               last_item_twiddled = i;
-
-                               ss_msglist[num_ss++] = IMAP->msgids[i];
-                               imap_do_store_msg(i, oper, bits_to_twiddle);
-
-                               if (!silent) {
-                                       cprintf("* %d FETCH (", i+1);
-                                       imap_fetch_flags(i);
-                                       cprintf(")\r\n");
-                               }
-
-                       }
-               }
-       }
-
-       /*
-        * Now manipulate the database -- all in one shot.
-        */
-       if ( (last_item_twiddled >= 0) && (num_ss > 0) ) {
-
-               if (bits_to_twiddle & IMAP_SEEN) {
-                       CtdlSetSeen(ss_msglist, num_ss,
-                               ((IMAP->flags[last_item_twiddled] & IMAP_SEEN) ? 1 : 0),
-                               ctdlsetseen_seen,
-                               NULL, NULL
-                       );
-               }
-
-               if (bits_to_twiddle & IMAP_ANSWERED) {
-                       CtdlSetSeen(ss_msglist, num_ss,
-                               ((IMAP->flags[last_item_twiddled] & IMAP_ANSWERED) ? 1 : 0),
-                               ctdlsetseen_answered,
-                               NULL, NULL
-                       );
-               }
-
-       }
-
-       free(ss_msglist);
-
-       /*
-        * The following two commands implement "instant expunge" if enabled.
-        */
-       if (config.c_instant_expunge) {
-               imap_do_expunge();
-               imap_rescan_msgids();
-       }
-
-}
-
-
-/*
- * This function is called by the main command loop.
- */
-void imap_store(int num_parms, char *parms[]) {
-       char items[1024];
-       char *itemlist[256];
-       int num_items;
-       int i;
-
-       if (num_parms < 3) {
-               cprintf("%s BAD invalid parameters\r\n", parms[0]);
-               return;
-       }
-
-       if (imap_is_message_set(parms[2])) {
-               imap_pick_range(parms[2], 0);
-       }
-       else {
-               cprintf("%s BAD invalid parameters\r\n", parms[0]);
-               return;
-       }
-
-       strcpy(items, "");
-       for (i=3; i<num_parms; ++i) {
-               strcat(items, parms[i]);
-               if (i < (num_parms-1)) strcat(items, " ");
-       }
-
-       num_items = imap_extract_data_items(itemlist, items);
-       if (num_items < 1) {
-               cprintf("%s BAD invalid data item list\r\n", parms[0]);
-               return;
-       }
-
-       imap_do_store(num_items, itemlist);
-       cprintf("%s OK STORE completed\r\n", parms[0]);
-}
-
-/*
- * This function is called by the main command loop.
- */
-void imap_uidstore(int num_parms, char *parms[]) {
-       char items[1024];
-       char *itemlist[256];
-       int num_items;
-       int i;
-
-       if (num_parms < 4) {
-               cprintf("%s BAD invalid parameters\r\n", parms[0]);
-               return;
-       }
-
-       if (imap_is_message_set(parms[3])) {
-               imap_pick_range(parms[3], 1);
-       }
-       else {
-               cprintf("%s BAD invalid parameters\r\n", parms[0]);
-               return;
-       }
-
-       strcpy(items, "");
-       for (i=4; i<num_parms; ++i) {
-               strcat(items, parms[i]);
-               if (i < (num_parms-1)) strcat(items, " ");
-       }
-
-       num_items = imap_extract_data_items(itemlist, items);
-       if (num_items < 1) {
-               cprintf("%s BAD invalid data item list\r\n", parms[0]);
-               return;
-       }
-
-       imap_do_store(num_items, itemlist);
-       cprintf("%s OK UID STORE completed\r\n", parms[0]);
-}
-
-
diff --git a/citadel/imap_store.h b/citadel/imap_store.h
deleted file mode 100644 (file)
index d3aa734..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * $Id$
- *
- */
-
-void imap_store(int num_parms, char *parms[]);
-void imap_uidstore(int num_parms, char *parms[]);
diff --git a/citadel/imap_tools.c b/citadel/imap_tools.c
deleted file mode 100644 (file)
index c127550..0000000
+++ /dev/null
@@ -1,810 +0,0 @@
-/*
- * $Id$
- *
- * Utility functions for the IMAP module.
- *
- * Note: most of the UTF7 and UTF8 handling in here was lifted from Evolution.
- *
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include "citadel.h"
-#include "sysdep_decls.h"
-#include "tools.h"
-#include "room_ops.h"
-#include "internet_addressing.h"
-#include "imap_tools.h"
-
-
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-
-/* String handling helpers */
-
-/* This code uses some pretty narsty string manipulation. To make everything
- * manageable, we use this semi-high-level string manipulation API. Strings are
- * always \0-terminated, despite the fact that we keep track of the size. */
-
-struct string {
-       char* buffer;
-       int maxsize;
-       int size;
-};
-
-static void string_init(struct string* s, char* buf, int bufsize)
-{
-       s->buffer = buf;
-       s->maxsize = bufsize-1;
-       s->size = strlen(buf);
-}
-
-static char* string_end(struct string* s)
-{
-       return s->buffer + s->size;
-}
-
-/* Append a UTF8 string of a particular length (in bytes). -1 to autocalculate. */
-
-static void string_append_sn(struct string* s, char* p, int len)
-{
-       if (len == -1)
-               len = strlen(p);
-       if ((s->size+len) > s->maxsize)
-               len = s->maxsize - s->size;
-       memcpy(s->buffer + s->size, p, len);
-       s->size += len;
-       s->buffer[s->size] = '\0';
-}
-
-/* As above, always autocalculate. */
-
-#define string_append_s(s, p) string_append_sn((s), (p), -1)
-
-/* Appends a UTF8 character --- which may make the size change by more than 1!
- * If the string overflows, the last character may become invalid. */
-
-static void string_append_c(struct string* s, int c)
-{
-       char buf[5];
-       int len = 0;
-
-       /* Don't do anything if there's no room. */
-
-       if (s->size == s->maxsize)
-               return;
-
-       if (c <= 0x7F)
-       {
-               /* This is the most common case, so we optimise it. */
-
-               s->buffer[s->size++] = c;
-               s->buffer[s->size] = 0;
-               return;
-       }
-       else if (c <= 0x7FF)
-       {
-               buf[0] = 0xC0 | (c >> 6);
-               buf[1] = 0x80 | (c & 0x3F);
-               len = 2;
-       }
-       else if (c <= 0xFFFF)
-       {
-               buf[0] = 0xE0 | (c >> 12);
-               buf[1] = 0x80 | ((c >> 6) & 0x3f);
-               buf[2] = 0x80 | (c & 0x3f);
-               len = 3;
-       }
-       else
-       {
-               buf[0] = 0xf0 | c >> 18;
-               buf[1] = 0x80 | ((c >> 12) & 0x3f);
-               buf[2] = 0x80 | ((c >> 6) & 0x3f);
-               buf[3] = 0x80 | (c & 0x3f);
-               len = 4;
-       }
-
-       string_append_sn(s, buf, len);
-}      
-
-/* Reads a UTF8 character from a char*, advancing the pointer. */
-
-int utf8_getc(char** ptr)
-{
-       unsigned char* p = (unsigned char*) *ptr;
-       unsigned char c, r;
-       int v, m;
-
-       for (;;)
-       {
-               r = *p++;
-       loop:
-               if (r < 0x80)
-               {
-                       *ptr = p;
-                       v = r;
-                       break;
-               }
-               else if (r < 0xf8)
-               {
-                       /* valid start char? (max 4 octets) */
-                       v = r;
-                       m = 0x7f80;     /* used to mask out the length bits */
-                       do {
-                               c = *p++;
-                               if ((c & 0xc0) != 0x80)
-                               {
-                                       r = c;
-                                       goto loop;
-                               }
-                               v = (v<<6) | (c & 0x3f);
-                               r<<=1;
-                               m<<=5;
-                       } while (r & 0x40);
-                       
-                       *ptr = p;
-
-                       v &= ~m;
-                       break;
-               }
-       }
-
-       return v;
-}
-
-/* IMAP name safety */
-
-/* IMAP has certain special requirements in its character set, which means we
- * have to do a fair bit of work to convert Citadel's UTF8 strings to IMAP
- * strings. The next two routines (and their data tables) do that.
- */
-
-static char *utf7_alphabet =
-       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
-
-static unsigned char utf7_rank[256] = {
-       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
-       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
-       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3E,0x3F,0xFF,0xFF,0xFF,
-       0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
-       0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,
-       0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF,
-       0xFF,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
-       0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0xFF,0xFF,0xFF,0xFF,0xFF,
-       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
-       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
-       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
-       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
-       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
-       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
-       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
-       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
-};
-
-/* Base64 helpers. */
-
-static void utf7_closeb64(struct string* out, int v, int i)
-{
-       int x;
-
-       if (i > 0)
-       {
-               x = (v << (6-i)) & 0x3F;
-               string_append_c(out, utf7_alphabet[x]);
-       }
-       string_append_c(out, '-');
-}
-
-/* Convert from a Citadel name to an IMAP-safe name. Returns the end
- * of the destination.
- */
-static char* toimap(char* destp, char* destend, char* src)
-{
-       struct string dest;
-       int state = 0;
-       int v = 0;
-       int i = 0;
-
-       *destp = 0;
-       string_init(&dest, destp, destend-destp);
-       /* lprintf(CTDL_DEBUG, "toimap %s\r\n", src); */
-
-       for (;;)
-       {
-               int c = utf8_getc(&src);
-               if (c == '\0')
-                       break;
-
-               if (c >= 0x20 && c <= 0x7e)
-               {
-                       if (state == 1)
-                       {
-                               utf7_closeb64(&dest, v, i);
-                               state = 0;
-                               i = 0;
-                       }
-
-                       switch (c)
-                       {
-                               case '&':
-                                       string_append_sn(&dest, "&-", 2);
-                                       break;
-
-                               case '/':
-                                       /* Citadel extension: / becomes |, because /
-                                        * isn't valid as part of an IMAP name. */
-
-                                       c = '|';
-                                       goto defaultcase;
-
-                               case '\\':
-                                       /* Citadel extension: backslashes mark folder
-                                        * seperators in the IMAP subfolder emulation
-                                        * hack. We turn them into / characters,
-                                        * *except* if it's the last character in the
-                                        * string. */
-
-                                       if (*src != '\0')
-                                               c = '/';
-                                       /* fall through */
-
-                               default:
-                               defaultcase:
-                                       string_append_c(&dest, c);
-                       }
-               }
-               else
-               {
-                       if (state == 0)
-                       {
-                               string_append_c(&dest, '&');
-                               state = 1;
-                       }
-                       v = (v << 16) | c;
-                       i += 16;
-                       while (i >= 6)
-                       {
-                               int x = (v >> (i-6)) & 0x3f;
-                               string_append_c(&dest, utf7_alphabet[x]);
-                               i -= 6;
-                       }
-               }
-       }
-
-       if (state == 1)
-               utf7_closeb64(&dest, v, i);
-       /* lprintf(CTDL_DEBUG, "    -> %s\r\n", destp); */
-       return string_end(&dest);
-}
-
-/* Convert from an IMAP-safe name back into a Citadel name. Returns the end of the destination. */
-
-static int cfrommap(int c);
-static char* fromimap(char* destp, char* destend, char* src)
-{
-       struct string dest;
-       unsigned char *p = (unsigned char*) src;
-       int v = 0;
-       int i = 0;
-       int state = 0;
-       int c;
-
-       *destp = 0;
-       string_init(&dest, destp, destend-destp);
-       /* lprintf(CTDL_DEBUG, "fromimap %s\r\n", src); */
-
-       do {
-               c = *p++;
-               switch (state)
-               {
-                       case 0:
-                               /* US-ASCII characters. */
-                               
-                               if (c == '&')
-                                       state = 1;
-                               else
-                                       string_append_c(&dest, cfrommap(c));
-                               break;
-
-                       case 1:
-                               if (c == '-')
-                               {
-                                       string_append_c(&dest, '&');
-                                       state = 0;
-                               }
-                               else if (utf7_rank[c] != 0xff)
-                               {
-                                       v = utf7_rank[c];
-                                       i = 6;
-                                       state = 2;
-                               }
-                               else
-                               {
-                                       /* invalid char */
-                                       string_append_sn(&dest, "&-", 2);
-                                       state = 0;
-                               }
-                               break;
-                               
-                       case 2:
-                               if (c == '-')
-                                       state = 0;
-                               else if (utf7_rank[c] != 0xFF)
-                               {
-                                       v = (v<<6) | utf7_rank[c];
-                                       i += 6;
-                                       if (i >= 16)
-                                       {
-                                               int x = (v >> (i-16)) & 0xFFFF;
-                                               string_append_c(&dest, cfrommap(x));
-                                               i -= 16;
-                                       }
-                               }
-                               else
-                               {
-                                       string_append_c(&dest, cfrommap(c));
-                                       state = 0;
-                               }
-                               break;
-                       }
-       } while (c != '\0');
-
-       /* lprintf(CTDL_DEBUG, "      -> %s\r\n", destp); */
-       return string_end(&dest);
-}
-
-/* Undoes the special character conversion. */
-
-static int cfrommap(int c)
-{
-       switch (c)
-       {
-               case '|':       return '/';
-               case '/':       return '\\';
-       }
-       return c;               
-}
-
-/* Output a string to the IMAP client, either as a literal or quoted.
- * (We do a literal if it has any double-quotes or backslashes.) */
-
-void imap_strout(char *buf)
-{
-       int i;
-       int is_literal = 0;
-       int len;
-
-       if (buf == NULL) {      /* yeah, we handle this */
-               cprintf("NIL");
-               return;
-       }
-
-       len = strlen(buf);
-       for (i = 0; i < len; ++i) {
-               if ((buf[i] == '\"') || (buf[i] == '\\'))
-                       is_literal = 1;
-       }
-
-       if (is_literal) {
-               cprintf("{%ld}\r\n%s", (long)strlen(buf), buf);
-       } else {
-               cprintf("\"%s\"", buf);
-       }
-}
-
-/* Break a command down into tokens, unquoting any escaped characters. */
-
-int imap_parameterize(char** args, char* in)
-{
-       char* out = in;
-       int num = 0;
-
-       for (;;)
-       {
-               /* Skip whitespace. */
-
-               while (isspace(*in))
-                       in++;
-               if (*in == 0)
-                       break;
-
-               /* Found the start of a token. */
-               
-               args[num++] = out;
-
-               /* Read in the token. */
-
-               for (;;)
-               {
-                       int c = *in++;
-                       if (isspace(c))
-                               break;
-                       
-                       if (c == '\"')
-                       {
-                               /* Found a quoted section. */
-
-                               for (;;)
-                               {
-                                       c = *in++;
-                                       if (c == '\"')
-                                               break;
-                                       else if (c == '\\')
-                                               c = *in++;
-
-                                       *out++ = c;
-                                       if (c == 0)
-                                               return num;
-                               }
-                       }
-                       else if (c == '\\')
-                       {
-                               c = *in++;
-                               *out++ = c;
-                       }
-                       else
-                               *out++ = c;
-
-                       if (c == 0)
-                               return num;
-               }
-               *out++ = '\0';
-       }
-
-       return num;
-}
-
-/* Convert a struct ctdlroom to an IMAP-compatible mailbox name. */
-
-void imap_mailboxname(char *buf, int bufsize, struct ctdlroom *qrbuf)
-{
-       char* bufend = buf+bufsize;
-       struct floor *fl;
-       char* p = buf;
-
-       /* For mailboxes, just do it straight.
-        * Do the Cyrus-compatible thing: all private folders are
-        * subfolders of INBOX. */
-
-       if (qrbuf->QRflags & QR_MAILBOX)
-       {
-               if (strcasecmp(qrbuf->QRname+11, MAILROOM) == 0)
-                       p = toimap(p, bufend, "INBOX");
-               else
-               {
-                       p = toimap(p, bufend, "INBOX");
-                       if (p < bufend)
-                               *p++ = '/';
-                       p = toimap(p, bufend, qrbuf->QRname+11);
-               }
-       }
-       else
-       {
-               /* Otherwise, prefix the floor name as a "public folders" moniker. */
-
-               fl = cgetfloor(qrbuf->QRfloor);
-               p = toimap(p, bufend, fl->f_name);
-               if (p < bufend)
-                       *p++ = '/';
-               p = toimap(p, bufend, qrbuf->QRname);
-       }
-}
-
-/*
- * Convert an inputted folder name to our best guess as to what an equivalent
- * room name should be.
- *
- * If an error occurs, it returns -1.  Otherwise...
- *
- * The lower eight bits of the return value are the floor number on which the
- * room most likely resides.   The upper eight bits may contain flags,
- * including IR_MAILBOX if we're dealing with a personal room.
- *
- */
-
-int imap_roomname(char *rbuf, int bufsize, char *foldername)
-{
-       int levels;
-       char floorname[256];
-       char roomname[ROOMNAMELEN];
-       int i;
-       struct floor *fl;
-       int ret = (-1);
-
-       if (foldername == NULL)
-               return(-1);
-
-       /* Unmunge the entire string into the output buffer. */
-
-       fromimap(rbuf, rbuf+bufsize, foldername);
-
-       /* Is this an IMAP inbox? */
-
-       if (strncasecmp(rbuf, "INBOX", 5) == 0)
-       {
-               if (rbuf[5] == 0)
-               {
-                       /* It's the system inbox. */
-
-                       safestrncpy(rbuf, MAILROOM, bufsize);
-                       ret = (0 | IR_MAILBOX);
-                       goto exit;
-               }
-               else if (rbuf[5] == FDELIM)
-               {
-                       /* It's another personal mail folder. */
-
-                       safestrncpy(rbuf, rbuf+6, bufsize);
-                       ret = (0 | IR_MAILBOX);
-                       goto exit;
-               }
-
-               /* If we get here, the folder just happens to start with INBOX
-                * --- fall through. */
-       }
-
-       /* Is this a multi-level room name? */
-
-       levels = num_tokens(rbuf, FDELIM);
-       if (levels > 1)
-       {
-               /* Extract the main room name. */
-               
-               extract_token(floorname, rbuf, 0, FDELIM, sizeof floorname);
-               strcpy(roomname, &rbuf[strlen(floorname)+1]);
-
-               /* Try and find it on any floor. */
-               
-               for (i = 0; i < MAXFLOORS; ++i)
-               {
-                       fl = cgetfloor(i);
-                       if (fl->f_flags & F_INUSE)
-                       {
-                               if (strcasecmp(floorname, fl->f_name) == 0)
-                               {
-                                       /* Got it! */
-
-                                       safestrncpy(rbuf, roomname, bufsize);
-                                       ret = i;
-                                       goto exit;
-                               }
-                       }
-               }
-       }
-
-       /* Meh. It's either not a multi-level room name, or else we
-        * couldn't find it.
-        */
-       ret = (0 | IR_MAILBOX);
-
-exit:
-       lprintf(CTDL_DEBUG, "(That translates to \"%s\")\n", rbuf);
-       return(ret);
-}
-
-/*
- * Output a struct internet_address_list in the form an IMAP client wants
- */
-void imap_ial_out(struct internet_address_list *ialist)
-{
-       struct internet_address_list *iptr;
-
-       if (ialist == NULL) {
-               cprintf("NIL");
-               return;
-       }
-       cprintf("(");
-
-       for (iptr = ialist; iptr != NULL; iptr = iptr->next) {
-               cprintf("(");
-               imap_strout(iptr->ial_name);
-               cprintf(" NIL ");
-               imap_strout(iptr->ial_user);
-               cprintf(" ");
-               imap_strout(iptr->ial_node);
-               cprintf(")");
-       }
-
-       cprintf(")");
-}
-
-
-
-/*
- * Determine whether the supplied string is a valid message set.
- * If the string contains only numbers, colons, commas, and asterisks,
- * return 1 for a valid message set.  If any other character is found, 
- * return 0.
- */
-int imap_is_message_set(char *buf)
-{
-       int i;
-
-       if (buf == NULL)
-               return (0);     /* stupidity checks */
-       if (strlen(buf) == 0)
-               return (0);
-
-       if (!strcasecmp(buf, "ALL"))
-               return (1);     /* macro?  why?  */
-
-       for (i = 0; i < strlen(buf); ++i) {     /* now start the scan */
-               if (
-                          (!isdigit(buf[i]))
-                          && (buf[i] != ':')
-                          && (buf[i] != ',')
-                          && (buf[i] != '*')
-                   )
-                       return (0);
-       }
-
-       return (1);             /* looks like we're good */
-}
-
-
-/*
- * imap_match.c, based on wildmat.c from INN
- * hacked for Citadel/IMAP by Daniel Malament
- */
-
-/* note: not all return statements use these; don't change them */
-#define WILDMAT_TRUE   1
-#define WILDMAT_FALSE  0
-#define WILDMAT_ABORT  -1
-#define WILDMAT_DELIM  '/'
-
-/*
- * Match text and p, return TRUE, FALSE, or ABORT.
- */
-static int do_imap_match(const char *supplied_text, const char *supplied_p)
-{
-       int matched, i;
-       char lcase_text[SIZ], lcase_p[SIZ];
-       char *text = lcase_text;
-       char *p = lcase_p;
-
-       /* Copy both strings and lowercase them, in order to
-        * make this entire operation case-insensitive.
-        */
-       for (i=0; i<=strlen(supplied_text); ++i)
-               lcase_text[i] = tolower(supplied_text[i]);
-       for (i=0; i<=strlen(supplied_p); ++i)
-               p[i] = tolower(supplied_p[i]);
-
-       /* Start matching */
-       for (; *p; text++, p++) {
-               if ((*text == '\0') && (*p != '*') && (*p != '%')) {
-                       return WILDMAT_ABORT;
-               }
-               switch (*p) {
-               default:
-                       if (*text != *p) {
-                               return WILDMAT_FALSE;
-                       }
-                       continue;
-               case '*':
-star:
-                       while (++p, ((*p == '*') || (*p == '%'))) {
-                               /* Consecutive stars or %'s act
-                                * just like one star.
-                                */
-                               continue;
-                       }
-                       if (*p == '\0') {
-                               /* Trailing star matches everything. */
-                               return WILDMAT_TRUE;
-                       }
-                       while (*text) {
-                               if ((matched = do_imap_match(text++, p))
-                                  != WILDMAT_FALSE) {
-                                       return matched;
-                               }
-                       }
-                       return WILDMAT_ABORT;
-               case '%':
-                       while (++p, ((*p == '*') || (*p == '%'))) {
-                               /* Consecutive %'s act just like one, but even
-                                * a single star makes the sequence act like
-                                * one star, instead.
-                                */
-                               if (*p == '*') {
-                                       goto star;
-                               }
-                               continue;
-                       }
-                       if (*p == '\0') {
-                               /*
-                                * Trailing % matches everything
-                                * without a delimiter.
-                                */
-                               while (*text) {
-                                       if (*text == WILDMAT_DELIM) {
-                                               return WILDMAT_FALSE;
-                                       }
-                                       text++;
-                               }
-                               return WILDMAT_TRUE;
-                       }
-                       while (*text && (*(text - 1) != WILDMAT_DELIM)) {
-                               if ((matched = do_imap_match(text++, p))
-                                  != WILDMAT_FALSE) {
-                                       return matched;
-                               }
-                       }
-                       return WILDMAT_ABORT;
-               }
-       }
-
-       return (*text == '\0');
-}
-
-
-
-/*
- * Support function for mailbox pattern name matching in LIST and LSUB
- * Returns nonzero if the supplied mailbox name matches the supplied pattern.
- */
-int imap_mailbox_matches_pattern(char *pattern, char *mailboxname)
-{
-       /* handle just-star case quickly */
-       if ((pattern[0] == '*') && (pattern[1] == '\0')) {
-               return WILDMAT_TRUE;
-       }
-       return (do_imap_match(mailboxname, pattern) == WILDMAT_TRUE);
-}
-
-
-
-/*
- * Compare an IMAP date string (date only, no time) to the date found in
- * a Unix timestamp.
- */
-int imap_datecmp(char *datestr, time_t msgtime) {
-       char daystr[256];
-       char monthstr[256];
-       char yearstr[256];
-       int i;
-       int day, month, year;
-       int msgday, msgmonth, msgyear;
-       struct tm msgtm;
-
-       if (datestr == NULL) return(0);
-
-       /* Expecting a date in the form dd-Mmm-yyyy */
-       extract_token(daystr, datestr, 0, '-', sizeof daystr);
-       extract_token(monthstr, datestr, 1, '-', sizeof monthstr);
-       extract_token(yearstr, datestr, 2, '-', sizeof yearstr);
-
-       day = atoi(daystr);
-       year = atoi(yearstr);
-       month = 0;
-       for (i=0; i<12; ++i) {
-               if (!strcasecmp(monthstr, ascmonths[i])) {
-                       month = i;
-               }
-       }
-
-       /* Extract day/month/year from message timestamp */
-       localtime_r(&msgtime, &msgtm);
-       msgday = msgtm.tm_mday;
-       msgmonth = msgtm.tm_mon;
-       msgyear = msgtm.tm_year + 1900;
-
-       /* Now start comparing */
-
-       if (year < msgyear) return(+1);
-       if (year > msgyear) return(-1);
-
-       if (month < msgmonth) return(+1);
-       if (month > msgmonth) return(-1);
-
-       if (day < msgday) return(+1);
-       if (day > msgday) return(-1);
-
-       return(0);
-}
-
index ed8e7c9c75499af297fb106c3e653ac68780c97a..a839ffbabc7cdeafdab05a6db17f64f9e8f4c5ae 100644 (file)
@@ -62,4 +62,11 @@ void CtdlRegisterFixedOutputHook(char *content_type,
 );
 void CtdlUnRegisterFixedOutputHook(char *content_type);
 
+void CtdlRegisterMaintenanceThread(char *name, void *(*thread_proc) (void *arg));
+
+/* TODODRW: This needs to be changed into a hook type interface
+ * for now we have this horrible hack
+ */
+void CtdlModuleStartCryptoMsgs(char *ok_response, char *nosup_response, char *error_response);
+
 #endif /* CTDL_MODULE_H */
index 4d18623c57e838f10145abfd40a8862842608dc5..9a708872273f57d29dd04baf439cb06ebdadf6df 100644 (file)
@@ -47,7 +47,7 @@
 #include "genstamp.h"
 #include "internet_addressing.h"
 #include "vcard.h"
-#include "serv_vcard.h"
+#include "serv_vcard.h"        /* Needed for vcard_getuser and extract_inet_email_addrs */
 #include "journaling.h"
 
 struct jnlq *jnlq = NULL;      /* journal queue */
diff --git a/citadel/modules/crypto/serv_crypto.h b/citadel/modules/crypto/serv_crypto.h
new file mode 100644 (file)
index 0000000..5017482
--- /dev/null
@@ -0,0 +1,26 @@
+/* $Id$ */
+
+/*
+ * Number of days for which self-signed certs are valid.
+ */
+#define SIGN_DAYS      3650    /* Ten years */
+
+/* Shared Diffie-Hellman parameters */
+#define DH_P           "1A74527AEE4EE2568E85D4FB2E65E18C9394B9C80C42507D7A6A0DBE9A9A54B05A9A96800C34C7AA5297095B69C88901EEFD127F969DCA26A54C0E0B5C5473EBAEB00957D2633ECAE3835775425DE66C0DE6D024DBB17445E06E6B0C78415E589B8814F08531D02FD43778451E7685541079CFFB79EF0D26EFEEBBB69D1E80383"
+#define DH_G           "2"
+#define DH_L           1024
+#define CIT_CIPHERS    "ALL:RC4+RSA:+SSLv2:+TLSv1:!MD5:@STRENGTH"      /* see ciphers(1) */
+
+#ifdef HAVE_OPENSSL
+void destruct_ssl(void);
+void init_ssl(void);
+void client_write_ssl (char *buf, int nbytes);
+int client_read_ssl (char *buf, int bytes, int timeout);
+void cmd_stls(char *params);
+void cmd_gtls(char *params);
+void endtls(void);
+void ssl_lock(int mode, int n, const char *file, int line);
+void CtdlStartTLS(char *ok_response, char *nosup_response, char *error_response);
+extern SSL_CTX *ssl_ctx;  
+
+#endif
index 66b46570d54153bd0b202af2b454ca053ceb2b94..d6f2c9b6fed859ef36ec370e342baa183ca99088 100644 (file)
@@ -60,7 +60,7 @@
 #include "msgbase.h"
 #include "user_ops.h"
 #include "control.h"
-#include "serv_network.h"
+#include "serv_network.h"      /* Needed for defenition of UseTable */
 #include "tools.h"
 
 
index f6e2c672a8902f0a07be23f3a4c18b7a39e27fa9..b5753fbf63c95e4e156082f36da568d6d8ea9485 100644 (file)
@@ -473,6 +473,7 @@ CTDL_MODULE_INIT(fulltext)
 {
        initialize_ft_cache();
        CtdlRegisterProtoHook(cmd_srch, "SRCH", "Full text search");
+       CtdlRegisterMaintenanceThread ("indexer", indexer_thread);
 
        /* return our Subversion id for the Log */
        return "$Id$";
diff --git a/citadel/modules/imap/imap_acl.c b/citadel/modules/imap/imap_acl.c
new file mode 100644 (file)
index 0000000..1632532
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * $Id$
+ *
+ * Functions which implement RFC2086 (and maybe RFC4314) (IMAP ACL extension)
+ *
+ */
+
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.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 <sys/wait.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include "citadel.h"
+#include "server.h"
+#include "sysdep_decls.h"
+#include "citserver.h"
+#include "support.h"
+#include "config.h"
+#include "room_ops.h"
+#include "user_ops.h"
+#include "policy.h"
+#include "database.h"
+#include "msgbase.h"
+#include "tools.h"
+#include "internet_addressing.h"
+#include "serv_imap.h"
+#include "imap_tools.h"
+#include "imap_fetch.h"
+#include "imap_misc.h"
+#include "genstamp.h"
+
+
+
+/*
+ * Implements the SETACL command.
+ */
+void imap_setacl(int num_parms, char *parms[]) {
+
+       cprintf("%s BAD not yet implemented FIXME\r\n", parms[0]);
+       return;
+}
+
+
+/*
+ * Implements the DELETEACL command.
+ */
+void imap_deleteacl(int num_parms, char *parms[]) {
+
+       cprintf("%s BAD not yet implemented FIXME\r\n", parms[0]);
+       return;
+}
+
+
+/*
+ * Given the bits returned by CtdlRoomAccess(), populate a string buffer
+ * with IMAP ACL format flags.   This code is common to GETACL and MYRIGHTS.
+ */
+void imap_acl_flags(char *rights, int ra)
+{
+       strcpy(rights, "");
+
+       /* l - lookup (mailbox is visible to LIST/LSUB commands)
+        * r - read (SELECT the mailbox, perform STATUS et al)
+        * s - keep seen/unseen information across sessions (STORE SEEN flag)
+        */
+       if (    (ra & UA_KNOWN)                                 /* known rooms */
+          ||   ((ra & UA_GOTOALLOWED) && (ra & UA_ZAPPED))     /* zapped rooms */
+          ) {
+               strcat(rights, "l");
+               strcat(rights, "r");
+               strcat(rights, "s");
+
+               /* Only output the remaining flags if the room is known */
+
+               /* w - write (set or clear flags other than SEEN or DELETED, not supported in Citadel */
+
+               /* i - insert (perform APPEND, COPY into mailbox) */
+               /* p - post (send mail to submission address for mailbox - not enforced) */
+               /* c - create (CREATE new sub-mailboxes) */
+               if (ra & UA_POSTALLOWED) {
+                       strcat(rights, "i");
+                       strcat(rights, "p");
+                       strcat(rights, "c");
+               }
+
+               /* d - delete messages (STORE DELETED flag, perform EXPUNGE) */
+               if (ra & UA_DELETEALLOWED) {
+                       strcat(rights, "d");
+               }
+
+               /* a - administer (perform SETACL/DELETEACL/GETACL/LISTRIGHTS) */
+               if (ra & UA_ADMINALLOWED) {
+                       /*
+                        * This is the correct place to put the "a" flag.  We are leaving
+                        * it commented out for now, because it implies that we could
+                        * perform any of SETACL/DELETEACL/GETACL/LISTRIGHTS.  Since these
+                        * commands are not yet implemented, omitting the flag should
+                        * theoretically prevent compliant clients from attempting to
+                        * perform them.
+                        */
+                       /* strcat(rights, "a"); * commented out */
+               }
+       }
+}
+
+
+/*
+ * Implements the GETACL command.
+ */
+void imap_getacl(int num_parms, char *parms[]) {
+       char roomname[ROOMNAMELEN];
+       char savedroom[ROOMNAMELEN];
+       int msgs, new;
+       int ret;
+       struct ctdluser temp;
+       struct cdbdata *cdbus;
+       int ra;
+       char rights[32];
+
+       if (num_parms != 3) {
+               cprintf("%s BAD usage error\r\n", parms[0]);
+               return;
+       }
+
+       /*
+        * Search for the specified room or folder
+        */
+       ret = imap_grabroom(roomname, parms[2], 0);
+       if (ret != 0) {
+               cprintf("%s NO Invalid mailbox name or access denied\r\n",
+                       parms[0]);
+               return;
+       }
+
+       /*
+        * usergoto() formally takes us to the desired room.  (If another
+        * folder is selected, save its name so we can return there!!!!!)
+        */
+       if (IMAP->selected) {
+               strcpy(savedroom, CC->room.QRname);
+       }
+       usergoto(roomname, 0, 0, &msgs, &new);
+
+       cprintf("* ACL");
+       cprintf(" ");
+       imap_strout(parms[2]);
+
+       /*
+        * Traverse the userlist
+        */
+       cdb_rewind(CDB_USERS);
+       while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
+               memset(&temp, 0, sizeof temp);
+               memcpy(&temp, cdbus->ptr, sizeof temp);
+               cdb_free(cdbus);
+
+               CtdlRoomAccess(&CC->room, &temp, &ra, NULL);
+               if (strlen(temp.fullname) > 0) {
+                       imap_acl_flags(rights, ra);
+                       if (strlen(rights) > 0) {
+                               cprintf(" ");
+                               imap_strout(temp.fullname);
+                               cprintf(" %s", rights);
+                       }
+               }
+       }
+
+       cprintf("\r\n");
+
+       /*
+        * If another folder is selected, go back to that room so we can resume
+        * our happy day without violent explosions.
+        */
+       if (IMAP->selected) {
+               usergoto(savedroom, 0, 0, &msgs, &new);
+       }
+
+       cprintf("%s OK GETACL completed\r\n", parms[0]);
+}
+
+
+/*
+ * Implements the LISTRIGHTS command.
+ */
+void imap_listrights(int num_parms, char *parms[]) {
+       char roomname[ROOMNAMELEN];
+       char savedroom[ROOMNAMELEN];
+       int msgs, new;
+       int ret;
+       struct recptypes *valid;
+       struct ctdluser temp;
+
+       if (num_parms != 4) {
+               cprintf("%s BAD usage error\r\n", parms[0]);
+               return;
+       }
+
+       /*
+        * Search for the specified room/folder
+        */
+       ret = imap_grabroom(roomname, parms[2], 0);
+       if (ret != 0) {
+               cprintf("%s NO Invalid mailbox name or access denied\r\n",
+                       parms[0]);
+               return;
+       }
+
+       /*
+        * Search for the specified user
+        */
+       ret = (-1);
+       valid = validate_recipients(parms[3]);
+       if (valid != NULL) {
+               if (valid->num_local == 1) {
+                       ret = getuser(&temp, valid->recp_local);
+               }
+               free_recipients(valid);
+       }
+       if (ret != 0) {
+               cprintf("%s NO Invalid user name or access denied\r\n",
+                       parms[0]);
+               return;
+       }
+
+       /*
+        * usergoto() formally takes us to the desired room.  (If another
+        * folder is selected, save its name so we can return there!!!!!)
+        */
+       if (IMAP->selected) {
+               strcpy(savedroom, CC->room.QRname);
+       }
+       usergoto(roomname, 0, 0, &msgs, &new);
+
+
+       /*
+        * Now output the list of rights
+        */
+       cprintf("* LISTRIGHTS ");
+       imap_strout(parms[2]);
+       cprintf(" ");
+       imap_strout(parms[3]);
+       cprintf(" ");
+       imap_strout("");                /* FIXME ... do something here */
+       cprintf("\r\n");
+
+
+       /*
+        * If another folder is selected, go back to that room so we can resume
+        * our happy day without violent explosions.
+        */
+       if (IMAP->selected) {
+               usergoto(savedroom, 0, 0, &msgs, &new);
+       }
+
+       cprintf("%s OK LISTRIGHTS completed\r\n", parms[0]);
+       return;
+}
+
+
+/*
+ * Implements the MYRIGHTS command.
+ */
+void imap_myrights(int num_parms, char *parms[]) {
+       char roomname[ROOMNAMELEN];
+       char savedroom[ROOMNAMELEN];
+       int msgs, new;
+       int ret;
+       int ra;
+       char rights[32];
+
+       if (num_parms != 3) {
+               cprintf("%s BAD usage error\r\n", parms[0]);
+               return;
+       }
+
+       ret = imap_grabroom(roomname, parms[2], 0);
+       if (ret != 0) {
+               cprintf("%s NO Invalid mailbox name or access denied\r\n",
+                       parms[0]);
+               return;
+       }
+
+       /*
+        * usergoto() formally takes us to the desired room.  (If another
+        * folder is selected, save its name so we can return there!!!!!)
+        */
+       if (IMAP->selected) {
+               strcpy(savedroom, CC->room.QRname);
+       }
+       usergoto(roomname, 0, 0, &msgs, &new);
+
+       CtdlRoomAccess(&CC->room, &CC->user, &ra, NULL);
+       imap_acl_flags(rights, ra);
+
+       cprintf("* MYRIGHTS ");
+       imap_strout(parms[2]);
+       cprintf(" %s\r\n", rights);
+
+       /*
+        * If a different folder was previously selected, return there now.
+        */
+       if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
+               usergoto(savedroom, 0, 0, &msgs, &new);
+       }
+
+       cprintf("%s OK MYRIGHTS completed\r\n", parms[0]);
+       return;
+}
+
+
diff --git a/citadel/modules/imap/imap_acl.h b/citadel/modules/imap/imap_acl.h
new file mode 100644 (file)
index 0000000..bfc2c14
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * $Id$
+ *
+ */
+
+void imap_setacl(int num_parms, char *parms[]);
+void imap_deleteacl(int num_parms, char *parms[]);
+void imap_getacl(int num_parms, char *parms[]);
+void imap_listrights(int num_parms, char *parms[]);
+void imap_myrights(int num_parms, char *parms[]);
+
diff --git a/citadel/modules/imap/imap_fetch.c b/citadel/modules/imap/imap_fetch.c
new file mode 100644 (file)
index 0000000..66d5b44
--- /dev/null
@@ -0,0 +1,1331 @@
+/*
+ * $Id$
+ *
+ * Implements the FETCH command in IMAP.
+ * This is a good example of the protocol's gratuitous complexity.
+ *
+ */
+
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.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 <sys/wait.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include "citadel.h"
+#include "server.h"
+#include "sysdep_decls.h"
+#include "citserver.h"
+#include "support.h"
+#include "config.h"
+#include "room_ops.h"
+#include "user_ops.h"
+#include "policy.h"
+#include "database.h"
+#include "msgbase.h"
+#include "tools.h"
+#include "internet_addressing.h"
+#include "mime_parser.h"
+#include "serv_imap.h"
+#include "imap_tools.h"
+#include "imap_fetch.h"
+#include "genstamp.h"
+
+
+
+/*
+ * Individual field functions for imap_do_fetch_msg() ...
+ */
+
+void imap_fetch_uid(int seq) {
+       cprintf("UID %ld", IMAP->msgids[seq-1]);
+}
+
+void imap_fetch_flags(int seq) {
+       int num_flags_printed = 0;
+       cprintf("FLAGS (");
+       if (IMAP->flags[seq] & IMAP_DELETED) {
+               if (num_flags_printed > 0) cprintf(" ");
+               cprintf("\\Deleted");
+               ++num_flags_printed;
+       }
+       if (IMAP->flags[seq] & IMAP_SEEN) {
+               if (num_flags_printed > 0) cprintf(" ");
+               cprintf("\\Seen");
+               ++num_flags_printed;
+       }
+       if (IMAP->flags[seq] & IMAP_ANSWERED) {
+               if (num_flags_printed > 0) cprintf(" ");
+               cprintf("\\Answered");
+               ++num_flags_printed;
+       }
+       if (IMAP->flags[seq] & IMAP_RECENT) {
+               if (num_flags_printed > 0) cprintf(" ");
+               cprintf("\\Recent");
+               ++num_flags_printed;
+       }
+       cprintf(")");
+}
+
+void imap_fetch_internaldate(struct CtdlMessage *msg) {
+       char buf[SIZ];
+       time_t msgdate;
+
+       if (!msg) return;
+       if (msg->cm_fields['T'] != NULL) {
+               msgdate = atol(msg->cm_fields['T']);
+       }
+       else {
+               msgdate = time(NULL);
+       }
+
+       datestring(buf, sizeof buf, msgdate, DATESTRING_IMAP);
+       cprintf("INTERNALDATE \"%s\"", buf);
+}
+
+
+/*
+ * Fetch RFC822-formatted messages.
+ *
+ * 'whichfmt' should be set to one of:
+ *     "RFC822"        entire message
+ *     "RFC822.HEADER" headers only (with trailing blank line)
+ *     "RFC822.SIZE"   size of translated message
+ *     "RFC822.TEXT"   body only (without leading blank line)
+ */
+void imap_fetch_rfc822(long msgnum, char *whichfmt) {
+       char buf[SIZ];
+       char *ptr = NULL;
+       size_t headers_size, text_size, total_size;
+       size_t bytes_to_send = 0;
+       struct MetaData smi;
+       int need_to_rewrite_metadata = 0;
+       int need_body = 0;
+
+       /* Determine whether this particular fetch operation requires
+        * us to fetch the message body from disk.  If not, we can save
+        * on some disk operations...
+        */
+       if ( (!strcasecmp(whichfmt, "RFC822"))
+          || (!strcasecmp(whichfmt, "RFC822.TEXT")) ) {
+               need_body = 1;
+       }
+
+       /* If this is an RFC822.SIZE fetch, first look in the message's
+        * metadata record to see if we've saved that information.
+        */
+       if (!strcasecmp(whichfmt, "RFC822.SIZE")) {
+               GetMetaData(&smi, msgnum);
+               if (smi.meta_rfc822_length > 0L) {
+                       cprintf("RFC822.SIZE %ld", smi.meta_rfc822_length);
+                       return;
+               }
+               need_to_rewrite_metadata = 1;
+               need_body = 1;
+       }
+       
+       /* Cache the most recent RFC822 FETCH because some clients like to
+        * fetch in pieces, and we don't want to have to go back to the
+        * message store for each piece.  We also burn the cache if the
+        * client requests something that involves reading the message
+        * body, but we haven't fetched the body yet.
+        */
+       if ((IMAP->cached_rfc822_data != NULL)
+          && (IMAP->cached_rfc822_msgnum == msgnum)
+          && (IMAP->cached_rfc822_withbody || (!need_body)) ) {
+               /* Good to go! */
+       }
+       else if (IMAP->cached_rfc822_data != NULL) {
+               /* Some other message is cached -- free it */
+               free(IMAP->cached_rfc822_data);
+               IMAP->cached_rfc822_data = NULL;
+               IMAP->cached_rfc822_msgnum = (-1);
+               IMAP->cached_rfc822_len = 0;
+       }
+
+       /* At this point, we now can fetch and convert the message iff it's not
+        * the one we had cached.
+        */
+       if (IMAP->cached_rfc822_data == NULL) {
+               /*
+                * Load the message into memory for translation & measurement
+                */
+               CC->redirect_buffer = malloc(SIZ);
+               CC->redirect_len = 0;
+               CC->redirect_alloc = SIZ;
+               CtdlOutputMsg(msgnum, MT_RFC822,
+                       (need_body ? HEADERS_ALL : HEADERS_ONLY),
+                       0, 1, NULL);
+               if (!need_body) cprintf("\r\n");        /* extra trailing newline */
+               IMAP->cached_rfc822_data = CC->redirect_buffer;
+               IMAP->cached_rfc822_len = CC->redirect_len;
+               IMAP->cached_rfc822_msgnum = msgnum;
+               IMAP->cached_rfc822_withbody = need_body;
+               CC->redirect_buffer = NULL;
+               CC->redirect_len = 0;
+               CC->redirect_alloc = 0;
+               if ( (need_to_rewrite_metadata) && (IMAP->cached_rfc822_len > 0) ) {
+                       smi.meta_rfc822_length = (long)IMAP->cached_rfc822_len;
+                       PutMetaData(&smi);
+               }
+       }
+
+       /*
+        * Now figure out where the headers/text break is.  IMAP considers the
+        * intervening blank line to be part of the headers, not the text.
+        */
+       headers_size = 0;
+       text_size = 0;
+       total_size = 0;
+
+       if (need_body) {
+               ptr = IMAP->cached_rfc822_data;
+               do {
+                       ptr = memreadline(ptr, buf, sizeof buf);
+                       if (*ptr != 0) {
+                               striplt(buf);
+                               if (strlen(buf) == 0) {
+                                       headers_size = ptr - IMAP->cached_rfc822_data;
+                               }
+                       }
+               } while ( (headers_size == 0) && (*ptr != 0) );
+
+               total_size = IMAP->cached_rfc822_len;
+               text_size = total_size - headers_size;
+       }
+       else {
+               headers_size = IMAP->cached_rfc822_len;
+               total_size = IMAP->cached_rfc822_len;
+               text_size = 0;
+       }
+
+       lprintf(CTDL_DEBUG, "RFC822: headers=%d, text=%d, total=%d\n",
+               headers_size, text_size, total_size);
+
+       if (!strcasecmp(whichfmt, "RFC822.SIZE")) {
+               cprintf("RFC822.SIZE %d", total_size);
+               return;
+       }
+
+       else if (!strcasecmp(whichfmt, "RFC822")) {
+               ptr = IMAP->cached_rfc822_data;
+               bytes_to_send = total_size;
+       }
+
+       else if (!strcasecmp(whichfmt, "RFC822.HEADER")) {
+               ptr = IMAP->cached_rfc822_data;
+               bytes_to_send = headers_size;
+       }
+
+       else if (!strcasecmp(whichfmt, "RFC822.TEXT")) {
+               ptr = &IMAP->cached_rfc822_data[headers_size];
+               bytes_to_send = text_size;
+       }
+
+       cprintf("%s {%d}\r\n", whichfmt, bytes_to_send);
+       client_write(ptr, bytes_to_send);
+}
+
+
+
+/*
+ * Load a specific part of a message into the temp file to be output to a
+ * client.  FIXME we can handle parts like "2" and "2.1" and even "2.MIME"
+ * but we still can't handle "2.HEADER" (which might not be a problem).
+ *
+ * Note: mime_parser() was called with dont_decode set to 1, so we have the
+ * luxury of simply spewing without having to re-encode.
+ */
+void imap_load_part(char *name, char *filename, char *partnum, char *disp,
+                   void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
+                   void *cbuserdata)
+{
+       char mbuf2[SIZ];
+       char *desired_section;
+
+       desired_section = (char *)cbuserdata;
+
+       if (!strcasecmp(partnum, desired_section)) {
+               client_write(content, length);
+       }
+
+       snprintf(mbuf2, sizeof mbuf2, "%s.MIME", partnum);
+
+       if (!strcasecmp(desired_section, mbuf2)) {
+               cprintf("Content-type: %s", cbtype);
+               if (strlen(cbcharset) > 0)
+                       cprintf("; charset=\"%s\"", cbcharset);
+               if (strlen(name) > 0)
+                       cprintf("; name=\"%s\"", name);
+               cprintf("\r\n");
+               if (strlen(encoding) > 0)
+                       cprintf("Content-Transfer-Encoding: %s\r\n", encoding);
+               if (strlen(encoding) > 0) {
+                       cprintf("Content-Disposition: %s", disp);
+                       if (strlen(filename) > 0) {
+                               cprintf("; filename=\"%s\"", filename);
+                       }
+                       cprintf("\r\n");
+               }
+               cprintf("Content-Length: %ld\r\n", (long)length);
+               cprintf("\r\n");
+       }
+                       
+
+}
+
+
+/* 
+ * Called by imap_fetch_envelope() to output the "From" field.
+ * This is in its own function because its logic is kind of complex.  We
+ * really need to make this suck less.
+ */
+void imap_output_envelope_from(struct CtdlMessage *msg) {
+       char user[SIZ], node[SIZ], name[SIZ];
+
+       if (!msg) return;
+
+       /* For anonymous messages, it's so easy! */
+       if (!is_room_aide() && (msg->cm_anon_type == MES_ANONONLY)) {
+               cprintf("((\"----\" NIL \"x\" \"x.org\")) ");
+               return;
+       }
+       if (!is_room_aide() && (msg->cm_anon_type == MES_ANONOPT)) {
+               cprintf("((\"anonymous\" NIL \"x\" \"x.org\")) ");
+               return;
+       }
+
+       /* For everything else, we do stuff. */
+       cprintf("((");                          /* open double-parens */
+       imap_strout(msg->cm_fields['A']);       /* personal name */
+       cprintf(" NIL ");                       /* source route (not used) */
+
+
+       if (msg->cm_fields['F'] != NULL) {
+               process_rfc822_addr(msg->cm_fields['F'], user, node, name);
+               imap_strout(user);              /* mailbox name (user id) */
+               cprintf(" ");
+               if (!strcasecmp(node, config.c_nodename)) {
+                       imap_strout(config.c_fqdn);
+               }
+               else {
+                       imap_strout(node);              /* host name */
+               }
+       }
+       else {
+               imap_strout(msg->cm_fields['A']); /* mailbox name (user id) */
+               cprintf(" ");
+               imap_strout(msg->cm_fields['N']);       /* host name */
+       }
+       
+       cprintf(")) ");                         /* close double-parens */
+}
+
+
+
+/*
+ * Output an envelope address (or set of addresses) in the official,
+ * convuluted, braindead format.  (Note that we can't use this for
+ * the "From" address because its data may come from a number of different
+ * fields.  But we can use it for "To" and possibly others.
+ */
+void imap_output_envelope_addr(char *addr) {
+       char individual_addr[256];
+       int num_addrs;
+       int i;
+       char user[256];
+       char node[256];
+       char name[256];
+
+       if (addr == NULL) {
+               cprintf("NIL ");
+               return;
+       }
+
+       if (strlen(addr) == 0) {
+               cprintf("NIL ");
+               return;
+       }
+
+       cprintf("(");
+
+       /* How many addresses are listed here? */
+       num_addrs = num_tokens(addr, ',');
+
+       /* Output them one by one. */
+       for (i=0; i<num_addrs; ++i) {
+               extract_token(individual_addr, addr, i, ',', sizeof individual_addr);
+               striplt(individual_addr);
+               process_rfc822_addr(individual_addr, user, node, name);
+               cprintf("(");
+               imap_strout(name);
+               cprintf(" NIL ");
+               imap_strout(user);
+               cprintf(" ");
+               imap_strout(node);
+               cprintf(")");
+               if (i < (num_addrs-1)) cprintf(" ");
+       }
+
+       cprintf(") ");
+}
+
+
+/*
+ * Implements the ENVELOPE fetch item
+ * 
+ * Note that the imap_strout() function can cleverly output NULL fields as NIL,
+ * so we don't have to check for that condition like we do elsewhere.
+ */
+void imap_fetch_envelope(struct CtdlMessage *msg) {
+       char datestringbuf[SIZ];
+       time_t msgdate;
+       char *fieldptr = NULL;
+
+       if (!msg) return;
+
+       /* Parse the message date into an IMAP-format date string */
+       if (msg->cm_fields['T'] != NULL) {
+               msgdate = atol(msg->cm_fields['T']);
+       }
+       else {
+               msgdate = time(NULL);
+       }
+       datestring(datestringbuf, sizeof datestringbuf,
+               msgdate, DATESTRING_IMAP);
+
+       /* Now start spewing data fields.  The order is important, as it is
+        * defined by the protocol specification.  Nonexistent fields must
+        * be output as NIL, existent fields must be quoted or literalled.
+        * The imap_strout() function conveniently does all this for us.
+        */
+       cprintf("ENVELOPE (");
+
+       /* Date */
+       imap_strout(datestringbuf);
+       cprintf(" ");
+
+       /* Subject */
+       imap_strout(msg->cm_fields['U']);
+       cprintf(" ");
+
+       /* From */
+       imap_output_envelope_from(msg);
+
+       /* Sender (default to same as 'From' if not present) */
+       fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Sender");
+       if (fieldptr != NULL) {
+               imap_output_envelope_addr(fieldptr);
+               free(fieldptr);
+       }
+       else {
+               imap_output_envelope_from(msg);
+       }
+
+       /* Reply-to */
+       fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Reply-to");
+       if (fieldptr != NULL) {
+               imap_output_envelope_addr(fieldptr);
+               free(fieldptr);
+       }
+       else {
+               imap_output_envelope_from(msg);
+       }
+
+       /* To */
+       imap_output_envelope_addr(msg->cm_fields['R']);
+
+       /* Cc (we do it this way because there might be a legacy non-Citadel Cc: field present) */
+       fieldptr = msg->cm_fields['Y'];
+       if (fieldptr != NULL) {
+               imap_output_envelope_addr(fieldptr);
+       }
+       else {
+               fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Cc");
+               imap_output_envelope_addr(fieldptr);
+               if (fieldptr != NULL) free(fieldptr);
+       }
+
+       /* Bcc */
+       fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Bcc");
+       imap_output_envelope_addr(fieldptr);
+       if (fieldptr != NULL) free(fieldptr);
+
+       /* In-reply-to */
+       fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "In-reply-to");
+       imap_strout(fieldptr);
+       cprintf(" ");
+       if (fieldptr != NULL) free(fieldptr);
+
+       /* message ID */
+       imap_strout(msg->cm_fields['I']);
+
+       cprintf(")");
+}
+
+/*
+ * This function is called only when CC->redirect_buffer contains a set of
+ * RFC822 headers with no body attached.  Its job is to strip that set of
+ * headers down to *only* the ones we're interested in.
+ */
+void imap_strip_headers(char *section) {
+       char buf[SIZ];
+       char *which_fields = NULL;
+       int doing_headers = 0;
+       int headers_not = 0;
+       char *parms[SIZ];
+        int num_parms = 0;
+       int i;
+       char *boiled_headers = NULL;
+       int ok = 0;
+       int done_headers = 0;
+       char *ptr = NULL;
+
+       if (CC->redirect_buffer == NULL) return;
+
+       which_fields = strdup(section);
+
+       if (!strncasecmp(which_fields, "HEADER.FIELDS", 13))
+               doing_headers = 1;
+       if (!strncasecmp(which_fields, "HEADER.FIELDS.NOT", 17))
+               headers_not = 1;
+
+       for (i=0; i<strlen(which_fields); ++i) {
+               if (which_fields[i]=='(')
+                       strcpy(which_fields, &which_fields[i+1]);
+       }
+       for (i=0; i<strlen(which_fields); ++i) {
+               if (which_fields[i]==')')
+                       which_fields[i] = 0;
+       }
+       num_parms = imap_parameterize(parms, which_fields);
+
+       boiled_headers = malloc(CC->redirect_alloc);
+       strcpy(boiled_headers, "");
+
+       ptr = CC->redirect_buffer;
+       ok = 0;
+       while ( (done_headers == 0) && (ptr = memreadline(ptr, buf, sizeof buf), *ptr != 0) ) {
+               if (!isspace(buf[0])) {
+                       ok = 0;
+                       if (doing_headers == 0) ok = 1;
+                       else {
+                               if (headers_not) ok = 1;
+                               else ok = 0;
+                               for (i=0; i<num_parms; ++i) {
+                                       if ( (!strncasecmp(buf, parms[i],
+                                          strlen(parms[i]))) &&
+                                          (buf[strlen(parms[i])]==':') ) {
+                                               if (headers_not) ok = 0;
+                                               else ok = 1;
+                                       }
+                               }
+                       }
+               }
+
+               if (ok) {
+                       strcat(boiled_headers, buf);
+                       strcat(boiled_headers, "\r\n");
+               }
+
+               if (strlen(buf) == 0) done_headers = 1;
+               if (buf[0]=='\r') done_headers = 1;
+               if (buf[0]=='\n') done_headers = 1;
+       }
+
+       strcat(boiled_headers, "\r\n");
+
+       /* Now save it back (it'll always be smaller) */
+       strcpy(CC->redirect_buffer, boiled_headers);
+       CC->redirect_len = strlen(boiled_headers);
+
+       free(which_fields);
+       free(boiled_headers);
+}
+
+
+/*
+ * Implements the BODY and BODY.PEEK fetch items
+ */
+void imap_fetch_body(long msgnum, char *item, int is_peek) {
+       struct CtdlMessage *msg = NULL;
+       char section[SIZ];
+       char partial[SIZ];
+       int is_partial = 0;
+       size_t pstart, pbytes;
+       int loading_body_now = 0;
+       int need_body = 1;
+       int burn_the_cache = 0;
+
+       /* extract section */
+       safestrncpy(section, item, sizeof section);
+       if (strchr(section, '[') != NULL) {
+               stripallbut(section, '[', ']');
+       }
+       lprintf(CTDL_DEBUG, "Section is: %s%s\n", section, ((strlen(section)==0) ? "(empty)" : "") );
+       if (!strncasecmp(section, "HEADER", 6)) {
+               need_body = 0;
+       }
+
+       /* Burn the cache if we don't have the same section of the 
+        * same message again.
+        */
+       if (IMAP->cached_body != NULL) {
+               if (IMAP->cached_bodymsgnum != msgnum) {
+                       burn_the_cache = 1;
+               }
+               else if ( (!IMAP->cached_body_withbody) && (need_body) ) {
+                       burn_the_cache = 1;
+               }
+               else if (strcasecmp(IMAP->cached_bodypart, section)) {
+                       burn_the_cache = 1;
+               }
+               if (burn_the_cache) {
+                       /* Yup, go ahead and burn the cache. */
+                       free(IMAP->cached_body);
+                       IMAP->cached_body_len = 0;
+                       IMAP->cached_body = NULL;
+                       IMAP->cached_bodymsgnum = (-1);
+                       strcpy(IMAP->cached_bodypart, "");
+               }
+       }
+
+       /* extract partial */
+       safestrncpy(partial, item, sizeof partial);
+       if (strchr(partial, '<') != NULL) {
+               stripallbut(partial, '<', '>');
+               is_partial = 1;
+       }
+       if (is_partial == 0) strcpy(partial, "");
+       /* if (strlen(partial) > 0) lprintf(CTDL_DEBUG, "Partial is %s\n", partial); */
+
+       if (IMAP->cached_body == NULL) {
+               CC->redirect_buffer = malloc(SIZ);
+               CC->redirect_len = 0;
+               CC->redirect_alloc = SIZ;
+               loading_body_now = 1;
+               msg = CtdlFetchMessage(msgnum, (need_body ? 1 : 0));
+       }
+
+       /* Now figure out what the client wants, and get it */
+
+       if (!loading_body_now) {
+               /* What we want is already in memory */
+       }
+
+       else if ( (!strcmp(section, "1")) && (msg->cm_format_type != 4) ) {
+               CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_NONE, 0, 1);
+       }
+
+       else if (!strcmp(section, "")) {
+               CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1);
+       }
+
+       /*
+        * If the client asked for just headers, or just particular header
+        * fields, strip it down.
+        */
+       else if (!strncasecmp(section, "HEADER", 6)) {
+               CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ONLY, 0, 1);
+               imap_strip_headers(section);
+       }
+
+       /*
+        * Strip it down if the client asked for everything _except_ headers.
+        */
+       else if (!strncasecmp(section, "TEXT", 4)) {
+               CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_NONE, 0, 1);
+       }
+
+       /*
+        * Anything else must be a part specifier.
+        * (Note value of 1 passed as 'dont_decode' so client gets it encoded)
+        */
+       else {
+               mime_parser(msg->cm_fields['M'], NULL,
+                               *imap_load_part, NULL, NULL,
+                               section,
+                               1);
+       }
+
+       if (loading_body_now) {
+               IMAP->cached_body = CC->redirect_buffer;
+               IMAP->cached_body_len = CC->redirect_len;
+               IMAP->cached_bodymsgnum = msgnum;
+               IMAP->cached_body_withbody = need_body;
+               strcpy(IMAP->cached_bodypart, section);
+               CC->redirect_buffer = NULL;
+               CC->redirect_len = 0;
+               CC->redirect_alloc = 0;
+       }
+
+       if (is_partial == 0) {
+               cprintf("BODY[%s] {%d}\r\n", section, IMAP->cached_body_len);
+               pstart = 0;
+               pbytes = IMAP->cached_body_len;
+       }
+       else {
+               sscanf(partial, "%d.%d", &pstart, &pbytes);
+               if (pbytes > (IMAP->cached_body_len - pstart)) {
+                       pbytes = IMAP->cached_body_len - pstart;
+               }
+               cprintf("BODY[%s]<%d> {%d}\r\n", section, pstart, pbytes);
+       }
+
+       /* Here we go -- output it */
+       client_write(&IMAP->cached_body[pstart], pbytes);
+
+       if (msg != NULL) {
+               CtdlFreeMessage(msg);
+       }
+
+       /* Mark this message as "seen" *unless* this is a "peek" operation */
+       if (is_peek == 0) {
+               CtdlSetSeen(&msgnum, 1, 1, ctdlsetseen_seen, NULL, NULL);
+       }
+}
+
+/*
+ * Called immediately before outputting a multipart bodystructure
+ */
+void imap_fetch_bodystructure_pre(
+               char *name, char *filename, char *partnum, char *disp,
+               void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
+               void *cbuserdata
+               ) {
+
+       cprintf("(");
+}
+
+
+
+/*
+ * Called immediately after outputting a multipart bodystructure
+ */
+void imap_fetch_bodystructure_post(
+               char *name, char *filename, char *partnum, char *disp,
+               void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
+               void *cbuserdata
+               ) {
+
+       char subtype[128];
+
+       cprintf(" ");
+
+       /* disposition */
+       extract_token(subtype, cbtype, 1, '/', sizeof subtype);
+       imap_strout(subtype);
+
+       /* body language */
+       cprintf(" NIL");
+
+       cprintf(")");
+}
+
+
+
+/*
+ * Output the info for a MIME part in the format required by BODYSTRUCTURE.
+ *
+ */
+void imap_fetch_bodystructure_part(
+               char *name, char *filename, char *partnum, char *disp,
+               void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
+               void *cbuserdata
+               ) {
+
+       int have_cbtype = 0;
+       int have_encoding = 0;
+       int lines = 0;
+       size_t i;
+       char cbmaintype[128];
+       char cbsubtype[128];
+
+       if (cbtype != NULL) if (strlen(cbtype)>0) have_cbtype = 1;
+       if (have_cbtype) {
+               extract_token(cbmaintype, cbtype, 0, '/', sizeof cbmaintype);
+               extract_token(cbsubtype, cbtype, 1, '/', sizeof cbsubtype);
+       }
+       else {
+               strcpy(cbmaintype, "TEXT");
+               strcpy(cbsubtype, "PLAIN");
+       }
+
+       cprintf("(");
+       imap_strout(cbmaintype);
+       cprintf(" ");
+       imap_strout(cbsubtype);
+       cprintf(" ");
+
+       if (cbcharset == NULL) {
+               cprintf("(\"CHARSET\" \"US-ASCII\"");
+       }
+       else if (strlen(cbcharset) == 0) {
+               cprintf("(\"CHARSET\" \"US-ASCII\"");
+       }
+       else {
+               cprintf("(\"CHARSET\" ");
+               imap_strout(cbcharset);
+       }
+
+       if (name != NULL) if (strlen(name)>0) {
+               cprintf(" \"NAME\" ");
+               imap_strout(name);
+       }
+
+       cprintf(") ");
+
+       cprintf("NIL ");        /* Body ID */
+       cprintf("NIL ");        /* Body description */
+
+       if (encoding != NULL) if (strlen(encoding) > 0)  have_encoding = 1;
+       if (have_encoding) {
+               imap_strout(encoding);
+       }
+       else {
+               imap_strout("7BIT");
+       }
+       cprintf(" ");
+
+       /* The next field is the size of the part in bytes. */
+       cprintf("%ld ", (long)length);  /* bytes */
+
+       /* The next field is the number of lines in the part, if and only
+        * if the part is TEXT.  More gratuitous complexity.
+        */
+       if (!strcasecmp(cbmaintype, "TEXT")) {
+               if (length) for (i=0; i<length; ++i) {
+                       if (((char *)content)[i] == '\n') ++lines;
+               }
+               cprintf("%d ", lines);
+       }
+
+       /* More gratuitous complexity */
+       if ((!strcasecmp(cbmaintype, "MESSAGE"))
+          && (!strcasecmp(cbsubtype, "RFC822"))) {
+               /* FIXME
+                     A body type of type MESSAGE and subtype RFC822
+                     contains, immediately after the basic fields, the
+                     envelope structure, body structure, and size in
+                     text lines of the encapsulated message.
+               */
+       }
+
+       /* MD5 value of body part; we can get away with NIL'ing this */
+       cprintf("NIL ");
+
+       /* Disposition */
+       if (disp == NULL) {
+               cprintf("NIL");
+       }
+       else if (strlen(disp) == 0) {
+               cprintf("NIL");
+       }
+       else {
+               cprintf("(");
+               imap_strout(disp);
+               if (filename != NULL) if (strlen(filename)>0) {
+                       cprintf(" (\"FILENAME\" ");
+                       imap_strout(filename);
+                       cprintf(")");
+               }
+               cprintf(")");
+       }
+
+       /* Body language (not defined yet) */
+       cprintf(" NIL)");
+}
+
+
+
+/*
+ * Spew the BODYSTRUCTURE data for a message.
+ *
+ */
+void imap_fetch_bodystructure (long msgnum, char *item,
+               struct CtdlMessage *msg) {
+       char *rfc822 = NULL;
+       char *rfc822_body = NULL;
+       size_t rfc822_len;
+       size_t rfc822_headers_len;
+       size_t rfc822_body_len;
+       char *ptr = NULL;
+       char buf[SIZ];
+       int lines = 0;
+
+       /* Handle NULL message gracefully */
+       if (msg == NULL) {
+               cprintf("BODYSTRUCTURE (\"TEXT\" \"PLAIN\" "
+                       "(\"CHARSET\" \"US-ASCII\") NIL NIL "
+                       "\"7BIT\" 0 0)");
+               return;
+       }
+
+       /* For non-RFC822 (ordinary Citadel) messages, this is short and
+        * sweet...
+        */
+       if (msg->cm_format_type != FMT_RFC822) {
+
+               /* *sigh* We have to RFC822-format the message just to be able
+                * to measure it.  FIXME use smi cached fields if possible
+                */
+
+               CC->redirect_buffer = malloc(SIZ);
+               CC->redirect_len = 0;
+               CC->redirect_alloc = SIZ;
+               CtdlOutputPreLoadedMsg(msg, MT_RFC822, 0, 0, 1);
+               rfc822 = CC->redirect_buffer;
+               rfc822_len = CC->redirect_len;
+               CC->redirect_buffer = NULL;
+               CC->redirect_len = 0;
+               CC->redirect_alloc = 0;
+
+               ptr = rfc822;
+               while (ptr = memreadline(ptr, buf, sizeof buf), *ptr != 0) {
+                       ++lines;
+                       if ((strlen(buf) == 0) && (rfc822_body == NULL)) {
+                               rfc822_body = ptr;
+                       }
+               }
+
+               rfc822_headers_len = rfc822_body - rfc822;
+               rfc822_body_len = rfc822_len - rfc822_headers_len;
+               free(rfc822);
+
+               cprintf("BODYSTRUCTURE (\"TEXT\" \"PLAIN\" "
+                       "(\"CHARSET\" \"US-ASCII\") NIL NIL "
+                       "\"7BIT\" %d %d)", rfc822_body_len, lines);
+
+               return;
+       }
+
+       /* For messages already stored in RFC822 format, we have to parse. */
+       cprintf("BODYSTRUCTURE ");
+       mime_parser(msg->cm_fields['M'],
+                       NULL,
+                       *imap_fetch_bodystructure_part, /* part */
+                       *imap_fetch_bodystructure_pre,  /* pre-multi */
+                       *imap_fetch_bodystructure_post, /* post-multi */
+                       NULL,
+                       1);     /* don't decode -- we want it as-is */
+}
+
+
+/*
+ * imap_do_fetch() calls imap_do_fetch_msg() to output the data of an
+ * individual message, once it has been selected for output.
+ */
+void imap_do_fetch_msg(int seq, int num_items, char **itemlist) {
+       int i;
+       struct CtdlMessage *msg = NULL;
+       int body_loaded = 0;
+
+       /* Don't attempt to fetch bogus messages or UID's */
+       if (seq < 1) return;
+       if (IMAP->msgids[seq-1] < 1L) return;
+
+       buffer_output();
+       cprintf("* %d FETCH (", seq);
+
+       for (i=0; i<num_items; ++i) {
+
+               /* Fetchable without going to the message store at all */
+               if (!strcasecmp(itemlist[i], "UID")) {
+                       imap_fetch_uid(seq);
+               }
+               else if (!strcasecmp(itemlist[i], "FLAGS")) {
+                       imap_fetch_flags(seq-1);
+               }
+
+               /* Potentially fetchable from cache, if the client requests
+                * stuff from the same message several times in a row.
+                */
+               else if (!strcasecmp(itemlist[i], "RFC822")) {
+                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]);
+               }
+               else if (!strcasecmp(itemlist[i], "RFC822.HEADER")) {
+                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]);
+               }
+               else if (!strcasecmp(itemlist[i], "RFC822.SIZE")) {
+                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]);
+               }
+               else if (!strcasecmp(itemlist[i], "RFC822.TEXT")) {
+                       imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]);
+               }
+
+               /* BODY fetches do their own fetching and caching too. */
+               else if (!strncasecmp(itemlist[i], "BODY[", 5)) {
+                       imap_fetch_body(IMAP->msgids[seq-1], itemlist[i], 0);
+               }
+               else if (!strncasecmp(itemlist[i], "BODY.PEEK[", 10)) {
+                       imap_fetch_body(IMAP->msgids[seq-1], itemlist[i], 1);
+               }
+
+               /* Otherwise, load the message into memory.
+                */
+               else if (!strcasecmp(itemlist[i], "BODYSTRUCTURE")) {
+                       if ((msg != NULL) && (!body_loaded)) {
+                               CtdlFreeMessage(msg);   /* need the whole thing */
+                               msg = NULL;
+                       }
+                       if (msg == NULL) {
+                               msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                               body_loaded = 1;
+                       }
+                       imap_fetch_bodystructure(IMAP->msgids[seq-1],
+                                       itemlist[i], msg);
+               }
+               else if (!strcasecmp(itemlist[i], "ENVELOPE")) {
+                       if (msg == NULL) {
+                               msg = CtdlFetchMessage(IMAP->msgids[seq-1], 0);
+                               body_loaded = 0;
+                       }
+                       imap_fetch_envelope(msg);
+               }
+               else if (!strcasecmp(itemlist[i], "INTERNALDATE")) {
+                       if (msg == NULL) {
+                               msg = CtdlFetchMessage(IMAP->msgids[seq-1], 0);
+                               body_loaded = 0;
+                       }
+                       imap_fetch_internaldate(msg);
+               }
+
+               if (i != num_items-1) cprintf(" ");
+       }
+
+       cprintf(")\r\n");
+       unbuffer_output();
+       if (msg != NULL) {
+               CtdlFreeMessage(msg);
+       }
+}
+
+
+
+/*
+ * imap_fetch() calls imap_do_fetch() to do its actual work, once it's
+ * validated and boiled down the request a bit.
+ */
+void imap_do_fetch(int num_items, char **itemlist) {
+       int i;
+
+       if (IMAP->num_msgs > 0) {
+               for (i = 0; i < IMAP->num_msgs; ++i) {
+
+                       /* Abort the fetch loop if the session breaks.
+                        * This is important for users who keep mailboxes
+                        * that are too big *and* are too impatient to
+                        * let them finish loading.  :)
+                        */
+                       if (CC->kill_me) return;
+
+                       /* Get any message marked for fetch. */
+                       if (IMAP->flags[i] & IMAP_SELECTED) {
+                               imap_do_fetch_msg(i+1, num_items, itemlist);
+                       }
+               }
+       }
+}
+
+
+
+/*
+ * Back end for imap_handle_macros()
+ * Note that this function *only* looks at the beginning of the string.  It
+ * is not a generic search-and-replace function.
+ */
+void imap_macro_replace(char *str, char *find, char *replace) {
+       char holdbuf[SIZ];
+
+       if (!strncasecmp(str, find, strlen(find))) {
+               if (str[strlen(find)]==' ') {
+                       strcpy(holdbuf, &str[strlen(find)+1]);
+                       strcpy(str, replace);
+                       strcat(str, " ");
+                       strcat(str, holdbuf);
+               }
+               if (str[strlen(find)]==0) {
+                       strcpy(holdbuf, &str[strlen(find)+1]);
+                       strcpy(str, replace);
+               }
+       }
+}
+
+
+
+/*
+ * Handle macros embedded in FETCH data items.
+ * (What the heck are macros doing in a wire protocol?  Are we trying to save
+ * the computer at the other end the trouble of typing a lot of characters?)
+ */
+void imap_handle_macros(char *str) {
+       int i;
+       int nest = 0;
+
+       for (i=0; i<strlen(str); ++i) {
+               if (str[i]=='(') ++nest;
+               if (str[i]=='[') ++nest;
+               if (str[i]=='<') ++nest;
+               if (str[i]=='{') ++nest;
+               if (str[i]==')') --nest;
+               if (str[i]==']') --nest;
+               if (str[i]=='>') --nest;
+               if (str[i]=='}') --nest;
+
+               if (nest <= 0) {
+                       imap_macro_replace(&str[i],
+                               "ALL",
+                               "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE"
+                       );
+                       imap_macro_replace(&str[i],
+                               "BODY",
+                               "BODYSTRUCTURE"
+                       );
+                       imap_macro_replace(&str[i],
+                               "FAST",
+                               "FLAGS INTERNALDATE RFC822.SIZE"
+                       );
+                       imap_macro_replace(&str[i],
+                               "FULL",
+                               "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY"
+                       );
+               }
+       }
+}
+
+
+/*
+ * Break out the data items requested, possibly a parenthesized list.
+ * Returns the number of data items, or -1 if the list is invalid.
+ * NOTE: this function alters the string it is fed, and uses it as a buffer
+ * to hold the data for the pointers it returns.
+ */
+int imap_extract_data_items(char **argv, char *items) {
+       int num_items = 0;
+       int nest = 0;
+       int i, initial_len;
+       char *start;
+
+       /* Convert all whitespace to ordinary space characters. */
+       for (i=0; i<strlen(items); ++i) {
+               if (isspace(items[i])) items[i]=' ';
+       }
+
+       /* Strip leading and trailing whitespace, then strip leading and
+        * trailing parentheses if it's a list
+        */
+       striplt(items);
+       if ( (items[0]=='(') && (items[strlen(items)-1]==')') ) {
+               items[strlen(items)-1] = 0;
+               strcpy(items, &items[1]);
+               striplt(items);
+       }
+
+       /* Parse any macro data items */
+       imap_handle_macros(items);
+
+       /*
+        * Now break out the data items.  We throw in one trailing space in
+        * order to avoid having to break out the last one manually.
+        */
+       strcat(items, " ");
+       start = items;
+       initial_len = strlen(items);
+       for (i=0; i<initial_len; ++i) {
+               if (items[i]=='(') ++nest;
+               if (items[i]=='[') ++nest;
+               if (items[i]=='<') ++nest;
+               if (items[i]=='{') ++nest;
+               if (items[i]==')') --nest;
+               if (items[i]==']') --nest;
+               if (items[i]=='>') --nest;
+               if (items[i]=='}') --nest;
+
+               if (nest <= 0) if (items[i]==' ') {
+                       items[i] = 0;
+                       argv[num_items++] = start;
+                       start = &items[i+1];
+               }
+       }
+
+       return(num_items);
+
+}
+
+
+/*
+ * One particularly hideous aspect of IMAP is that we have to allow the client
+ * to specify arbitrary ranges and/or sets of messages to fetch.  Citadel IMAP
+ * handles this by setting the IMAP_SELECTED flag for each message specified in
+ * the ranges/sets, then looping through the message array, outputting messages
+ * with the flag set.  We don't bother returning an error if an out-of-range
+ * number is specified (we just return quietly) because any client braindead
+ * enough to request a bogus message number isn't going to notice the
+ * difference anyway.
+ *
+ * This function clears out the IMAP_SELECTED bits, then sets that bit for each
+ * message included in the specified range.
+ *
+ * Set is_uid to 1 to fetch by UID instead of sequence number.
+ */
+void imap_pick_range(char *supplied_range, int is_uid) {
+       int i;
+       int num_sets;
+       int s;
+       char setstr[SIZ], lostr[SIZ], histr[SIZ];
+       long lo, hi;
+       char actual_range[SIZ];
+
+       /* 
+        * Handle the "ALL" macro
+        */
+       if (!strcasecmp(supplied_range, "ALL")) {
+               safestrncpy(actual_range, "1:*", sizeof actual_range);
+       }
+       else {
+               safestrncpy(actual_range, supplied_range, sizeof actual_range);
+       }
+
+       /*
+        * Clear out the IMAP_SELECTED flags for all messages.
+        */
+       for (i = 0; i < IMAP->num_msgs; ++i) {
+               IMAP->flags[i] = IMAP->flags[i] & ~IMAP_SELECTED;
+       }
+
+       /*
+        * Now set it for all specified messages.
+        */
+       num_sets = num_tokens(actual_range, ',');
+       for (s=0; s<num_sets; ++s) {
+               extract_token(setstr, actual_range, s, ',', sizeof setstr);
+
+               extract_token(lostr, setstr, 0, ':', sizeof lostr);
+               if (num_tokens(setstr, ':') >= 2) {
+                       extract_token(histr, setstr, 1, ':', sizeof histr);
+                       if (!strcmp(histr, "*")) snprintf(histr, sizeof histr, "%ld", LONG_MAX);
+               } 
+               else {
+                       safestrncpy(histr, lostr, sizeof histr);
+               }
+               lo = atol(lostr);
+               hi = atol(histr);
+
+               /* Loop through the array, flipping bits where appropriate */
+               for (i = 1; i <= IMAP->num_msgs; ++i) {
+                       if (is_uid) {   /* fetch by sequence number */
+                               if ( (IMAP->msgids[i-1]>=lo)
+                                  && (IMAP->msgids[i-1]<=hi)) {
+                                       IMAP->flags[i-1] |= IMAP_SELECTED;
+                               }
+                       }
+                       else {          /* fetch by uid */
+                               if ( (i>=lo) && (i<=hi)) {
+                                       IMAP->flags[i-1] |= IMAP_SELECTED;
+                               }
+                       }
+               }
+       }
+
+}
+
+
+
+/*
+ * This function is called by the main command loop.
+ */
+void imap_fetch(int num_parms, char *parms[]) {
+       char items[SIZ];
+       char *itemlist[512];
+       int num_items;
+       int i;
+
+       if (num_parms < 4) {
+               cprintf("%s BAD invalid parameters\r\n", parms[0]);
+               return;
+       }
+
+       imap_pick_range(parms[2], 0);
+
+       strcpy(items, "");
+       for (i=3; i<num_parms; ++i) {
+               strcat(items, parms[i]);
+               if (i < (num_parms-1)) strcat(items, " ");
+       }
+
+       num_items = imap_extract_data_items(itemlist, items);
+       if (num_items < 1) {
+               cprintf("%s BAD invalid data item list\r\n", parms[0]);
+               return;
+       }
+
+       imap_do_fetch(num_items, itemlist);
+       cprintf("%s OK FETCH completed\r\n", parms[0]);
+}
+
+/*
+ * This function is called by the main command loop.
+ */
+void imap_uidfetch(int num_parms, char *parms[]) {
+       char items[SIZ];
+       char *itemlist[512];
+       int num_items;
+       int i;
+       int have_uid_item = 0;
+
+       if (num_parms < 5) {
+               cprintf("%s BAD invalid parameters\r\n", parms[0]);
+               return;
+       }
+
+       imap_pick_range(parms[3], 1);
+
+       strcpy(items, "");
+       for (i=4; i<num_parms; ++i) {
+               strcat(items, parms[i]);
+               if (i < (num_parms-1)) strcat(items, " ");
+       }
+
+       num_items = imap_extract_data_items(itemlist, items);
+       if (num_items < 1) {
+               cprintf("%s BAD invalid data item list\r\n", parms[0]);
+               return;
+       }
+
+       /* If the "UID" item was not included, we include it implicitly
+        * (at the beginning) because this is a UID FETCH command
+        */
+       for (i=0; i<num_items; ++i) {
+               if (!strcasecmp(itemlist[i], "UID")) ++have_uid_item;
+       }
+       if (have_uid_item == 0) {
+               memmove(&itemlist[1], &itemlist[0], (sizeof(itemlist[0]) * num_items));
+               ++num_items;
+               itemlist[0] = "UID";
+       }
+
+       imap_do_fetch(num_items, itemlist);
+       cprintf("%s OK UID FETCH completed\r\n", parms[0]);
+}
+
+
diff --git a/citadel/modules/imap/imap_fetch.h b/citadel/modules/imap/imap_fetch.h
new file mode 100644 (file)
index 0000000..f8ed5f1
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+ * $Id$
+ *
+ */
+
+void imap_pick_range(char *range, int is_uid);
+void imap_fetch(int num_parms, char *parms[]);
+void imap_uidfetch(int num_parms, char *parms[]);
+void imap_fetch_flags(int seq);
+int imap_extract_data_items(char **argv, char *items);
diff --git a/citadel/modules/imap/imap_list.c b/citadel/modules/imap/imap_list.c
new file mode 100644 (file)
index 0000000..57bb658
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ * $Id$
+ *
+ * Implements the LIST and LSUB commands.
+ *
+ * Copyright (C) 2000-2007 by Art Cancro and others.
+ * This code is released under the terms of the GNU General Public License.
+ *
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.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 <sys/wait.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include "citadel.h"
+#include "server.h"
+#include "sysdep_decls.h"
+#include "citserver.h"
+#include "support.h"
+#include "config.h"
+#include "room_ops.h"
+#include "user_ops.h"
+#include "policy.h"
+#include "database.h"
+#include "msgbase.h"
+#include "tools.h"
+#include "internet_addressing.h"
+#include "serv_imap.h"
+#include "imap_tools.h"
+#include "imap_fetch.h"
+#include "imap_search.h"
+#include "imap_store.h"
+#include "imap_acl.h"
+#include "imap_misc.h"
+#include "imap_list.h"
+
+
+/*
+ * Used by LIST and LSUB to show the floors in the listing
+ */
+void imap_list_floors(char *verb, int num_patterns, char **patterns)
+{
+       int i;
+       struct floor *fl;
+       int j = 0;
+       int match = 0;
+
+       for (i = 0; i < MAXFLOORS; ++i) {
+               fl = cgetfloor(i);
+               if (fl->f_flags & F_INUSE) {
+                       match = 0;
+                       for (j=0; j<num_patterns; ++j) {
+                               if (imap_mailbox_matches_pattern (patterns[j], fl->f_name)) {
+                                       match = 1;
+                               }
+                       }
+                       if (match) {
+                               cprintf("* %s (\\NoSelect \\HasChildren) \"/\" ", verb);
+                               imap_strout(fl->f_name);
+                               cprintf("\r\n");
+                       }
+               }
+       }
+}
+
+
+/*
+ * Back end for imap_list()
+ *
+ * Implementation note: IMAP "subscribed folder" is equivalent to Citadel "known room"
+ *
+ * The "user data" field is actually an array of pointers; see below for the breakdown
+ *
+ */
+void imap_listroom(struct ctdlroom *qrbuf, void *data)
+{
+       char buf[SIZ];
+       char return_options[256];
+       int ra;
+       int yes_output_this_room;
+
+       char **data_for_callback;
+       char *verb;
+       int subscribed_rooms_only;
+       int num_patterns;
+       char **patterns;
+       int return_subscribed;
+       int return_children;
+       int return_metadata;
+       int i = 0;
+       int match = 0;
+
+       /* Here's how we break down the array of pointers passed to us */
+       data_for_callback = data;
+       verb = data_for_callback[0];
+       subscribed_rooms_only = (int) data_for_callback[1];
+       num_patterns = (int) data_for_callback[2];
+       patterns = (char **) data_for_callback[3];
+       return_subscribed = (int) data_for_callback[4];
+       return_children = (int) data_for_callback[5];
+       return_metadata = (int) data_for_callback[6];
+
+       /* Only list rooms to which the user has access!! */
+       yes_output_this_room = 0;
+       strcpy(return_options, "");
+       CtdlRoomAccess(qrbuf, &CC->user, &ra, NULL);
+
+       if (return_subscribed) {
+               if (ra & UA_KNOWN) {
+                       strcat(return_options, "\\Subscribed");
+               }
+       }
+
+       /* Warning: ugly hack.
+        * We don't have any way to determine the presence of child mailboxes
+        * without refactoring this entire module.  So we're just going to return
+        * the \HasChildren attribute for every room.
+        * We'll fix this later when we have time.
+        */
+       if (return_children) {
+               if (strlen(return_options) > 0) {
+                       strcat(return_options, " ");
+               }
+               strcat(return_options, "\\HasChildren");
+       }
+
+       if (subscribed_rooms_only) {
+               if (ra & UA_KNOWN) {
+                       yes_output_this_room = 1;
+               }
+       }
+       else {
+               if ((ra & UA_KNOWN) || ((ra & UA_GOTOALLOWED) && (ra & UA_ZAPPED))) {
+                       yes_output_this_room = 1;
+               }
+       }
+
+       if (yes_output_this_room) {
+               imap_mailboxname(buf, sizeof buf, qrbuf);
+               match = 0;
+               for (i=0; i<num_patterns; ++i) {
+                       if (imap_mailbox_matches_pattern(patterns[i], buf)) {
+                               match = 1;
+                       }
+               }
+               if (match) {
+                       cprintf("* %s (%s) \"/\" ", verb, return_options);
+                       imap_strout(buf);
+
+                       if (return_metadata) {
+                               cprintf(" (METADATA ())");      /* FIXME */
+                       }
+
+                       cprintf("\r\n");
+               }
+       }
+}
+
+
+/*
+ * Implements the LIST and LSUB commands
+ */
+void imap_list(int num_parms, char *parms[])
+{
+       int subscribed_rooms_only = 0;
+       char verb[16];
+       int i, j, paren_nest;
+       char *data_for_callback[7];
+       int num_patterns = 1;
+       char *patterns[MAX_PATTERNS];
+       int selection_left = (-1);
+       int selection_right = (-1);
+       int return_left = (-1);
+       int return_right = (-1);
+       int root_pos = 2;
+       int patterns_left = 3;
+       int patterns_right = 3;
+       int extended_list_in_use = 0;
+       int return_subscribed = 0;
+       int return_children = 0;
+       int return_metadata = 0;
+       int select_metadata_left = (-1);
+       int select_metadata_right = (-1);
+       int select_metadata_nest = 0;
+
+       if (num_parms < 4) {
+               cprintf("%s BAD arguments invalid\r\n", parms[0]);
+               return;
+       }
+
+       /* parms[1] is the IMAP verb being used (e.g. LIST or LSUB)
+        * This tells us how to behave, and what verb to return back to the caller
+        */
+       safestrncpy(verb, parms[1], sizeof verb);
+       j = strlen(verb);
+       for (i=0; i<j; ++i) {
+               verb[i] = toupper(verb[i]);
+       }
+
+       if (!strcasecmp(verb, "LSUB")) {
+               subscribed_rooms_only = 1;
+       }
+
+       /*
+        * In order to implement draft-ietf-imapext-list-extensions-18
+        * ("LIST Command Extensions") we need to:
+        *
+        * 1. Extract "selection options"
+        *                              (Extraction: done
+        *                              SUBSCRIBED option: done
+        *                              RECURSIVEMATCH option: not done yet
+        *                              REMOTE: safe to silently ignore)
+        *
+        * 2. Extract "return options"
+        *                              (Extraction: done
+        *                              SUBSCRIBED option: done
+        *                              CHILDREN option: done, but needs a non-ugly rewrite)
+        *
+        * 3. Determine whether there is more than one match pattern (done)
+        */
+
+       /*
+        * If parameter 2 begins with a '(' character, the client is specifying
+        * selection options.  Extract their exact position, and then modify our
+        * expectation of where the root folder will be specified.
+        */
+       if (parms[2][0] == '(') {
+               extended_list_in_use = 1;
+               selection_left = 2;
+               paren_nest = 0;
+               for (i=2; i<num_parms; ++i) {
+                       for (j=0; j<strlen(parms[i]); ++j) {
+                               if (parms[i][j] == '(') ++paren_nest;
+                               if (parms[i][j] == ')') --paren_nest;
+                       }
+                       if (paren_nest == 0) {
+                               selection_right = i;    /* found end of selection options */
+                               root_pos = i+1;         /* folder root appears after selection options */
+                               i = num_parms + 1;      /* break out of the loop */
+                       }
+               }
+       }
+
+       /* If selection options were found, do something with them.
+        */
+       if ((selection_left > 0) && (selection_right >= selection_left)) {
+
+               /* Strip off the outer parentheses */
+               if (parms[selection_left][0] == '(') {
+                       strcpy(parms[selection_left], &parms[selection_left][1]);
+               }
+               if (parms[selection_right][strlen(parms[selection_right])-1] == ')') {
+                       parms[selection_right][strlen(parms[selection_right])-1] = 0;
+               }
+
+               for (i=selection_left; i<=selection_right; ++i) {
+
+                       /* are we in the middle of a metadata select block? */
+                       if ((select_metadata_left >= 0) && (select_metadata_right < 0)) {
+                               select_metadata_nest += haschar(parms[i], '(') - haschar(parms[i], ')') ;
+                               if (select_metadata_nest == 0) {
+                                       select_metadata_right = i;
+                               }
+                       }
+
+                       else if (!strcasecmp(parms[i], "METADATA")) {
+                               select_metadata_left = i+1;
+                               select_metadata_nest = 0;
+                       }
+
+                       else if (!strcasecmp(parms[i], "SUBSCRIBED")) {
+                               subscribed_rooms_only = 1;
+                       }
+
+                       else if (!strcasecmp(parms[i], "RECURSIVEMATCH")) {
+                               /* FIXME - do this! */
+                       }
+
+               }
+
+       }
+
+       lprintf(CTDL_DEBUG, "select metadata: %d to %d\n", select_metadata_left, select_metadata_right);
+       /* FIXME blah, we have to do something with this */
+
+       /* The folder root appears immediately after the selection options,
+        * or in position 2 if no selection options were specified.
+        */
+       patterns_left = root_pos + 1;
+       patterns_right = root_pos + 1;
+
+       if (parms[patterns_left][0] == '(') {
+               extended_list_in_use = 1;
+               paren_nest = 0;
+               for (i=patterns_left; i<num_parms; ++i) {
+                       for (j=0; j<strlen(parms[i]); ++j) {
+                               if (parms[i][j] == '(') ++paren_nest;
+                               if (parms[i][j] == ')') --paren_nest;
+                       }
+                       if (paren_nest == 0) {
+                               patterns_right = i;     /* found end of patterns */
+                               i = num_parms + 1;      /* break out of the loop */
+                       }
+               }
+               num_patterns = patterns_right - patterns_left + 1;
+               for (i=0; i<num_patterns; ++i) {
+                       if (i < MAX_PATTERNS) {
+                               patterns[i] = malloc(512);
+                               snprintf(patterns[i], 512, "%s%s", parms[root_pos], parms[patterns_left+i]);
+                               if (i == 0) {
+                                       strcpy(patterns[i], &patterns[i][1]);
+                               }
+                               if (i == num_patterns-1) {
+                                       patterns[i][strlen(patterns[i])-1] = 0;
+                               }
+                       }
+               }
+       }
+       else {
+               num_patterns = 1;
+               patterns[0] = malloc(512);
+               snprintf(patterns[0], 512, "%s%s", parms[root_pos], parms[patterns_left]);
+       }
+
+       /* If the word "RETURN" appears after the folder pattern list, then the client
+        * is specifying return options.
+        */
+       if (num_parms - patterns_right > 2) if (!strcasecmp(parms[patterns_right+1], "RETURN")) {
+               return_left = patterns_right + 2;
+               extended_list_in_use = 1;
+               paren_nest = 0;
+               for (i=return_left; i<num_parms; ++i) {
+                       for (j=0; j<strlen(parms[i]); ++j) {
+                               if (parms[i][j] == '(') ++paren_nest;
+                               if (parms[i][j] == ')') --paren_nest;
+                       }
+
+                       /* Might as well look for these while we're in here... */
+                       if (parms[i][0] == '(') strcpy(parms[i], &parms[i][1]);
+                       if (parms[i][strlen(parms[i])-1] == ')') parms[i][strlen(parms[i])-1] = 0;
+                       lprintf(9, "evaluating <%s>\n", parms[i]);
+
+                       if (!strcasecmp(parms[i], "SUBSCRIBED")) {
+                               return_subscribed = 1;
+                       }
+
+                       else if (!strcasecmp(parms[i], "CHILDREN")) {
+                               return_children = 1;
+                       }
+
+                       else if (!strcasecmp(parms[i], "METADATA")) {
+                               return_metadata = 1;
+                       }
+
+                       if (paren_nest == 0) {
+                               return_right = i;       /* found end of patterns */
+                               i = num_parms + 1;      /* break out of the loop */
+                       }
+               }
+       }
+
+       /* Now start setting up the data we're going to send to the ForEachRoom() callback.
+        */
+       data_for_callback[0] = (char *) verb;
+       data_for_callback[1] = (char *) subscribed_rooms_only;
+       data_for_callback[2] = (char *) num_patterns;
+       data_for_callback[3] = (char *) patterns;
+       data_for_callback[4] = (char *) return_subscribed;
+       data_for_callback[5] = (char *) return_children;
+       data_for_callback[6] = (char *) return_metadata;
+
+       /* The non-extended LIST command is required to treat an empty
+        * ("" string) mailbox name argument as a special request to return the
+        * hierarchy delimiter and the root name of the name given in the
+        * reference parameter.
+        */
+       if ( (strlen(patterns[0]) == 0) && (extended_list_in_use == 0) ) {
+               cprintf("* %s (\\Noselect) \"/\" \"\"\r\n", verb);
+       }
+
+       /* Non-empty mailbox names, and any form of the extended LIST command,
+        * is handled by this loop.
+        */
+       else {
+               imap_list_floors(verb, num_patterns, patterns);
+               ForEachRoom(imap_listroom, data_for_callback);
+       }
+
+       /* 
+        * Free the pattern buffers we allocated above.
+        */
+       for (i=0; i<num_patterns; ++i) {
+               free(patterns[i]);
+       }
+
+       cprintf("%s OK %s completed\r\n", parms[0], verb);
+}
diff --git a/citadel/modules/imap/imap_list.h b/citadel/modules/imap/imap_list.h
new file mode 100644 (file)
index 0000000..e65d855
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * $Id$
+ */
+
+/*
+ * In the extended form of LIST the client is allowed to specify
+ * multiple match patterns.  How many will we allow?
+ */
+#define MAX_PATTERNS 20
+
+void imap_list(int num_parms, char *parms[]);
diff --git a/citadel/modules/imap/imap_metadata.c b/citadel/modules/imap/imap_metadata.c
new file mode 100644 (file)
index 0000000..4dde983
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * $Id$
+ *
+ * IMAP METADATA extension
+ *
+ * This is an implementation of the Bynari variant of the METADATA extension.
+ *
+ */
+
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.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 <sys/wait.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include "citadel.h"
+#include "server.h"
+#include "sysdep_decls.h"
+#include "citserver.h"
+#include "support.h"
+#include "config.h"
+#include "room_ops.h"
+#include "user_ops.h"
+#include "policy.h"
+#include "database.h"
+#include "msgbase.h"
+#include "tools.h"
+#include "internet_addressing.h"
+#include "serv_imap.h"
+#include "imap_tools.h"
+#include "imap_fetch.h"
+#include "imap_misc.h"
+#include "genstamp.h"
+
+
+
+/*
+ * Implements the SETMETADATA command.
+ *
+ * Again, the only thing we're interested in setting here is the folder type.
+ *
+ * Attempting to set anything else calls a stub which fools the client into
+ * thinking that there is no remaining space available to store annotations.
+ */
+void imap_setmetadata(int num_parms, char *parms[]) {
+       char roomname[ROOMNAMELEN];
+       char savedroom[ROOMNAMELEN];
+       int msgs, new;
+       int ret;
+       int setting_user_value = 0;
+       char set_value[32];
+       int set_view = VIEW_BBS;
+       struct visit vbuf;
+
+       if (num_parms != 6) {
+               cprintf("%s BAD usage error\r\n", parms[0]);
+               return;
+       }
+
+       /*
+        * Don't allow other types of metadata to be set
+        */
+       if (strcasecmp(parms[3], "/vendor/kolab/folder-type")) {
+               cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", parms[0]);
+               return;
+       }
+
+       if (!strcasecmp(parms[4], "(value.shared")) {
+               setting_user_value = 0;                         /* global view */
+       }
+       else if (!strcasecmp(parms[4], "(value.priv")) {
+               setting_user_value = 1;                         /* per-user view */
+       }
+       else {
+               cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", parms[0]);
+               return;
+       }
+
+       /*
+        * Extract the folder type without any parentheses.  Then learn
+        * the Citadel view type based on the supplied folder type.
+        */
+       extract_token(set_value, parms[5], 0, ')', sizeof set_value);
+       if (!strncasecmp(set_value, "mail", 4)) {
+               set_view = VIEW_MAILBOX;
+       }
+       else if (!strncasecmp(set_value, "event", 5)) {
+               set_view = VIEW_CALENDAR;
+       }
+       else if (!strncasecmp(set_value, "contact", 7)) {
+               set_view = VIEW_ADDRESSBOOK;
+       }
+       else if (!strncasecmp(set_value, "journal", 7)) {
+               set_view = VIEW_JOURNAL;
+       }
+       else if (!strncasecmp(set_value, "note", 4)) {
+               set_view = VIEW_NOTES;
+       }
+       else if (!strncasecmp(set_value, "task", 4)) {
+               set_view = VIEW_TASKS;
+       }
+       else {
+               set_view = VIEW_MAILBOX;
+       }
+
+       ret = imap_grabroom(roomname, parms[2], 0);
+       if (ret != 0) {
+               cprintf("%s NO Invalid mailbox name or access denied\r\n",
+                       parms[0]);
+               return;
+       }
+
+       /*
+        * usergoto() formally takes us to the desired room.  (If another
+        * folder is selected, save its name so we can return there!!!!!)
+        */
+       if (IMAP->selected) {
+               strcpy(savedroom, CC->room.QRname);
+       }
+       usergoto(roomname, 0, 0, &msgs, &new);
+
+       /*
+        * Always set the per-user view to the requested one.
+        */
+       CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
+       vbuf.v_view = set_view;
+       CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
+
+       /* If this is a "value.priv" set operation, we're done. */
+
+       if (setting_user_value)
+       {
+               cprintf("%s OK SETANNOTATION complete\r\n", parms[0]);
+       }
+
+       /* If this is a "value.shared" set operation, we are allowed to perform it
+        * under certain conditions.
+        */
+       else if (       (is_room_aide())                                        /* aide or room aide */
+               ||      (       (CC->room.QRflags & QR_MAILBOX)
+                       &&      (CC->user.usernum == atol(CC->room.QRname))     /* mailbox owner */
+                       )
+               ||      (msgs == 0)             /* hack: if room is empty, assume we just created it */
+       ) {
+               lgetroom(&CC->room, CC->room.QRname);
+               CC->room.QRdefaultview = set_view;
+               lputroom(&CC->room);
+               cprintf("%s OK SETANNOTATION complete\r\n", parms[0]);
+       }
+
+       /* If we got to this point, we don't have permission to set the default view. */
+       else {
+               cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", parms[0]);
+       }
+
+       /*
+        * If a different folder was previously selected, return there now.
+        */
+       if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
+               usergoto(savedroom, 0, 0, &msgs, &new);
+       }
+       return;
+}
+
+
+/*
+ * Implements the GETMETADATA command.
+ *
+ * Regardless of what the client asked for, we are going to supply them with
+ * the folder type.  It's the only metadata we have anyway.
+ */
+void imap_getmetadata(int num_parms, char *parms[]) {
+       char roomname[ROOMNAMELEN];
+       char savedroom[ROOMNAMELEN];
+       int msgs, new;
+       int ret;
+
+       if (num_parms > 5) {
+               cprintf("%s BAD usage error\r\n", parms[0]);
+               return;
+       }
+
+       ret = imap_grabroom(roomname, parms[2], 0);
+       if (ret != 0) {
+               cprintf("%s NO Invalid mailbox name or access denied\r\n",
+                       parms[0]);
+               return;
+       }
+
+       /*
+        * usergoto() formally takes us to the desired room.  (If another
+        * folder is selected, save its name so we can return there!!!!!)
+        */
+       if (IMAP->selected) {
+               strcpy(savedroom, CC->room.QRname);
+       }
+       usergoto(roomname, 0, 0, &msgs, &new);
+
+       cprintf("* METADATA ");
+       imap_strout(parms[2]);
+       cprintf(" \"/vendor/kolab/folder-type\" (\"value.shared\" \"");
+
+       /* If it's one of our hard-coded default rooms, we know what to do... */
+
+       if (!strcasecmp(&CC->room.QRname[11], MAILROOM)) {
+               cprintf("mail.inbox");
+       }
+       else if (!strcasecmp(&CC->room.QRname[11], SENTITEMS)) {
+               cprintf("mail.sentitems");
+       }
+       else if (!strcasecmp(&CC->room.QRname[11], USERCALENDARROOM)) {
+               cprintf("event.default");
+       }
+       else if (!strcasecmp(&CC->room.QRname[11], USERCONTACTSROOM)) {
+               cprintf("contact.default");
+       }
+       else if (!strcasecmp(&CC->room.QRname[11], USERNOTESROOM)) {
+               cprintf("note.default");
+       }
+       else if (!strcasecmp(&CC->room.QRname[11], USERTASKSROOM)) {
+               cprintf("task.default");
+       }
+
+       /* Otherwise, use the view for this room to determine the type of data.
+        * We are going with the default view rather than the user's view, because
+        * the default view almost always defines the actual contents, while the
+        * user's view might only make changes to presentation.  It also saves us
+        * an extra database access because we don't need to load the visit record.
+        */
+
+       else if (CC->room.QRdefaultview == VIEW_CALENDAR) {
+               cprintf("event");
+       }
+       else if (CC->room.QRdefaultview == VIEW_ADDRESSBOOK) {
+               cprintf("contact");
+       }
+       else if (CC->room.QRdefaultview == VIEW_TASKS) {
+               cprintf("task");
+       }
+       else if (CC->room.QRdefaultview == VIEW_NOTES) {
+               cprintf("note");
+       }
+       else if (CC->room.QRdefaultview == VIEW_JOURNAL) {
+               cprintf("journal");
+       }
+
+       /* If none of the above conditions were met, consider it an ordinary mailbox. */
+       else {
+               cprintf("mail");
+       }
+
+       /* "mail.outbox" and "junkemail" are not implemented. */
+
+       cprintf("\")\r\n");
+
+       /*
+        * If a different folder was previously selected, return there now.
+        */
+       if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
+               usergoto(savedroom, 0, 0, &msgs, &new);
+       }
+
+       cprintf("%s OK GETMETADATA complete\r\n", parms[0]);
+       return;
+}
+
diff --git a/citadel/modules/imap/imap_metadata.h b/citadel/modules/imap/imap_metadata.h
new file mode 100644 (file)
index 0000000..2ce656b
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * $Id: imap_acl.h 5148 2007-05-08 15:40:16Z ajc $
+ *
+ */
+
+void imap_getmetadata(int num_parms, char *parms[]);
+void imap_setmetadata(int num_parms, char *parms[]);
diff --git a/citadel/modules/imap/imap_misc.c b/citadel/modules/imap/imap_misc.c
new file mode 100644 (file)
index 0000000..350f9ad
--- /dev/null
@@ -0,0 +1,472 @@
+/*
+ * $Id$
+ *
+ *
+ */
+
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.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 <sys/wait.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include "citadel.h"
+#include "server.h"
+#include "sysdep_decls.h"
+#include "citserver.h"
+#include "support.h"
+#include "config.h"
+#include "room_ops.h"
+#include "user_ops.h"
+#include "policy.h"
+#include "database.h"
+#include "msgbase.h"
+#include "tools.h"
+#include "internet_addressing.h"
+#include "serv_imap.h"
+#include "imap_tools.h"
+#include "imap_fetch.h"
+#include "imap_misc.h"
+#include "genstamp.h"
+
+
+
+
+
+
+/*
+ * imap_copy() calls imap_do_copy() to do its actual work, once it's
+ * validated and boiled down the request a bit.  (returns 0 on success)
+ */
+int imap_do_copy(char *destination_folder) {
+       int i;
+       char roomname[ROOMNAMELEN];
+       struct ctdlroom qrbuf;
+       long *selected_msgs = NULL;
+       int num_selected = 0;
+
+       if (IMAP->num_msgs < 1) {
+               return(0);
+       }
+
+       i = imap_grabroom(roomname, destination_folder, 0);
+       if (i != 0) return(i);
+
+       /*
+        * Copy all the message pointers in one shot.
+        */
+       selected_msgs = malloc(sizeof(long) * IMAP->num_msgs);
+       if (selected_msgs == NULL) return(-1);
+
+       for (i = 0; i < IMAP->num_msgs; ++i) {
+               if (IMAP->flags[i] & IMAP_SELECTED) {
+                       selected_msgs[num_selected++] = IMAP->msgids[i];
+               }
+       }
+
+       if (num_selected > 0) {
+               CtdlCopyMsgsToRoom(selected_msgs, num_selected, roomname);
+       }
+       free(selected_msgs);
+
+       /* Don't bother wasting any more time if there were no messages. */
+       if (num_selected == 0) {
+               return(0);
+       }
+
+       /* Enumerate lists of messages for which flags are toggled */
+       long *seen_yes = NULL;
+       int num_seen_yes = 0;
+       long *seen_no = NULL;
+       int num_seen_no = 0;
+       long *answ_yes = NULL;
+       int num_answ_yes = 0;
+       long *answ_no = NULL;
+       int num_answ_no = 0;
+
+       seen_yes = malloc(num_selected * sizeof(long));
+       seen_no = malloc(num_selected * sizeof(long));
+       answ_yes = malloc(num_selected * sizeof(long));
+       answ_no = malloc(num_selected * sizeof(long));
+
+       for (i = 0; i < IMAP->num_msgs; ++i) {
+               if (IMAP->flags[i] & IMAP_SELECTED) {
+                       if (IMAP->flags[i] & IMAP_SEEN) {
+                               seen_yes[num_seen_yes++] = IMAP->msgids[i];
+                       }
+                       if ((IMAP->flags[i] & IMAP_SEEN) == 0) {
+                               seen_no[num_seen_no++] = IMAP->msgids[i];
+                       }
+                       if (IMAP->flags[i] & IMAP_ANSWERED) {
+                               answ_yes[num_answ_yes++] = IMAP->msgids[i];
+                       }
+                       if ((IMAP->flags[i] & IMAP_ANSWERED) == 0) {
+                               answ_no[num_answ_no++] = IMAP->msgids[i];
+                       }
+               }
+       }
+
+       /* Set the flags... */
+       i = getroom(&qrbuf, roomname);
+       if (i == 0) {
+               CtdlSetSeen(seen_yes, num_seen_yes, 1, ctdlsetseen_seen, NULL, &qrbuf);
+               CtdlSetSeen(seen_no, num_seen_no, 0, ctdlsetseen_seen, NULL, &qrbuf);
+               CtdlSetSeen(answ_yes, num_answ_yes, 1, ctdlsetseen_answered, NULL, &qrbuf);
+               CtdlSetSeen(answ_no, num_answ_no, 0, ctdlsetseen_answered, NULL, &qrbuf);
+       }
+
+       free(seen_yes);
+       free(seen_no);
+       free(answ_yes);
+       free(answ_no);
+
+       return(0);
+}
+
+
+
+/*
+ * This function is called by the main command loop.
+ */
+void imap_copy(int num_parms, char *parms[]) {
+       int ret;
+
+       if (num_parms != 4) {
+               cprintf("%s BAD invalid parameters\r\n", parms[0]);
+               return;
+       }
+
+       if (imap_is_message_set(parms[2])) {
+               imap_pick_range(parms[2], 0);
+       }
+       else {
+               cprintf("%s BAD invalid parameters\r\n", parms[0]);
+               return;
+       }
+
+       ret = imap_do_copy(parms[3]);
+       if (!ret) {
+               cprintf("%s OK COPY completed\r\n", parms[0]);
+       }
+       else {
+               cprintf("%s NO COPY failed (error %d)\r\n", parms[0], ret);
+       }
+}
+
+/*
+ * This function is called by the main command loop.
+ */
+void imap_uidcopy(int num_parms, char *parms[]) {
+
+       if (num_parms != 5) {
+               cprintf("%s BAD invalid parameters\r\n", parms[0]);
+               return;
+       }
+
+       if (imap_is_message_set(parms[3])) {
+               imap_pick_range(parms[3], 1);
+       }
+       else {
+               cprintf("%s BAD invalid parameters\r\n", parms[0]);
+               return;
+       }
+
+       if (imap_do_copy(parms[4]) == 0) {
+               cprintf("%s OK UID COPY completed\r\n", parms[0]);
+       }
+       else {
+               cprintf("%s NO UID COPY failed\r\n", parms[0]);
+       }
+}
+
+
+/*
+ * Poll for instant messages (yeah, we can do this in IMAP ... I think)
+ */
+void imap_print_instant_messages(void) {
+       struct ExpressMessage *ptr, *holdptr;
+       char *dumpomatic = NULL;
+       char tmp[SIZ];
+       int i;
+       size_t size, size2;
+       struct tm stamp;
+
+       if (CC->FirstExpressMessage == NULL) {
+               return;
+       }
+       begin_critical_section(S_SESSION_TABLE);
+       ptr = CC->FirstExpressMessage;
+       CC->FirstExpressMessage = NULL;
+       end_critical_section(S_SESSION_TABLE);
+
+       while (ptr != NULL) {
+               localtime_r(&(ptr->timestamp), &stamp);
+               size = strlen(ptr->text) + SIZ;
+               dumpomatic = malloc(size);
+               strcpy(dumpomatic, "");
+               if (ptr->flags && EM_BROADCAST)
+                       strcat(dumpomatic, "Broadcast message ");
+               else if (ptr->flags && EM_CHAT)
+                       strcat(dumpomatic, "Chat request ");
+               else if (ptr->flags && EM_GO_AWAY)
+                       strcat(dumpomatic, "Please logoff now, as requested ");
+               else
+                       strcat(dumpomatic, "Message ");
+
+               /* Timestamp.  Can this be improved? */
+               if (stamp.tm_hour == 0 || stamp.tm_hour == 12)
+                       sprintf(tmp, "at 12:%02d%cm",
+                               stamp.tm_min, 
+                               stamp.tm_hour ? 'p' : 'a');
+               else if (stamp.tm_hour > 12)            /* pm */
+                       sprintf(tmp, "at %d:%02dpm",
+                               stamp.tm_hour - 12,
+                               stamp.tm_min);
+               else                                    /* am */
+                       sprintf(tmp, "at %d:%02dam",
+                               stamp.tm_hour, stamp.tm_min);
+               strcat(dumpomatic, tmp);
+
+               size2 = strlen(dumpomatic);
+               snprintf(&dumpomatic[size2], size - size2,
+                       " from %s:\n", ptr->sender);
+               if (ptr->text != NULL)
+                       strcat(dumpomatic, ptr->text);
+
+               holdptr = ptr->next;
+               if (ptr->text != NULL) free(ptr->text);
+               free(ptr);
+               ptr = holdptr;
+
+               for (i=0; i<strlen(dumpomatic); ++i) {
+                       if (!isprint(dumpomatic[i])) dumpomatic[i] = ' ';
+                       if (dumpomatic[i]=='\\') dumpomatic[i]='/';
+                       if (dumpomatic[i]=='\"') dumpomatic[i]='\'';
+               }
+
+               cprintf("* OK [ALERT] %s\r\n", dumpomatic);
+               free(dumpomatic);
+       }
+       cprintf("000\n");
+}
+
+
+/*
+ * imap_do_append_flags() is called by imap_append() to set any flags that
+ * the client specified at append time.
+ *
+ * FIXME find a way to do these in bulk so we don't max out our db journal
+ */
+void imap_do_append_flags(long new_msgnum, char *new_message_flags) {
+       char flags[32];
+       char this_flag[sizeof flags];
+       int i;
+
+       if (new_message_flags == NULL) return;
+       if (strlen(new_message_flags) == 0) return;
+
+       safestrncpy(flags, new_message_flags, sizeof flags);
+
+       for (i=0; i<num_tokens(flags, ' '); ++i) {
+               extract_token(this_flag, flags, i, ' ', sizeof this_flag);
+               if (this_flag[0] == '\\') strcpy(this_flag, &this_flag[1]);
+               if (!strcasecmp(this_flag, "Seen")) {
+                       CtdlSetSeen(&new_msgnum, 1, 1, ctdlsetseen_seen,
+                               NULL, NULL);
+               }
+               if (!strcasecmp(this_flag, "Answered")) {
+                       CtdlSetSeen(&new_msgnum, 1, 1, ctdlsetseen_answered,
+                               NULL, NULL);
+               }
+       }
+}
+
+
+/*
+ * This function is called by the main command loop.
+ */
+void imap_append(int num_parms, char *parms[]) {
+       long literal_length;
+       long bytes_transferred;
+       long stripped_length = 0;
+       struct CtdlMessage *msg = NULL;
+       long new_msgnum = (-1L);
+       int ret = 0;
+       char roomname[ROOMNAMELEN];
+       char buf[SIZ];
+       char savedroom[ROOMNAMELEN];
+       int msgs, new;
+       int i;
+       char new_message_flags[SIZ];
+
+       if (num_parms < 4) {
+               cprintf("%s BAD usage error\r\n", parms[0]);
+               return;
+       }
+
+       if ( (parms[num_parms-1][0] != '{')
+          || (parms[num_parms-1][strlen(parms[num_parms-1])-1] != '}') )  {
+               cprintf("%s BAD no message literal supplied\r\n", parms[0]);
+               return;
+       }
+
+       strcpy(new_message_flags, "");
+       if (num_parms >= 5) {
+               for (i=3; i<num_parms; ++i) {
+                       strcat(new_message_flags, parms[i]);
+                       strcat(new_message_flags, " ");
+               }
+               stripallbut(new_message_flags, '(', ')');
+       }
+
+       /* This is how we'd do this if it were relevant in our data store.
+        * if (num_parms >= 6) {
+        *  new_message_internaldate = parms[4];
+        * }
+        */
+
+       literal_length = atol(&parms[num_parms-1][1]);
+       if (literal_length < 1) {
+               cprintf("%s BAD Message length must be at least 1.\r\n",
+                       parms[0]);
+               return;
+       }
+
+       imap_free_transmitted_message();        /* just in case. */
+       IMAP->transmitted_message = malloc(literal_length + 1);
+       if (IMAP->transmitted_message == NULL) {
+               cprintf("%s NO Cannot allocate memory.\r\n", parms[0]);
+               return;
+       }
+       IMAP->transmitted_length = literal_length;
+
+       cprintf("+ Transmit message now.\r\n");
+
+       bytes_transferred = 0;
+
+       ret = client_read(IMAP->transmitted_message, literal_length);
+       IMAP->transmitted_message[literal_length] = 0;
+
+       if (ret != 1) {
+               cprintf("%s NO Read failed.\r\n", parms[0]);
+               return;
+       }
+
+       /* Client will transmit a trailing CRLF after the literal (the message
+        * text) is received.  This call to client_getln() absorbs it.
+        */
+       flush_output();
+       client_getln(buf, sizeof buf);
+
+       /* Convert RFC822 newlines (CRLF) to Unix newlines (LF) */
+       lprintf(CTDL_DEBUG, "Converting CRLF to LF\n");
+       stripped_length = 0;
+       for (i=0; i<literal_length; ++i) {
+               if (strncmp(&IMAP->transmitted_message[i], "\r\n", 2)) {
+                       IMAP->transmitted_message[stripped_length++] =
+                               IMAP->transmitted_message[i];
+               }
+       }
+       literal_length = stripped_length;
+       IMAP->transmitted_message[literal_length] = 0;  /* reterminate it */
+
+       lprintf(CTDL_DEBUG, "Converting message format\n");
+       msg = convert_internet_message(IMAP->transmitted_message);
+       IMAP->transmitted_message = NULL;
+       IMAP->transmitted_length = 0;
+
+       ret = imap_grabroom(roomname, parms[2], 0);
+       if (ret != 0) {
+               cprintf("%s NO Invalid mailbox name or access denied\r\n",
+                       parms[0]);
+               return;
+       }
+
+       /*
+        * usergoto() formally takes us to the desired room.  (If another
+        * folder is selected, save its name so we can return there!!!!!)
+        */
+       if (IMAP->selected) {
+               strcpy(savedroom, CC->room.QRname);
+       }
+       usergoto(roomname, 0, 0, &msgs, &new);
+
+       /* If the user is locally authenticated, FORCE the From: header to
+        * show up as the real sender.  FIXME do we really want to do this?
+        * Probably should make it site-definable or even room-definable.
+        *
+        * For now, we allow "forgeries" if the room is one of the user's
+        * private mailboxes.
+        */
+       if (CC->logged_in) {
+          if ( (CC->room.QRflags & QR_MAILBOX) == 0) {
+               if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
+               if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
+               if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
+               msg->cm_fields['A'] = strdup(CC->user.fullname);
+               msg->cm_fields['N'] = strdup(config.c_nodename);
+               msg->cm_fields['H'] = strdup(config.c_humannode);
+           }
+       }
+
+       /* 
+        * Can we post here?
+        */
+       ret = CtdlDoIHavePermissionToPostInThisRoom(buf, sizeof buf);
+
+       if (ret) {
+               /* Nope ... print an error message */
+               cprintf("%s NO %s\r\n", parms[0], buf);
+       }
+
+       else {
+               /* Yes ... go ahead and post! */
+               if (msg != NULL) {
+                       new_msgnum = CtdlSubmitMsg(msg, NULL, "");
+               }
+               if (new_msgnum >= 0L) {
+                       cprintf("%s OK APPEND completed\r\n", parms[0]);
+               }
+               else {
+                       cprintf("%s BAD Error %ld saving message to disk.\r\n",
+                               parms[0], new_msgnum);
+               }
+       }
+
+       /*
+        * IMAP protocol response to client has already been sent by now.
+        *
+        * If another folder is selected, go back to that room so we can resume
+        * our happy day without violent explosions.
+        */
+       if (IMAP->selected) {
+               usergoto(savedroom, 0, 0, &msgs, &new);
+       }
+
+       /* We don't need this buffer anymore */
+       CtdlFreeMessage(msg);
+
+       if (new_message_flags != NULL) {
+               imap_do_append_flags(new_msgnum, new_message_flags);
+       }
+}
diff --git a/citadel/modules/imap/imap_misc.h b/citadel/modules/imap/imap_misc.h
new file mode 100644 (file)
index 0000000..7fec936
--- /dev/null
@@ -0,0 +1,9 @@
+/*
+ * $Id$
+ *
+ */
+
+void imap_copy(int num_parms, char *parms[]);
+void imap_uidcopy(int num_parms, char *parms[]);
+void imap_print_instant_messages(void);
+void imap_append(int num_parms, char *parms[]);
diff --git a/citadel/modules/imap/imap_search.c b/citadel/modules/imap/imap_search.c
new file mode 100644 (file)
index 0000000..64f26f6
--- /dev/null
@@ -0,0 +1,637 @@
+/*
+ * $Id$
+ *
+ * Implements IMAP's gratuitously complex SEARCH command.
+ *
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.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 <sys/wait.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include "citadel.h"
+#include "server.h"
+#include "sysdep_decls.h"
+#include "citserver.h"
+#include "support.h"
+#include "config.h"
+#include "room_ops.h"
+#include "user_ops.h"
+#include "policy.h"
+#include "database.h"
+#include "msgbase.h"
+#include "tools.h"
+#include "internet_addressing.h"
+#include "serv_imap.h"
+#include "imap_tools.h"
+#include "imap_fetch.h"
+#include "imap_search.h"
+#include "genstamp.h"
+#include "serv_fulltext.h"     /* Needed for ft_search */
+
+
+/*
+ * imap_do_search() calls imap_do_search_msg() to search an individual
+ * message after it has been fetched from the disk.  This function returns
+ * nonzero if there is a match.
+ *
+ * supplied_msg MAY be used to pass a pointer to the message in memory,
+ * if for some reason it's already been loaded.  If not, the message will
+ * be loaded only if one or more search criteria require it.
+ */
+int imap_do_search_msg(int seq, struct CtdlMessage *supplied_msg,
+                       int num_items, char **itemlist, int is_uid) {
+
+       int match = 0;
+       int is_not = 0;
+       int is_or = 0;
+       int pos = 0;
+       int i;
+       char *fieldptr;
+       struct CtdlMessage *msg = NULL;
+       int need_to_free_msg = 0;
+
+       if (num_items == 0) {
+               return(0);
+       }
+       msg = supplied_msg;
+
+       /* Initially we start at the beginning. */
+       pos = 0;
+
+       /* Check for the dreaded NOT criterion. */
+       if (!strcasecmp(itemlist[0], "NOT")) {
+               is_not = 1;
+               pos = 1;
+       }
+
+       /* Check for the dreaded OR criterion. */
+       if (!strcasecmp(itemlist[0], "OR")) {
+               is_or = 1;
+               pos = 1;
+       }
+
+       /* Now look for criteria. */
+       if (!strcasecmp(itemlist[pos], "ALL")) {
+               match = 1;
+               ++pos;
+       }
+       
+       else if (!strcasecmp(itemlist[pos], "ANSWERED")) {
+               if (IMAP->flags[seq-1] & IMAP_ANSWERED) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "BCC")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Bcc");
+                       if (fieldptr != NULL) {
+                               if (bmstrcasestr(fieldptr, itemlist[pos+1])) {
+                                       match = 1;
+                               }
+                               free(fieldptr);
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "BEFORE")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       if (msg->cm_fields['T'] != NULL) {
+                               if (imap_datecmp(itemlist[pos+1],
+                                               atol(msg->cm_fields['T'])) < 0) {
+                                       match = 1;
+                               }
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "BODY")) {
+
+               /* If fulltext indexing is active, on this server,
+                *  all messages have already been qualified.
+                */
+               if (config.c_enable_fulltext) {
+                       match = 1;
+               }
+
+               /* Otherwise, we have to do a slow search. */
+               else {
+                       if (msg == NULL) {
+                               msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                               need_to_free_msg = 1;
+                       }
+                       if (msg != NULL) {
+                               if (bmstrcasestr(msg->cm_fields['M'], itemlist[pos+1])) {
+                                       match = 1;
+                               }
+                       }
+               }
+
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "CC")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       fieldptr = msg->cm_fields['Y'];
+                       if (fieldptr != NULL) {
+                               if (bmstrcasestr(fieldptr, itemlist[pos+1])) {
+                                       match = 1;
+                               }
+                       }
+                       else {
+                               fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Cc");
+                               if (fieldptr != NULL) {
+                                       if (bmstrcasestr(fieldptr, itemlist[pos+1])) {
+                                               match = 1;
+                                       }
+                                       free(fieldptr);
+                               }
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "DELETED")) {
+               if (IMAP->flags[seq-1] & IMAP_DELETED) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "DRAFT")) {
+               if (IMAP->flags[seq-1] & IMAP_DRAFT) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "FLAGGED")) {
+               if (IMAP->flags[seq-1] & IMAP_FLAGGED) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "FROM")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       if (bmstrcasestr(msg->cm_fields['A'], itemlist[pos+1])) {
+                               match = 1;
+                       }
+                       if (bmstrcasestr(msg->cm_fields['F'], itemlist[pos+1])) {
+                               match = 1;
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "HEADER")) {
+
+               /* We've got to do a slow search for this because the client
+                * might be asking for an RFC822 header field that has not been
+                * converted into a Citadel header field.  That requires
+                * examining the message body.
+                */
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+
+               if (msg != NULL) {
+       
+                       CC->redirect_buffer = malloc(SIZ);
+                       CC->redirect_len = 0;
+                       CC->redirect_alloc = SIZ;
+                       CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ONLY, 0, 1);
+       
+                       fieldptr = rfc822_fetch_field(CC->redirect_buffer, itemlist[pos+1]);
+                       if (fieldptr != NULL) {
+                               if (bmstrcasestr(fieldptr, itemlist[pos+2])) {
+                                       match = 1;
+                               }
+                               free(fieldptr);
+                       }
+       
+                       free(CC->redirect_buffer);
+                       CC->redirect_buffer = NULL;
+                       CC->redirect_len = 0;
+                       CC->redirect_alloc = 0;
+               }
+
+               pos += 3;       /* Yes, three */
+       }
+
+       else if (!strcasecmp(itemlist[pos], "KEYWORD")) {
+               /* not implemented */
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "LARGER")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       if (strlen(msg->cm_fields['M']) > atoi(itemlist[pos+1])) {
+                               match = 1;
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "NEW")) {
+               if ( (IMAP->flags[seq-1] & IMAP_RECENT) && (!(IMAP->flags[seq-1] & IMAP_SEEN))) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "OLD")) {
+               if (!(IMAP->flags[seq-1] & IMAP_RECENT)) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "ON")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       if (msg->cm_fields['T'] != NULL) {
+                               if (imap_datecmp(itemlist[pos+1],
+                                               atol(msg->cm_fields['T'])) == 0) {
+                                       match = 1;
+                               }
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "RECENT")) {
+               if (IMAP->flags[seq-1] & IMAP_RECENT) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SEEN")) {
+               if (IMAP->flags[seq-1] & IMAP_SEEN) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SENTBEFORE")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       if (msg->cm_fields['T'] != NULL) {
+                               if (imap_datecmp(itemlist[pos+1],
+                                               atol(msg->cm_fields['T'])) < 0) {
+                                       match = 1;
+                               }
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SENTON")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       if (msg->cm_fields['T'] != NULL) {
+                               if (imap_datecmp(itemlist[pos+1],
+                                               atol(msg->cm_fields['T'])) == 0) {
+                                       match = 1;
+                               }
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SENTSINCE")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       if (msg->cm_fields['T'] != NULL) {
+                               if (imap_datecmp(itemlist[pos+1],
+                                               atol(msg->cm_fields['T'])) >= 0) {
+                                       match = 1;
+                               }
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SINCE")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       if (msg->cm_fields['T'] != NULL) {
+                               if (imap_datecmp(itemlist[pos+1],
+                                               atol(msg->cm_fields['T'])) >= 0) {
+                                       match = 1;
+                               }
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SMALLER")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       if (strlen(msg->cm_fields['M']) < atoi(itemlist[pos+1])) {
+                               match = 1;
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SUBJECT")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       if (bmstrcasestr(msg->cm_fields['U'], itemlist[pos+1])) {
+                               match = 1;
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "TEXT")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       for (i='A'; i<='Z'; ++i) {
+                               if (bmstrcasestr(msg->cm_fields[i], itemlist[pos+1])) {
+                                       match = 1;
+                               }
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "TO")) {
+               if (msg == NULL) {
+                       msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
+                       need_to_free_msg = 1;
+               }
+               if (msg != NULL) {
+                       if (bmstrcasestr(msg->cm_fields['R'], itemlist[pos+1])) {
+                               match = 1;
+                       }
+               }
+               pos += 2;
+       }
+
+       /* FIXME this is b0rken.  fix it. */
+       else if (imap_is_message_set(itemlist[pos])) {
+               if (is_msg_in_sequence_set(itemlist[pos], seq)) {
+                       match = 1;
+               }
+               pos += 1;
+       }
+
+       /* FIXME this is b0rken.  fix it. */
+       else if (!strcasecmp(itemlist[pos], "UID")) {
+               if (is_msg_in_sequence_set(itemlist[pos+1], IMAP->msgids[seq-1])) {
+                       match = 1;
+               }
+               pos += 2;
+       }
+
+       /* Now here come the 'UN' criteria.  Why oh why do we have to
+        * implement *both* the 'UN' criteria *and* the 'NOT' keyword?  Why
+        * can't there be *one* way to do things?  More gratuitous complexity.
+        */
+
+       else if (!strcasecmp(itemlist[pos], "UNANSWERED")) {
+               if ((IMAP->flags[seq-1] & IMAP_ANSWERED) == 0) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "UNDELETED")) {
+               if ((IMAP->flags[seq-1] & IMAP_DELETED) == 0) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "UNDRAFT")) {
+               if ((IMAP->flags[seq-1] & IMAP_DRAFT) == 0) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "UNFLAGGED")) {
+               if ((IMAP->flags[seq-1] & IMAP_FLAGGED) == 0) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "UNKEYWORD")) {
+               /* FIXME */
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "UNSEEN")) {
+               if ((IMAP->flags[seq-1] & IMAP_SEEN) == 0) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       /* Remember to negate if we were told to */
+       if (is_not) {
+               match = !match;
+       }
+
+       /* Keep going if there are more criteria! */
+       if (pos < num_items) {
+
+               if (is_or) {
+                       match = (match || imap_do_search_msg(seq, msg,
+                               num_items - pos, &itemlist[pos], is_uid));
+               }
+               else {
+                       match = (match && imap_do_search_msg(seq, msg,
+                               num_items - pos, &itemlist[pos], is_uid));
+               }
+
+       }
+
+       if (need_to_free_msg) {
+               CtdlFreeMessage(msg);
+       }
+       return(match);
+}
+
+
+/*
+ * imap_search() calls imap_do_search() to do its actual work, once it's
+ * validated and boiled down the request a bit.
+ */
+void imap_do_search(int num_items, char **itemlist, int is_uid) {
+       int i, j, k;
+       int fts_num_msgs = 0;
+       long *fts_msgs = NULL;
+       int is_in_list = 0;
+       int num_results = 0;
+
+       /* If there is a BODY search criterion in the query, use our full
+        * text index to disqualify messages that don't have any chance of
+        * matching.  (Only do this if the index is enabled!!)
+        */
+       if (config.c_enable_fulltext) for (i=0; i<(num_items-1); ++i) {
+               if (!strcasecmp(itemlist[i], "BODY")) {
+                       ft_search(&fts_num_msgs, &fts_msgs, itemlist[i+1]);
+                       if (fts_num_msgs > 0) {
+                               for (j=0; j < IMAP->num_msgs; ++j) {
+                                       if (IMAP->flags[j] & IMAP_SELECTED) {
+                                               is_in_list = 0;
+                                               for (k=0; k<fts_num_msgs; ++k) {
+                                                       if (IMAP->msgids[j] == fts_msgs[k]) {
+                                                               ++is_in_list;
+                                                       }
+                                               }
+                                       }
+                                       if (!is_in_list) {
+                                               IMAP->flags[j] = IMAP->flags[j] & ~IMAP_SELECTED;
+                                       }
+                               }
+                       }
+                       else {          /* no hits on the index; disqualify every message */
+                               for (j=0; j < IMAP->num_msgs; ++j) {
+                                       IMAP->flags[j] = IMAP->flags[j] & ~IMAP_SELECTED;
+                               }
+                       }
+                       if (fts_msgs) {
+                               free(fts_msgs);
+                       }
+               }
+       }
+
+       /* Now go through the messages and apply all search criteria. */
+       buffer_output();
+       cprintf("* SEARCH ");
+       if (IMAP->num_msgs > 0)
+        for (i = 0; i < IMAP->num_msgs; ++i)
+         if (IMAP->flags[i] & IMAP_SELECTED) {
+               if (imap_do_search_msg(i+1, NULL, num_items, itemlist, is_uid)) {
+                       if (num_results != 0) {
+                               cprintf(" ");
+                       }
+                       if (is_uid) {
+                               cprintf("%ld", IMAP->msgids[i]);
+                       }
+                       else {
+                               cprintf("%d", i+1);
+                       }
+                       ++num_results;
+               }
+       }
+       cprintf("\r\n");
+       unbuffer_output();
+}
+
+
+/*
+ * This function is called by the main command loop.
+ */
+void imap_search(int num_parms, char *parms[]) {
+       int i;
+
+       if (num_parms < 3) {
+               cprintf("%s BAD invalid parameters\r\n", parms[0]);
+               return;
+       }
+
+       for (i = 0; i < IMAP->num_msgs; ++i) {
+               IMAP->flags[i] |= IMAP_SELECTED;
+       }
+
+       imap_do_search(num_parms-2, &parms[2], 0);
+       cprintf("%s OK SEARCH completed\r\n", parms[0]);
+}
+
+/*
+ * This function is called by the main command loop.
+ */
+void imap_uidsearch(int num_parms, char *parms[]) {
+       int i;
+
+       if (num_parms < 4) {
+               cprintf("%s BAD invalid parameters\r\n", parms[0]);
+               return;
+       }
+
+       for (i = 0; i < IMAP->num_msgs; ++i) {
+               IMAP->flags[i] |= IMAP_SELECTED;
+       }
+
+       imap_do_search(num_parms-3, &parms[3], 1);
+       cprintf("%s OK UID SEARCH completed\r\n", parms[0]);
+}
+
+
diff --git a/citadel/modules/imap/imap_search.h b/citadel/modules/imap/imap_search.h
new file mode 100644 (file)
index 0000000..dd0195c
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * $Id$
+ *
+ */
+
+void imap_search(int num_parms, char *parms[]);
+void imap_uidsearch(int num_parms, char *parms[]);
diff --git a/citadel/modules/imap/imap_store.c b/citadel/modules/imap/imap_store.c
new file mode 100644 (file)
index 0000000..7e16378
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * $Id$
+ *
+ * Implements the STORE command in IMAP.
+ *
+ */
+
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.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 <sys/wait.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include "citadel.h"
+#include "server.h"
+#include "sysdep_decls.h"
+#include "citserver.h"
+#include "support.h"
+#include "config.h"
+#include "room_ops.h"
+#include "user_ops.h"
+#include "policy.h"
+#include "database.h"
+#include "msgbase.h"
+#include "tools.h"
+#include "internet_addressing.h"
+#include "serv_imap.h"
+#include "imap_tools.h"
+#include "imap_fetch.h"
+#include "imap_store.h"
+#include "genstamp.h"
+
+
+
+
+
+
+/*
+ * imap_do_store() calls imap_do_store_msg() to tweak the settings of
+ * an individual message.
+ *
+ * We also implement the ".SILENT" protocol option here.  :(
+ */
+void imap_do_store_msg(int seq, char *oper, unsigned int bits_to_twiddle) {
+
+
+       if (!strncasecmp(oper, "FLAGS", 5)) {
+               IMAP->flags[seq] &= IMAP_MASK_SYSTEM;
+               IMAP->flags[seq] |= bits_to_twiddle;
+       }
+       else if (!strncasecmp(oper, "+FLAGS", 6)) {
+               IMAP->flags[seq] |= bits_to_twiddle;
+       }
+       else if (!strncasecmp(oper, "-FLAGS", 6)) {
+               IMAP->flags[seq] &= (~bits_to_twiddle);
+       }
+}
+
+
+
+/*
+ * imap_store() calls imap_do_store() to perform the actual bit twiddling
+ * on the flags.
+ */
+void imap_do_store(int num_items, char **itemlist) {
+       int i, j;
+       unsigned int bits_to_twiddle = 0;
+       char *oper;
+       char flag[32];
+       char whichflags[256];
+       char num_flags;
+       int silent = 0;
+       long *ss_msglist;
+       int num_ss = 0;
+       int last_item_twiddled = (-1);
+
+       if (num_items < 2) return;
+       oper = itemlist[0];
+       if (bmstrcasestr(oper, ".SILENT")) {
+               silent = 1;
+       }
+
+       /*
+        * ss_msglist is an array of message numbers to manipulate.  We
+        * are going to supply this array to CtdlSetSeen() later.
+        */
+       ss_msglist = malloc(IMAP->num_msgs * sizeof(long));
+       if (ss_msglist == NULL) return;
+
+       /*
+        * Ok, go ahead and parse the flags.
+        */
+       for (i=1; i<num_items; ++i) {
+               strcpy(whichflags, itemlist[i]);
+               if (whichflags[0]=='(') {
+                       safestrncpy(whichflags, &whichflags[1], 
+                               sizeof whichflags);
+               }
+               if (whichflags[strlen(whichflags)-1]==')') {
+                       whichflags[strlen(whichflags)-1]=0;
+               }
+               striplt(whichflags);
+
+               /* A client might twiddle more than one bit at a time.
+                * Note that we check for the flag names without the leading
+                * backslash because imap_parameterize() strips them out.
+                */
+               num_flags = num_tokens(whichflags, ' ');
+               for (j=0; j<num_flags; ++j) {
+                       extract_token(flag, whichflags, j, ' ', sizeof flag);
+
+                       if ((!strcasecmp(flag, "\\Deleted"))
+                          || (!strcasecmp(flag, "Deleted"))) {
+                               if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom()) {
+                                       bits_to_twiddle |= IMAP_DELETED;
+                               }
+                       }
+                       if ((!strcasecmp(flag, "\\Seen"))
+                          || (!strcasecmp(flag, "Seen"))) {
+                               bits_to_twiddle |= IMAP_SEEN;
+                       }
+                       if ((!strcasecmp(flag, "\\Answered")) 
+                          || (!strcasecmp(flag, "\\Answered"))) {
+                               bits_to_twiddle |= IMAP_ANSWERED;
+                       }
+               }
+       }
+
+       if (IMAP->num_msgs > 0) {
+               for (i = 0; i < IMAP->num_msgs; ++i) {
+                       if (IMAP->flags[i] & IMAP_SELECTED) {
+                               last_item_twiddled = i;
+
+                               ss_msglist[num_ss++] = IMAP->msgids[i];
+                               imap_do_store_msg(i, oper, bits_to_twiddle);
+
+                               if (!silent) {
+                                       cprintf("* %d FETCH (", i+1);
+                                       imap_fetch_flags(i);
+                                       cprintf(")\r\n");
+                               }
+
+                       }
+               }
+       }
+
+       /*
+        * Now manipulate the database -- all in one shot.
+        */
+       if ( (last_item_twiddled >= 0) && (num_ss > 0) ) {
+
+               if (bits_to_twiddle & IMAP_SEEN) {
+                       CtdlSetSeen(ss_msglist, num_ss,
+                               ((IMAP->flags[last_item_twiddled] & IMAP_SEEN) ? 1 : 0),
+                               ctdlsetseen_seen,
+                               NULL, NULL
+                       );
+               }
+
+               if (bits_to_twiddle & IMAP_ANSWERED) {
+                       CtdlSetSeen(ss_msglist, num_ss,
+                               ((IMAP->flags[last_item_twiddled] & IMAP_ANSWERED) ? 1 : 0),
+                               ctdlsetseen_answered,
+                               NULL, NULL
+                       );
+               }
+
+       }
+
+       free(ss_msglist);
+
+       /*
+        * The following two commands implement "instant expunge" if enabled.
+        */
+       if (config.c_instant_expunge) {
+               imap_do_expunge();
+               imap_rescan_msgids();
+       }
+
+}
+
+
+/*
+ * This function is called by the main command loop.
+ */
+void imap_store(int num_parms, char *parms[]) {
+       char items[1024];
+       char *itemlist[256];
+       int num_items;
+       int i;
+
+       if (num_parms < 3) {
+               cprintf("%s BAD invalid parameters\r\n", parms[0]);
+               return;
+       }
+
+       if (imap_is_message_set(parms[2])) {
+               imap_pick_range(parms[2], 0);
+       }
+       else {
+               cprintf("%s BAD invalid parameters\r\n", parms[0]);
+               return;
+       }
+
+       strcpy(items, "");
+       for (i=3; i<num_parms; ++i) {
+               strcat(items, parms[i]);
+               if (i < (num_parms-1)) strcat(items, " ");
+       }
+
+       num_items = imap_extract_data_items(itemlist, items);
+       if (num_items < 1) {
+               cprintf("%s BAD invalid data item list\r\n", parms[0]);
+               return;
+       }
+
+       imap_do_store(num_items, itemlist);
+       cprintf("%s OK STORE completed\r\n", parms[0]);
+}
+
+/*
+ * This function is called by the main command loop.
+ */
+void imap_uidstore(int num_parms, char *parms[]) {
+       char items[1024];
+       char *itemlist[256];
+       int num_items;
+       int i;
+
+       if (num_parms < 4) {
+               cprintf("%s BAD invalid parameters\r\n", parms[0]);
+               return;
+       }
+
+       if (imap_is_message_set(parms[3])) {
+               imap_pick_range(parms[3], 1);
+       }
+       else {
+               cprintf("%s BAD invalid parameters\r\n", parms[0]);
+               return;
+       }
+
+       strcpy(items, "");
+       for (i=4; i<num_parms; ++i) {
+               strcat(items, parms[i]);
+               if (i < (num_parms-1)) strcat(items, " ");
+       }
+
+       num_items = imap_extract_data_items(itemlist, items);
+       if (num_items < 1) {
+               cprintf("%s BAD invalid data item list\r\n", parms[0]);
+               return;
+       }
+
+       imap_do_store(num_items, itemlist);
+       cprintf("%s OK UID STORE completed\r\n", parms[0]);
+}
+
+
diff --git a/citadel/modules/imap/imap_store.h b/citadel/modules/imap/imap_store.h
new file mode 100644 (file)
index 0000000..d3aa734
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * $Id$
+ *
+ */
+
+void imap_store(int num_parms, char *parms[]);
+void imap_uidstore(int num_parms, char *parms[]);
diff --git a/citadel/modules/imap/imap_tools.c b/citadel/modules/imap/imap_tools.c
new file mode 100644 (file)
index 0000000..c127550
--- /dev/null
@@ -0,0 +1,810 @@
+/*
+ * $Id$
+ *
+ * Utility functions for the IMAP module.
+ *
+ * Note: most of the UTF7 and UTF8 handling in here was lifted from Evolution.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "citadel.h"
+#include "sysdep_decls.h"
+#include "tools.h"
+#include "room_ops.h"
+#include "internet_addressing.h"
+#include "imap_tools.h"
+
+
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+
+/* String handling helpers */
+
+/* This code uses some pretty narsty string manipulation. To make everything
+ * manageable, we use this semi-high-level string manipulation API. Strings are
+ * always \0-terminated, despite the fact that we keep track of the size. */
+
+struct string {
+       char* buffer;
+       int maxsize;
+       int size;
+};
+
+static void string_init(struct string* s, char* buf, int bufsize)
+{
+       s->buffer = buf;
+       s->maxsize = bufsize-1;
+       s->size = strlen(buf);
+}
+
+static char* string_end(struct string* s)
+{
+       return s->buffer + s->size;
+}
+
+/* Append a UTF8 string of a particular length (in bytes). -1 to autocalculate. */
+
+static void string_append_sn(struct string* s, char* p, int len)
+{
+       if (len == -1)
+               len = strlen(p);
+       if ((s->size+len) > s->maxsize)
+               len = s->maxsize - s->size;
+       memcpy(s->buffer + s->size, p, len);
+       s->size += len;
+       s->buffer[s->size] = '\0';
+}
+
+/* As above, always autocalculate. */
+
+#define string_append_s(s, p) string_append_sn((s), (p), -1)
+
+/* Appends a UTF8 character --- which may make the size change by more than 1!
+ * If the string overflows, the last character may become invalid. */
+
+static void string_append_c(struct string* s, int c)
+{
+       char buf[5];
+       int len = 0;
+
+       /* Don't do anything if there's no room. */
+
+       if (s->size == s->maxsize)
+               return;
+
+       if (c <= 0x7F)
+       {
+               /* This is the most common case, so we optimise it. */
+
+               s->buffer[s->size++] = c;
+               s->buffer[s->size] = 0;
+               return;
+       }
+       else if (c <= 0x7FF)
+       {
+               buf[0] = 0xC0 | (c >> 6);
+               buf[1] = 0x80 | (c & 0x3F);
+               len = 2;
+       }
+       else if (c <= 0xFFFF)
+       {
+               buf[0] = 0xE0 | (c >> 12);
+               buf[1] = 0x80 | ((c >> 6) & 0x3f);
+               buf[2] = 0x80 | (c & 0x3f);
+               len = 3;
+       }
+       else
+       {
+               buf[0] = 0xf0 | c >> 18;
+               buf[1] = 0x80 | ((c >> 12) & 0x3f);
+               buf[2] = 0x80 | ((c >> 6) & 0x3f);
+               buf[3] = 0x80 | (c & 0x3f);
+               len = 4;
+       }
+
+       string_append_sn(s, buf, len);
+}      
+
+/* Reads a UTF8 character from a char*, advancing the pointer. */
+
+int utf8_getc(char** ptr)
+{
+       unsigned char* p = (unsigned char*) *ptr;
+       unsigned char c, r;
+       int v, m;
+
+       for (;;)
+       {
+               r = *p++;
+       loop:
+               if (r < 0x80)
+               {
+                       *ptr = p;
+                       v = r;
+                       break;
+               }
+               else if (r < 0xf8)
+               {
+                       /* valid start char? (max 4 octets) */
+                       v = r;
+                       m = 0x7f80;     /* used to mask out the length bits */
+                       do {
+                               c = *p++;
+                               if ((c & 0xc0) != 0x80)
+                               {
+                                       r = c;
+                                       goto loop;
+                               }
+                               v = (v<<6) | (c & 0x3f);
+                               r<<=1;
+                               m<<=5;
+                       } while (r & 0x40);
+                       
+                       *ptr = p;
+
+                       v &= ~m;
+                       break;
+               }
+       }
+
+       return v;
+}
+
+/* IMAP name safety */
+
+/* IMAP has certain special requirements in its character set, which means we
+ * have to do a fair bit of work to convert Citadel's UTF8 strings to IMAP
+ * strings. The next two routines (and their data tables) do that.
+ */
+
+static char *utf7_alphabet =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
+
+static unsigned char utf7_rank[256] = {
+       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3E,0x3F,0xFF,0xFF,0xFF,
+       0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+       0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,
+       0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF,
+       0xFF,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
+       0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0xFF,0xFF,0xFF,0xFF,0xFF,
+       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+};
+
+/* Base64 helpers. */
+
+static void utf7_closeb64(struct string* out, int v, int i)
+{
+       int x;
+
+       if (i > 0)
+       {
+               x = (v << (6-i)) & 0x3F;
+               string_append_c(out, utf7_alphabet[x]);
+       }
+       string_append_c(out, '-');
+}
+
+/* Convert from a Citadel name to an IMAP-safe name. Returns the end
+ * of the destination.
+ */
+static char* toimap(char* destp, char* destend, char* src)
+{
+       struct string dest;
+       int state = 0;
+       int v = 0;
+       int i = 0;
+
+       *destp = 0;
+       string_init(&dest, destp, destend-destp);
+       /* lprintf(CTDL_DEBUG, "toimap %s\r\n", src); */
+
+       for (;;)
+       {
+               int c = utf8_getc(&src);
+               if (c == '\0')
+                       break;
+
+               if (c >= 0x20 && c <= 0x7e)
+               {
+                       if (state == 1)
+                       {
+                               utf7_closeb64(&dest, v, i);
+                               state = 0;
+                               i = 0;
+                       }
+
+                       switch (c)
+                       {
+                               case '&':
+                                       string_append_sn(&dest, "&-", 2);
+                                       break;
+
+                               case '/':
+                                       /* Citadel extension: / becomes |, because /
+                                        * isn't valid as part of an IMAP name. */
+
+                                       c = '|';
+                                       goto defaultcase;
+
+                               case '\\':
+                                       /* Citadel extension: backslashes mark folder
+                                        * seperators in the IMAP subfolder emulation
+                                        * hack. We turn them into / characters,
+                                        * *except* if it's the last character in the
+                                        * string. */
+
+                                       if (*src != '\0')
+                                               c = '/';
+                                       /* fall through */
+
+                               default:
+                               defaultcase:
+                                       string_append_c(&dest, c);
+                       }
+               }
+               else
+               {
+                       if (state == 0)
+                       {
+                               string_append_c(&dest, '&');
+                               state = 1;
+                       }
+                       v = (v << 16) | c;
+                       i += 16;
+                       while (i >= 6)
+                       {
+                               int x = (v >> (i-6)) & 0x3f;
+                               string_append_c(&dest, utf7_alphabet[x]);
+                               i -= 6;
+                       }
+               }
+       }
+
+       if (state == 1)
+               utf7_closeb64(&dest, v, i);
+       /* lprintf(CTDL_DEBUG, "    -> %s\r\n", destp); */
+       return string_end(&dest);
+}
+
+/* Convert from an IMAP-safe name back into a Citadel name. Returns the end of the destination. */
+
+static int cfrommap(int c);
+static char* fromimap(char* destp, char* destend, char* src)
+{
+       struct string dest;
+       unsigned char *p = (unsigned char*) src;
+       int v = 0;
+       int i = 0;
+       int state = 0;
+       int c;
+
+       *destp = 0;
+       string_init(&dest, destp, destend-destp);
+       /* lprintf(CTDL_DEBUG, "fromimap %s\r\n", src); */
+
+       do {
+               c = *p++;
+               switch (state)
+               {
+                       case 0:
+                               /* US-ASCII characters. */
+                               
+                               if (c == '&')
+                                       state = 1;
+                               else
+                                       string_append_c(&dest, cfrommap(c));
+                               break;
+
+                       case 1:
+                               if (c == '-')
+                               {
+                                       string_append_c(&dest, '&');
+                                       state = 0;
+                               }
+                               else if (utf7_rank[c] != 0xff)
+                               {
+                                       v = utf7_rank[c];
+                                       i = 6;
+                                       state = 2;
+                               }
+                               else
+                               {
+                                       /* invalid char */
+                                       string_append_sn(&dest, "&-", 2);
+                                       state = 0;
+                               }
+                               break;
+                               
+                       case 2:
+                               if (c == '-')
+                                       state = 0;
+                               else if (utf7_rank[c] != 0xFF)
+                               {
+                                       v = (v<<6) | utf7_rank[c];
+                                       i += 6;
+                                       if (i >= 16)
+                                       {
+                                               int x = (v >> (i-16)) & 0xFFFF;
+                                               string_append_c(&dest, cfrommap(x));
+                                               i -= 16;
+                                       }
+                               }
+                               else
+                               {
+                                       string_append_c(&dest, cfrommap(c));
+                                       state = 0;
+                               }
+                               break;
+                       }
+       } while (c != '\0');
+
+       /* lprintf(CTDL_DEBUG, "      -> %s\r\n", destp); */
+       return string_end(&dest);
+}
+
+/* Undoes the special character conversion. */
+
+static int cfrommap(int c)
+{
+       switch (c)
+       {
+               case '|':       return '/';
+               case '/':       return '\\';
+       }
+       return c;               
+}
+
+/* Output a string to the IMAP client, either as a literal or quoted.
+ * (We do a literal if it has any double-quotes or backslashes.) */
+
+void imap_strout(char *buf)
+{
+       int i;
+       int is_literal = 0;
+       int len;
+
+       if (buf == NULL) {      /* yeah, we handle this */
+               cprintf("NIL");
+               return;
+       }
+
+       len = strlen(buf);
+       for (i = 0; i < len; ++i) {
+               if ((buf[i] == '\"') || (buf[i] == '\\'))
+                       is_literal = 1;
+       }
+
+       if (is_literal) {
+               cprintf("{%ld}\r\n%s", (long)strlen(buf), buf);
+       } else {
+               cprintf("\"%s\"", buf);
+       }
+}
+
+/* Break a command down into tokens, unquoting any escaped characters. */
+
+int imap_parameterize(char** args, char* in)
+{
+       char* out = in;
+       int num = 0;
+
+       for (;;)
+       {
+               /* Skip whitespace. */
+
+               while (isspace(*in))
+                       in++;
+               if (*in == 0)
+                       break;
+
+               /* Found the start of a token. */
+               
+               args[num++] = out;
+
+               /* Read in the token. */
+
+               for (;;)
+               {
+                       int c = *in++;
+                       if (isspace(c))
+                               break;
+                       
+                       if (c == '\"')
+                       {
+                               /* Found a quoted section. */
+
+                               for (;;)
+                               {
+                                       c = *in++;
+                                       if (c == '\"')
+                                               break;
+                                       else if (c == '\\')
+                                               c = *in++;
+
+                                       *out++ = c;
+                                       if (c == 0)
+                                               return num;
+                               }
+                       }
+                       else if (c == '\\')
+                       {
+                               c = *in++;
+                               *out++ = c;
+                       }
+                       else
+                               *out++ = c;
+
+                       if (c == 0)
+                               return num;
+               }
+               *out++ = '\0';
+       }
+
+       return num;
+}
+
+/* Convert a struct ctdlroom to an IMAP-compatible mailbox name. */
+
+void imap_mailboxname(char *buf, int bufsize, struct ctdlroom *qrbuf)
+{
+       char* bufend = buf+bufsize;
+       struct floor *fl;
+       char* p = buf;
+
+       /* For mailboxes, just do it straight.
+        * Do the Cyrus-compatible thing: all private folders are
+        * subfolders of INBOX. */
+
+       if (qrbuf->QRflags & QR_MAILBOX)
+       {
+               if (strcasecmp(qrbuf->QRname+11, MAILROOM) == 0)
+                       p = toimap(p, bufend, "INBOX");
+               else
+               {
+                       p = toimap(p, bufend, "INBOX");
+                       if (p < bufend)
+                               *p++ = '/';
+                       p = toimap(p, bufend, qrbuf->QRname+11);
+               }
+       }
+       else
+       {
+               /* Otherwise, prefix the floor name as a "public folders" moniker. */
+
+               fl = cgetfloor(qrbuf->QRfloor);
+               p = toimap(p, bufend, fl->f_name);
+               if (p < bufend)
+                       *p++ = '/';
+               p = toimap(p, bufend, qrbuf->QRname);
+       }
+}
+
+/*
+ * Convert an inputted folder name to our best guess as to what an equivalent
+ * room name should be.
+ *
+ * If an error occurs, it returns -1.  Otherwise...
+ *
+ * The lower eight bits of the return value are the floor number on which the
+ * room most likely resides.   The upper eight bits may contain flags,
+ * including IR_MAILBOX if we're dealing with a personal room.
+ *
+ */
+
+int imap_roomname(char *rbuf, int bufsize, char *foldername)
+{
+       int levels;
+       char floorname[256];
+       char roomname[ROOMNAMELEN];
+       int i;
+       struct floor *fl;
+       int ret = (-1);
+
+       if (foldername == NULL)
+               return(-1);
+
+       /* Unmunge the entire string into the output buffer. */
+
+       fromimap(rbuf, rbuf+bufsize, foldername);
+
+       /* Is this an IMAP inbox? */
+
+       if (strncasecmp(rbuf, "INBOX", 5) == 0)
+       {
+               if (rbuf[5] == 0)
+               {
+                       /* It's the system inbox. */
+
+                       safestrncpy(rbuf, MAILROOM, bufsize);
+                       ret = (0 | IR_MAILBOX);
+                       goto exit;
+               }
+               else if (rbuf[5] == FDELIM)
+               {
+                       /* It's another personal mail folder. */
+
+                       safestrncpy(rbuf, rbuf+6, bufsize);
+                       ret = (0 | IR_MAILBOX);
+                       goto exit;
+               }
+
+               /* If we get here, the folder just happens to start with INBOX
+                * --- fall through. */
+       }
+
+       /* Is this a multi-level room name? */
+
+       levels = num_tokens(rbuf, FDELIM);
+       if (levels > 1)
+       {
+               /* Extract the main room name. */
+               
+               extract_token(floorname, rbuf, 0, FDELIM, sizeof floorname);
+               strcpy(roomname, &rbuf[strlen(floorname)+1]);
+
+               /* Try and find it on any floor. */
+               
+               for (i = 0; i < MAXFLOORS; ++i)
+               {
+                       fl = cgetfloor(i);
+                       if (fl->f_flags & F_INUSE)
+                       {
+                               if (strcasecmp(floorname, fl->f_name) == 0)
+                               {
+                                       /* Got it! */
+
+                                       safestrncpy(rbuf, roomname, bufsize);
+                                       ret = i;
+                                       goto exit;
+                               }
+                       }
+               }
+       }
+
+       /* Meh. It's either not a multi-level room name, or else we
+        * couldn't find it.
+        */
+       ret = (0 | IR_MAILBOX);
+
+exit:
+       lprintf(CTDL_DEBUG, "(That translates to \"%s\")\n", rbuf);
+       return(ret);
+}
+
+/*
+ * Output a struct internet_address_list in the form an IMAP client wants
+ */
+void imap_ial_out(struct internet_address_list *ialist)
+{
+       struct internet_address_list *iptr;
+
+       if (ialist == NULL) {
+               cprintf("NIL");
+               return;
+       }
+       cprintf("(");
+
+       for (iptr = ialist; iptr != NULL; iptr = iptr->next) {
+               cprintf("(");
+               imap_strout(iptr->ial_name);
+               cprintf(" NIL ");
+               imap_strout(iptr->ial_user);
+               cprintf(" ");
+               imap_strout(iptr->ial_node);
+               cprintf(")");
+       }
+
+       cprintf(")");
+}
+
+
+
+/*
+ * Determine whether the supplied string is a valid message set.
+ * If the string contains only numbers, colons, commas, and asterisks,
+ * return 1 for a valid message set.  If any other character is found, 
+ * return 0.
+ */
+int imap_is_message_set(char *buf)
+{
+       int i;
+
+       if (buf == NULL)
+               return (0);     /* stupidity checks */
+       if (strlen(buf) == 0)
+               return (0);
+
+       if (!strcasecmp(buf, "ALL"))
+               return (1);     /* macro?  why?  */
+
+       for (i = 0; i < strlen(buf); ++i) {     /* now start the scan */
+               if (
+                          (!isdigit(buf[i]))
+                          && (buf[i] != ':')
+                          && (buf[i] != ',')
+                          && (buf[i] != '*')
+                   )
+                       return (0);
+       }
+
+       return (1);             /* looks like we're good */
+}
+
+
+/*
+ * imap_match.c, based on wildmat.c from INN
+ * hacked for Citadel/IMAP by Daniel Malament
+ */
+
+/* note: not all return statements use these; don't change them */
+#define WILDMAT_TRUE   1
+#define WILDMAT_FALSE  0
+#define WILDMAT_ABORT  -1
+#define WILDMAT_DELIM  '/'
+
+/*
+ * Match text and p, return TRUE, FALSE, or ABORT.
+ */
+static int do_imap_match(const char *supplied_text, const char *supplied_p)
+{
+       int matched, i;
+       char lcase_text[SIZ], lcase_p[SIZ];
+       char *text = lcase_text;
+       char *p = lcase_p;
+
+       /* Copy both strings and lowercase them, in order to
+        * make this entire operation case-insensitive.
+        */
+       for (i=0; i<=strlen(supplied_text); ++i)
+               lcase_text[i] = tolower(supplied_text[i]);
+       for (i=0; i<=strlen(supplied_p); ++i)
+               p[i] = tolower(supplied_p[i]);
+
+       /* Start matching */
+       for (; *p; text++, p++) {
+               if ((*text == '\0') && (*p != '*') && (*p != '%')) {
+                       return WILDMAT_ABORT;
+               }
+               switch (*p) {
+               default:
+                       if (*text != *p) {
+                               return WILDMAT_FALSE;
+                       }
+                       continue;
+               case '*':
+star:
+                       while (++p, ((*p == '*') || (*p == '%'))) {
+                               /* Consecutive stars or %'s act
+                                * just like one star.
+                                */
+                               continue;
+                       }
+                       if (*p == '\0') {
+                               /* Trailing star matches everything. */
+                               return WILDMAT_TRUE;
+                       }
+                       while (*text) {
+                               if ((matched = do_imap_match(text++, p))
+                                  != WILDMAT_FALSE) {
+                                       return matched;
+                               }
+                       }
+                       return WILDMAT_ABORT;
+               case '%':
+                       while (++p, ((*p == '*') || (*p == '%'))) {
+                               /* Consecutive %'s act just like one, but even
+                                * a single star makes the sequence act like
+                                * one star, instead.
+                                */
+                               if (*p == '*') {
+                                       goto star;
+                               }
+                               continue;
+                       }
+                       if (*p == '\0') {
+                               /*
+                                * Trailing % matches everything
+                                * without a delimiter.
+                                */
+                               while (*text) {
+                                       if (*text == WILDMAT_DELIM) {
+                                               return WILDMAT_FALSE;
+                                       }
+                                       text++;
+                               }
+                               return WILDMAT_TRUE;
+                       }
+                       while (*text && (*(text - 1) != WILDMAT_DELIM)) {
+                               if ((matched = do_imap_match(text++, p))
+                                  != WILDMAT_FALSE) {
+                                       return matched;
+                               }
+                       }
+                       return WILDMAT_ABORT;
+               }
+       }
+
+       return (*text == '\0');
+}
+
+
+
+/*
+ * Support function for mailbox pattern name matching in LIST and LSUB
+ * Returns nonzero if the supplied mailbox name matches the supplied pattern.
+ */
+int imap_mailbox_matches_pattern(char *pattern, char *mailboxname)
+{
+       /* handle just-star case quickly */
+       if ((pattern[0] == '*') && (pattern[1] == '\0')) {
+               return WILDMAT_TRUE;
+       }
+       return (do_imap_match(mailboxname, pattern) == WILDMAT_TRUE);
+}
+
+
+
+/*
+ * Compare an IMAP date string (date only, no time) to the date found in
+ * a Unix timestamp.
+ */
+int imap_datecmp(char *datestr, time_t msgtime) {
+       char daystr[256];
+       char monthstr[256];
+       char yearstr[256];
+       int i;
+       int day, month, year;
+       int msgday, msgmonth, msgyear;
+       struct tm msgtm;
+
+       if (datestr == NULL) return(0);
+
+       /* Expecting a date in the form dd-Mmm-yyyy */
+       extract_token(daystr, datestr, 0, '-', sizeof daystr);
+       extract_token(monthstr, datestr, 1, '-', sizeof monthstr);
+       extract_token(yearstr, datestr, 2, '-', sizeof yearstr);
+
+       day = atoi(daystr);
+       year = atoi(yearstr);
+       month = 0;
+       for (i=0; i<12; ++i) {
+               if (!strcasecmp(monthstr, ascmonths[i])) {
+                       month = i;
+               }
+       }
+
+       /* Extract day/month/year from message timestamp */
+       localtime_r(&msgtime, &msgtm);
+       msgday = msgtm.tm_mday;
+       msgmonth = msgtm.tm_mon;
+       msgyear = msgtm.tm_year + 1900;
+
+       /* Now start comparing */
+
+       if (year < msgyear) return(+1);
+       if (year > msgyear) return(-1);
+
+       if (month < msgmonth) return(+1);
+       if (month > msgmonth) return(-1);
+
+       if (day < msgday) return(+1);
+       if (day > msgday) return(-1);
+
+       return(0);
+}
+
diff --git a/citadel/modules/imap/serv_imap.c b/citadel/modules/imap/serv_imap.c
new file mode 100644 (file)
index 0000000..a391e91
--- /dev/null
@@ -0,0 +1,1596 @@
+/*
+ * $Id$ 
+ *
+ * IMAP server for the Citadel system
+ * Copyright (C) 2000-2007 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
+ * is perfect.  Indeed, with so much gratuitous complexity, *all* IMAP
+ * implementations have bugs.
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.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 <sys/wait.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include "citadel.h"
+#include "server.h"
+#include "citserver.h"
+#include "support.h"
+#include "config.h"
+#include "room_ops.h"
+#include "user_ops.h"
+#include "policy.h"
+#include "database.h"
+#include "msgbase.h"
+#include "tools.h"
+#include "internet_addressing.h"
+#include "serv_imap.h"
+#include "imap_tools.h"
+#include "imap_list.h"
+#include "imap_fetch.h"
+#include "imap_search.h"
+#include "imap_store.h"
+#include "imap_acl.h"
+#include "imap_metadata.h"
+#include "imap_misc.h"
+
+
+#include "ctdl_module.h"
+
+
+/* imap_rename() uses this struct containing list of rooms to rename */
+struct irl {
+       struct irl *next;
+       char irl_oldroom[ROOMNAMELEN];
+       char irl_newroom[ROOMNAMELEN];
+       int irl_newfloor;
+};
+
+/* Data which is passed between imap_rename() and imap_rename_backend() */
+struct irlparms {
+       char *oldname;
+       char *newname;
+       struct irl **irl;
+};
+
+
+/*
+ * If there is a message ID map in memory, free it
+ */
+void imap_free_msgids(void)
+{
+       if (IMAP->msgids != NULL) {
+               free(IMAP->msgids);
+               IMAP->msgids = NULL;
+               IMAP->num_msgs = 0;
+               IMAP->num_alloc = 0;
+       }
+       if (IMAP->flags != NULL) {
+               free(IMAP->flags);
+               IMAP->flags = NULL;
+       }
+       IMAP->last_mtime = (-1);
+}
+
+
+/*
+ * If there is a transmitted message in memory, free it
+ */
+void imap_free_transmitted_message(void)
+{
+       if (IMAP->transmitted_message != NULL) {
+               free(IMAP->transmitted_message);
+               IMAP->transmitted_message = NULL;
+               IMAP->transmitted_length = 0;
+       }
+}
+
+
+/*
+ * Set the \Seen, \Recent. and \Answered flags, based on the sequence
+ * sets stored in the visit record for this user/room.  Note that we have
+ * to parse each sequence set manually here, because calling the utility
+ * function is_msg_in_sequence_set() over and over again is too expensive.
+ *
+ * first_msg should be set to 0 to rescan the flags for every message in the
+ * room, or some other value if we're only interested in an incremental
+ * update.
+ */
+void imap_set_seen_flags(int first_msg)
+{
+       struct visit vbuf;
+       int i;
+       int num_sets;
+       int s;
+       char setstr[64], lostr[64], histr[64];
+       long lo, hi;
+
+       if (IMAP->num_msgs < 1) return;
+       CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
+
+       for (i = first_msg; i < IMAP->num_msgs; ++i) {
+               IMAP->flags[i] = IMAP->flags[i] & ~IMAP_SEEN;
+               IMAP->flags[i] |= IMAP_RECENT;
+               IMAP->flags[i] = IMAP->flags[i] & ~IMAP_ANSWERED;
+       }
+
+       /*
+        * Do the "\Seen" flag.
+        * (Any message not "\Seen" is considered "\Recent".)
+        */
+       num_sets = num_tokens(vbuf.v_seen, ',');
+       for (s=0; s<num_sets; ++s) {
+               extract_token(setstr, vbuf.v_seen, s, ',', sizeof setstr);
+
+               extract_token(lostr, setstr, 0, ':', sizeof lostr);
+               if (num_tokens(setstr, ':') >= 2) {
+                       extract_token(histr, setstr, 1, ':', sizeof histr);
+                       if (!strcmp(histr, "*")) {
+                               snprintf(histr, sizeof histr, "%ld", LONG_MAX);
+                       }
+               } 
+               else {
+                       strcpy(histr, lostr);
+               }
+               lo = atol(lostr);
+               hi = atol(histr);
+
+               for (i = first_msg; i < IMAP->num_msgs; ++i) {
+                       if ((IMAP->msgids[i] >= lo) && (IMAP->msgids[i] <= hi)){
+                               IMAP->flags[i] |= IMAP_SEEN;
+                               IMAP->flags[i] = IMAP->flags[i] & ~IMAP_RECENT;
+                       }
+               }
+       }
+
+       /* Do the ANSWERED flag */
+       num_sets = num_tokens(vbuf.v_answered, ',');
+       for (s=0; s<num_sets; ++s) {
+               extract_token(setstr, vbuf.v_answered, s, ',', sizeof setstr);
+
+               extract_token(lostr, setstr, 0, ':', sizeof lostr);
+               if (num_tokens(setstr, ':') >= 2) {
+                       extract_token(histr, setstr, 1, ':', sizeof histr);
+                       if (!strcmp(histr, "*")) {
+                               snprintf(histr, sizeof histr, "%ld", LONG_MAX);
+                       }
+               } 
+               else {
+                       strcpy(histr, lostr);
+               }
+               lo = atol(lostr);
+               hi = atol(histr);
+
+               for (i = first_msg; i < IMAP->num_msgs; ++i) {
+                       if ((IMAP->msgids[i] >= lo) && (IMAP->msgids[i] <= hi)){
+                               IMAP->flags[i] |= IMAP_ANSWERED;
+                       }
+               }
+       }
+
+}
+
+
+
+/*
+ * Back end for imap_load_msgids()
+ *
+ * Optimization: instead of calling realloc() to add each message, we
+ * allocate space in the list for REALLOC_INCREMENT messages at a time.  This
+ * allows the mapping to proceed much faster.
+ */
+void imap_add_single_msgid(long msgnum, void *userdata)
+{
+
+       ++IMAP->num_msgs;
+       if (IMAP->num_msgs > IMAP->num_alloc) {
+               IMAP->num_alloc += REALLOC_INCREMENT;
+               IMAP->msgids = realloc(IMAP->msgids,
+                                       (IMAP->num_alloc * sizeof(long)) );
+               IMAP->flags = realloc(IMAP->flags,
+                                       (IMAP->num_alloc * sizeof(long)) );
+       }
+       IMAP->msgids[IMAP->num_msgs - 1] = msgnum;
+       IMAP->flags[IMAP->num_msgs - 1] = 0;
+}
+
+
+
+/*
+ * Set up a message ID map for the current room (folder)
+ */
+void imap_load_msgids(void)
+{
+       struct cdbdata *cdbfr;
+
+       if (IMAP->selected == 0) {
+               lprintf(CTDL_ERR,
+                       "imap_load_msgids() can't run; no room selected\n");
+               return;
+       }
+
+       imap_free_msgids();     /* If there was already a map, free it */
+
+       /* Load the message list */
+       cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
+       if (cdbfr != NULL) {
+               IMAP->msgids = malloc(cdbfr->len);
+               memcpy(IMAP->msgids, cdbfr->ptr, cdbfr->len);
+               IMAP->num_msgs = cdbfr->len / sizeof(long);
+               IMAP->num_alloc = cdbfr->len / sizeof(long);
+               cdb_free(cdbfr);
+       }
+
+       if (IMAP->num_msgs) {
+               IMAP->flags = malloc(IMAP->num_alloc * sizeof(long));
+               memset(IMAP->flags, 0, (IMAP->num_alloc * sizeof(long)) );
+       }
+
+       imap_set_seen_flags(0);
+}
+
+
+/*
+ * Re-scan the selected room (folder) and see if it's been changed at all
+ */
+void imap_rescan_msgids(void)
+{
+
+       int original_num_msgs = 0;
+       long original_highest = 0L;
+       int i, j, jstart;
+       int message_still_exists;
+       struct cdbdata *cdbfr;
+       long *msglist = NULL;
+       int num_msgs = 0;
+       int num_recent = 0;
+
+       if (IMAP->selected == 0) {
+               lprintf(CTDL_ERR,
+                       "imap_load_msgids() can't run; no room selected\n");
+               return;
+       }
+
+       /*
+        * Check to see if the room's contents have changed.
+        * If not, we can avoid this rescan.
+        */
+       getroom(&CC->room, CC->room.QRname);
+       if (IMAP->last_mtime == CC->room.QRmtime) {     /* No changes! */
+               return;
+       }
+
+       /* Load the *current* message list from disk, so we can compare it
+        * to what we have in memory.
+        */
+       cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
+       if (cdbfr != NULL) {
+               msglist = malloc(cdbfr->len);
+               if (msglist == NULL) {
+                       lprintf(CTDL_CRIT, "malloc() failed\n");
+                       abort();
+               }
+               memcpy(msglist, cdbfr->ptr, (size_t)cdbfr->len);
+               num_msgs = cdbfr->len / sizeof(long);
+               cdb_free(cdbfr);
+       } else {
+               num_msgs = 0;
+       }
+
+       /*
+        * Check to see if any of the messages we know about have been expunged
+        */
+       if (IMAP->num_msgs > 0) {
+               jstart = 0;
+               for (i = 0; i < IMAP->num_msgs; ++i) {
+
+                       message_still_exists = 0;
+                       if (num_msgs > 0) {
+                               for (j = jstart; j < num_msgs; ++j) {
+                                       if (msglist[j] == IMAP->msgids[i]) {
+                                               message_still_exists = 1;
+                                               jstart = j;
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if (message_still_exists == 0) {
+                               cprintf("* %d EXPUNGE\r\n", i + 1);
+
+                               /* Here's some nice stupid nonsense.  When a
+                                * message is expunged, we have to slide all
+                                * the existing messages up in the message
+                                * array.
+                                */
+                               --IMAP->num_msgs;
+                               memcpy(&IMAP->msgids[i],
+                                      &IMAP->msgids[i + 1],
+                                      (sizeof(long) *
+                                       (IMAP->num_msgs - i)));
+                               memcpy(&IMAP->flags[i],
+                                      &IMAP->flags[i + 1],
+                                      (sizeof(long) *
+                                       (IMAP->num_msgs - i)));
+
+                               --i;
+                       }
+
+               }
+       }
+
+       /*
+        * Remember how many messages were here before we re-scanned.
+        */
+       original_num_msgs = IMAP->num_msgs;
+       if (IMAP->num_msgs > 0) {
+               original_highest = IMAP->msgids[IMAP->num_msgs - 1];
+       } else {
+               original_highest = 0L;
+       }
+
+       /*
+        * Now peruse the room for *new* messages only.
+        */
+       if (num_msgs > 0) {
+               for (j = 0; j < num_msgs; ++j) {
+                       if (msglist[j] > original_highest) {
+                               imap_add_single_msgid(msglist[j], NULL);
+                       }
+               }
+       }
+       imap_set_seen_flags(original_num_msgs);
+
+       /*
+        * If new messages have arrived, tell the client about them.
+        */
+       if (IMAP->num_msgs > original_num_msgs) {
+
+               for (j = 0; j < num_msgs; ++j) {
+                       if (IMAP->flags[j] & IMAP_RECENT) {
+                               ++num_recent;
+                       }
+               }
+
+               cprintf("* %d EXISTS\r\n", IMAP->num_msgs);
+               cprintf("* %d RECENT\r\n", num_recent);
+       }
+
+       if (num_msgs != 0) {
+               free(msglist);
+       }
+       IMAP->last_mtime = CC->room.QRmtime;
+}
+
+
+
+
+
+
+
+/*
+ * This cleanup function blows away the temporary memory and files used by
+ * the IMAP server.
+ */
+void imap_cleanup_function(void)
+{
+
+       /* Don't do this stuff if this is not a IMAP session! */
+       if (CC->h_command_function != imap_command_loop)
+               return;
+
+       /* If there is a mailbox selected, auto-expunge it. */
+       if (IMAP->selected) {
+               imap_do_expunge();
+       }
+
+       lprintf(CTDL_DEBUG, "Performing IMAP cleanup hook\n");
+       imap_free_msgids();
+       imap_free_transmitted_message();
+
+       if (IMAP->cached_rfc822_data != NULL) {
+               free(IMAP->cached_rfc822_data);
+               IMAP->cached_rfc822_data = NULL;
+               IMAP->cached_rfc822_msgnum = (-1);
+               IMAP->cached_rfc822_withbody = 0;
+       }
+
+       if (IMAP->cached_body != NULL) {
+               free(IMAP->cached_body);
+               IMAP->cached_body = NULL;
+               IMAP->cached_body_len = 0;
+               IMAP->cached_bodymsgnum = (-1);
+       }
+
+       free(IMAP);
+       lprintf(CTDL_DEBUG, "Finished IMAP cleanup hook\n");
+}
+
+
+/*
+ * Does the actual work of the CAPABILITY command (because we need to
+ * output this stuff in other places as well)
+ */
+void imap_output_capability_string(void) {
+       cprintf("CAPABILITY IMAP4REV1 NAMESPACE ID ACL AUTH=PLAIN AUTH=LOGIN");
+
+#ifdef HAVE_OPENSSL
+       if (!CC->redirect_ssl) cprintf(" STARTTLS");
+#endif
+
+       /* We are building a partial implementation of METADATA for the sole purpose
+        * of interoperating with the ical/vcard version of the Bynari Insight Connector.
+        * If you were expecting something else, comment out one or both of these
+        * extension advertisements.
+        */
+       cprintf(" METADATA");
+       /* cprintf(" LIST-EXTENDED"); */
+}
+
+/*
+ * implements the CAPABILITY command
+ */
+void imap_capability(int num_parms, char *parms[])
+{
+       cprintf("* ");
+       imap_output_capability_string();
+       cprintf("\r\n");
+       cprintf("%s OK CAPABILITY completed\r\n", parms[0]);
+}
+
+
+
+/*
+ * Implements the ID command (specified by RFC2971)
+ *
+ * We ignore the client-supplied information, and output a NIL response.
+ * Although this is technically a valid implementation of the extension, it
+ * is quite useless.  It exists only so that we may see which clients are
+ * making use of this extension.
+ * 
+ */
+void imap_id(int num_parms, char *parms[])
+{
+       cprintf("* ID NIL\r\n");
+       cprintf("%s OK ID completed\r\n", parms[0]);
+}
+
+
+
+/*
+ * Here's where our IMAP session begins its happy day.
+ */
+void imap_greeting(void)
+{
+
+       strcpy(CC->cs_clientname, "IMAP session");
+       IMAP = malloc(sizeof (struct citimap));
+       memset(IMAP, 0, sizeof(struct citimap));
+       IMAP->authstate = imap_as_normal;
+       IMAP->cached_rfc822_data = NULL;
+       IMAP->cached_rfc822_msgnum = (-1);
+       IMAP->cached_rfc822_withbody = 0;
+
+       cprintf("* OK [");
+       imap_output_capability_string();
+       cprintf("] %s IMAP4rev1 %s ready\r\n", config.c_fqdn, CITADEL);
+}
+
+/*
+ * IMAPS is just like IMAP, except it goes crypto right away.
+ */
+void imaps_greeting(void) {
+       CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
+       imap_greeting();
+}
+
+
+/*
+ * implements the LOGIN command (ordinary username/password login)
+ */
+void imap_login(int num_parms, char *parms[])
+{
+       if (num_parms != 4) {
+               cprintf("%s BAD incorrect number of parameters\r\n", parms[0]);
+               return;
+       }
+
+       if (CtdlLoginExistingUser(NULL, parms[2]) == login_ok) {
+               if (CtdlTryPassword(parms[3]) == pass_ok) {
+                       cprintf("%s OK [", parms[0]);
+                       imap_output_capability_string();
+                       cprintf("] Hello, %s\r\n", CC->user.fullname);
+                       return;
+               }
+       }
+
+       cprintf("%s BAD Login incorrect\r\n", parms[0]);
+}
+
+
+/*
+ * Implements the AUTHENTICATE command
+ */
+void imap_authenticate(int num_parms, char *parms[])
+{
+       char buf[SIZ];
+
+       if (num_parms != 3) {
+               cprintf("%s BAD incorrect number of parameters\r\n",
+                       parms[0]);
+               return;
+       }
+
+       if (CC->logged_in) {
+               cprintf("%s BAD Already logged in.\r\n", parms[0]);
+               return;
+       }
+
+       if (!strcasecmp(parms[2], "LOGIN")) {
+               CtdlEncodeBase64(buf, "Username:", 9);
+               cprintf("+ %s\r\n", buf);
+               IMAP->authstate = imap_as_expecting_username;
+               strcpy(IMAP->authseq, parms[0]);
+               return;
+       }
+
+       if (!strcasecmp(parms[2], "PLAIN")) {
+               // CtdlEncodeBase64(buf, "Username:", 9);
+               // cprintf("+ %s\r\n", buf);
+               cprintf("+ \r\n");
+               IMAP->authstate = imap_as_expecting_plainauth;
+               strcpy(IMAP->authseq, parms[0]);
+               return;
+       }
+
+       else {
+               cprintf("%s NO AUTHENTICATE %s failed\r\n",
+                       parms[0], parms[1]);
+       }
+}
+
+void imap_auth_plain(char *cmd)
+{
+       char decoded_authstring[1024];
+       char ident[256];
+       char user[256];
+       char pass[256];
+       int result;
+
+       CtdlDecodeBase64(decoded_authstring, cmd, strlen(cmd));
+       safestrncpy(ident, decoded_authstring, sizeof ident);
+       safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
+       safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
+
+       IMAP->authstate = imap_as_normal;
+
+       if (strlen(ident) > 0) {
+               result = CtdlLoginExistingUser(user, ident);
+       }
+       else {
+               result = CtdlLoginExistingUser(NULL, user);
+       }
+
+       if (result == login_ok) {
+               if (CtdlTryPassword(pass) == pass_ok) {
+                       cprintf("%s OK authentication succeeded\r\n", IMAP->authseq);
+                       return;
+               }
+       }
+       cprintf("%s NO authentication failed\r\n", IMAP->authseq);
+}
+
+void imap_auth_login_user(char *cmd)
+{
+       char buf[SIZ];
+
+       CtdlDecodeBase64(buf, cmd, SIZ);
+       CtdlLoginExistingUser(NULL, buf);
+       CtdlEncodeBase64(buf, "Password:", 9);
+       cprintf("+ %s\r\n", buf);
+       IMAP->authstate = imap_as_expecting_password;
+       return;
+}
+
+void imap_auth_login_pass(char *cmd)
+{
+       char buf[SIZ];
+
+       CtdlDecodeBase64(buf, cmd, SIZ);
+       if (CtdlTryPassword(buf) == pass_ok) {
+               cprintf("%s OK authentication succeeded\r\n", IMAP->authseq);
+       } else {
+               cprintf("%s NO authentication failed\r\n", IMAP->authseq);
+       }
+       IMAP->authstate = imap_as_normal;
+       return;
+}
+
+
+/*
+ * implements the STARTTLS command (Citadel API version)
+ */
+void imap_starttls(int num_parms, char *parms[])
+{
+       char ok_response[SIZ];
+       char nosup_response[SIZ];
+       char error_response[SIZ];
+
+       sprintf(ok_response,
+               "%s OK begin TLS negotiation now\r\n",
+               parms[0]);
+       sprintf(nosup_response,
+               "%s NO TLS not supported here\r\n",
+               parms[0]);
+       sprintf(error_response,
+               "%s BAD Internal error\r\n",
+               parms[0]);
+       CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
+}
+
+
+/*
+ * implements the SELECT command
+ */
+void imap_select(int num_parms, char *parms[])
+{
+       char towhere[SIZ];
+       char augmented_roomname[ROOMNAMELEN];
+       int c = 0;
+       int ok = 0;
+       int ra = 0;
+       struct ctdlroom QRscratch;
+       int msgs, new;
+       int floornum;
+       int roomflags;
+       int i;
+
+       /* Convert the supplied folder name to a roomname */
+       i = imap_roomname(towhere, sizeof towhere, parms[2]);
+       if (i < 0) {
+               cprintf("%s NO Invalid mailbox name.\r\n", parms[0]);
+               IMAP->selected = 0;
+               return;
+       }
+       floornum = (i & 0x00ff);
+       roomflags = (i & 0xff00);
+
+       /* First try a regular match */
+       c = getroom(&QRscratch, towhere);
+
+       /* Then try a mailbox name match */
+       if (c != 0) {
+               MailboxName(augmented_roomname, sizeof augmented_roomname,
+                           &CC->user, towhere);
+               c = getroom(&QRscratch, augmented_roomname);
+               if (c == 0)
+                       strcpy(towhere, augmented_roomname);
+       }
+
+       /* If the room exists, check security/access */
+       if (c == 0) {
+               /* See if there is an existing user/room relationship */
+               CtdlRoomAccess(&QRscratch, &CC->user, &ra, NULL);
+
+               /* normal clients have to pass through security */
+               if (ra & UA_KNOWN) {
+                       ok = 1;
+               }
+       }
+
+       /* Fail here if no such room */
+       if (!ok) {
+               cprintf("%s NO ... no such room, or access denied\r\n",
+                       parms[0]);
+               return;
+       }
+
+       /* If we already had some other folder selected, auto-expunge it */
+       imap_do_expunge();
+
+       /*
+        * usergoto() formally takes us to the desired room, happily returning
+        * the number of messages and number of new messages.
+        */
+       memcpy(&CC->room, &QRscratch, sizeof(struct ctdlroom));
+       usergoto(NULL, 0, 0, &msgs, &new);
+       IMAP->selected = 1;
+
+       if (!strcasecmp(parms[1], "EXAMINE")) {
+               IMAP->readonly = 1;
+       } else {
+               IMAP->readonly = 0;
+       }
+
+       imap_load_msgids();
+       IMAP->last_mtime = CC->room.QRmtime;
+
+       cprintf("* %d EXISTS\r\n", msgs);
+       cprintf("* %d RECENT\r\n", new);
+
+       cprintf("* OK [UIDVALIDITY 1] UID validity status\r\n");
+       cprintf("* OK [UIDNEXT %ld] Predicted next UID\r\n", CitControl.MMhighest + 1);
+
+       /* Note that \Deleted is a valid flag, but not a permanent flag,
+        * because we don't maintain its state across sessions.  Citadel
+        * automatically expunges mailboxes when they are de-selected.
+        */
+       cprintf("* FLAGS (\\Deleted \\Seen \\Answered)\r\n");
+       cprintf("* OK [PERMANENTFLAGS (\\Deleted \\Seen \\Answered)] "
+               "permanent flags\r\n");
+
+       cprintf("%s OK [%s] %s completed\r\n",
+               parms[0],
+               (IMAP->readonly ? "READ-ONLY" : "READ-WRITE"), parms[1]);
+}
+
+
+
+/*
+ * Does the real work for expunge.
+ */
+int imap_do_expunge(void)
+{
+       int i;
+       int num_expunged = 0;
+       long *delmsgs = NULL;
+       int num_delmsgs = 0;
+
+       lprintf(CTDL_DEBUG, "imap_do_expunge() called\n");
+       if (IMAP->selected == 0) {
+               return (0);
+       }
+
+       if (IMAP->num_msgs > 0) {
+               delmsgs = malloc(IMAP->num_msgs * sizeof(long));
+               for (i = 0; i < IMAP->num_msgs; ++i) {
+                       if (IMAP->flags[i] & IMAP_DELETED) {
+                               delmsgs[num_delmsgs++] = IMAP->msgids[i];
+                       }
+               }
+               if (num_delmsgs > 0) {
+                       CtdlDeleteMessages(CC->room.QRname, delmsgs, num_delmsgs, "");
+               }
+               num_expunged += num_delmsgs;
+               free(delmsgs);
+       }
+
+       if (num_expunged > 0) {
+               imap_rescan_msgids();
+       }
+
+       lprintf(CTDL_DEBUG, "Expunged %d messages from <%s>\n",
+               num_expunged, CC->room.QRname);
+       return (num_expunged);
+}
+
+
+/*
+ * implements the EXPUNGE command syntax
+ */
+void imap_expunge(int num_parms, char *parms[])
+{
+       int num_expunged = 0;
+
+       num_expunged = imap_do_expunge();
+       cprintf("%s OK expunged %d messages.\r\n", parms[0], num_expunged);
+}
+
+
+/*
+ * implements the CLOSE command
+ */
+void imap_close(int num_parms, char *parms[])
+{
+
+       /* Yes, we always expunge on close. */
+       if (IMAP->selected) {
+               imap_do_expunge();
+       }
+
+       IMAP->selected = 0;
+       IMAP->readonly = 0;
+       imap_free_msgids();
+       cprintf("%s OK CLOSE completed\r\n", parms[0]);
+}
+
+
+/*
+ * Implements the NAMESPACE command.
+ */
+void imap_namespace(int num_parms, char *parms[])
+{
+       int i;
+       struct floor *fl;
+       int floors = 0;
+       char buf[SIZ];
+
+       cprintf("* NAMESPACE ");
+
+       /* All personal folders are subordinate to INBOX. */
+       cprintf("((\"INBOX/\" \"/\")) ");
+
+       /* Other users' folders ... coming soon! FIXME */
+       cprintf("NIL ");
+
+       /* Show all floors as shared namespaces.  Neato! */
+       cprintf("(");
+       for (i = 0; i < MAXFLOORS; ++i) {
+               fl = cgetfloor(i);
+               if (fl->f_flags & F_INUSE) {
+                       if (floors > 0) cprintf(" ");
+                       cprintf("(");
+                       sprintf(buf, "%s/", fl->f_name);
+                       imap_strout(buf);
+                       cprintf(" \"/\")");
+                       ++floors;
+               }
+       }
+       cprintf(")");
+
+       /* Wind it up with a newline and a completion message. */
+       cprintf("\r\n");
+       cprintf("%s OK NAMESPACE completed\r\n", parms[0]);
+}
+
+
+
+/*
+ * Implements the CREATE command
+ *
+ */
+void imap_create(int num_parms, char *parms[])
+{
+       int ret;
+       char roomname[ROOMNAMELEN];
+       int floornum;
+       int flags;
+       int newroomtype = 0;
+       int newroomview = 0;
+
+       if (strchr(parms[2], '\\') != NULL) {
+               cprintf("%s NO Invalid character in folder name\r\n",
+                       parms[0]);
+               lprintf(CTDL_DEBUG, "invalid character in folder name\n");
+               return;
+       }
+
+       ret = imap_roomname(roomname, sizeof roomname, parms[2]);
+       if (ret < 0) {
+               cprintf("%s NO Invalid mailbox name or location\r\n",
+                       parms[0]);
+               lprintf(CTDL_DEBUG, "invalid mailbox name or location\n");
+               return;
+       }
+       floornum = (ret & 0x00ff);      /* lower 8 bits = floor number */
+       flags = (ret & 0xff00); /* upper 8 bits = flags        */
+
+       if (flags & IR_MAILBOX) {
+               if (strncasecmp(parms[2], "INBOX/", 6)) {
+                       cprintf("%s NO Personal folders must be created under INBOX\r\n", parms[0]);
+                       lprintf(CTDL_DEBUG, "not subordinate to inbox\n");
+                       return;
+               }
+       }
+
+       if (flags & IR_MAILBOX) {
+               newroomtype = 4;                /* private mailbox */
+               newroomview = VIEW_MAILBOX;
+       } else {
+               newroomtype = 0;                /* public folder */
+               newroomview = VIEW_BBS;
+       }
+
+       lprintf(CTDL_INFO, "Create new room <%s> on floor <%d> with type <%d>\n",
+               roomname, floornum, newroomtype);
+
+       ret = create_room(roomname, newroomtype, "", floornum, 1, 0, newroomview);
+       if (ret == 0) {
+               /*** DO NOT CHANGE THIS ERROR MESSAGE IN ANY WAY!  BYNARI CONNECTOR DEPENDS ON IT! ***/
+               cprintf("%s NO Mailbox already exists, or create failed\r\n", parms[0]);
+       } else {
+               cprintf("%s OK CREATE completed\r\n", parms[0]);
+       }
+       lprintf(CTDL_DEBUG, "imap_create() completed\n");
+}
+
+
+/*
+ * Locate a room by its IMAP folder name, and check access to it.
+ * If zapped_ok is nonzero, we can also look for the room in the zapped list.
+ */
+int imap_grabroom(char *returned_roomname, char *foldername, int zapped_ok)
+{
+       int ret;
+       char augmented_roomname[ROOMNAMELEN];
+       char roomname[ROOMNAMELEN];
+       int c;
+       struct ctdlroom QRscratch;
+       int ra;
+       int ok = 0;
+
+       ret = imap_roomname(roomname, sizeof roomname, foldername);
+       if (ret < 0) {
+               return (1);
+       }
+
+       /* First try a regular match */
+       c = getroom(&QRscratch, roomname);
+
+       /* Then try a mailbox name match */
+       if (c != 0) {
+               MailboxName(augmented_roomname, sizeof augmented_roomname,
+                           &CC->user, roomname);
+               c = getroom(&QRscratch, augmented_roomname);
+               if (c == 0)
+                       strcpy(roomname, augmented_roomname);
+       }
+
+       /* If the room exists, check security/access */
+       if (c == 0) {
+               /* See if there is an existing user/room relationship */
+               CtdlRoomAccess(&QRscratch, &CC->user, &ra, NULL);
+
+               /* normal clients have to pass through security */
+               if (ra & UA_KNOWN) {
+                       ok = 1;
+               }
+               if ((zapped_ok) && (ra & UA_ZAPPED)) {
+                       ok = 1;
+               }
+       }
+
+       /* Fail here if no such room */
+       if (!ok) {
+               strcpy(returned_roomname, "");
+               return (2);
+       } else {
+               strcpy(returned_roomname, QRscratch.QRname);
+               return (0);
+       }
+}
+
+
+/*
+ * Implements the STATUS command (sort of)
+ *
+ */
+void imap_status(int num_parms, char *parms[])
+{
+       int ret;
+       char roomname[ROOMNAMELEN];
+       char buf[SIZ];
+       char savedroom[ROOMNAMELEN];
+       int msgs, new;
+
+       ret = imap_grabroom(roomname, parms[2], 0);
+       if (ret != 0) {
+               cprintf
+                   ("%s NO Invalid mailbox name or location, or access denied\r\n",
+                    parms[0]);
+               return;
+       }
+
+       /*
+        * usergoto() formally takes us to the desired room, happily returning
+        * the number of messages and number of new messages.  (If another
+        * folder is selected, save its name so we can return there!!!!!)
+        */
+       if (IMAP->selected) {
+               strcpy(savedroom, CC->room.QRname);
+       }
+       usergoto(roomname, 0, 0, &msgs, &new);
+
+       /*
+        * Tell the client what it wants to know.  In fact, tell it *more* than
+        * it wants to know.  We happily IGnore the supplied status data item
+        * names and simply spew all possible data items.  It's far easier to
+        * code and probably saves us some processing time too.
+        */
+       imap_mailboxname(buf, sizeof buf, &CC->room);
+       cprintf("* STATUS ");
+       imap_strout(buf);
+       cprintf(" (MESSAGES %d ", msgs);
+       cprintf("RECENT %d ", new);     /* Initially, new==recent */
+       cprintf("UIDNEXT %ld ", CitControl.MMhighest + 1);
+       cprintf("UNSEEN %d)\r\n", new);
+
+       /*
+        * If another folder is selected, go back to that room so we can resume
+        * our happy day without violent explosions.
+        */
+       if (IMAP->selected) {
+               usergoto(savedroom, 0, 0, &msgs, &new);
+       }
+
+       /*
+        * Oooh, look, we're done!
+        */
+       cprintf("%s OK STATUS completed\r\n", parms[0]);
+}
+
+
+
+/*
+ * Implements the SUBSCRIBE command
+ *
+ */
+void imap_subscribe(int num_parms, char *parms[])
+{
+       int ret;
+       char roomname[ROOMNAMELEN];
+       char savedroom[ROOMNAMELEN];
+       int msgs, new;
+
+       ret = imap_grabroom(roomname, parms[2], 1);
+       if (ret != 0) {
+               cprintf(
+                       "%s NO Error %d: invalid mailbox name or location, or access denied\r\n",
+                       parms[0],
+                       ret
+               );
+               return;
+       }
+
+       /*
+        * usergoto() formally takes us to the desired room, which has the side
+        * effect of marking the room as not-zapped ... exactly the effect
+        * we're looking for.
+        */
+       if (IMAP->selected) {
+               strcpy(savedroom, CC->room.QRname);
+       }
+       usergoto(roomname, 0, 0, &msgs, &new);
+
+       /*
+        * If another folder is selected, go back to that room so we can resume
+        * our happy day without violent explosions.
+        */
+       if (IMAP->selected) {
+               usergoto(savedroom, 0, 0, &msgs, &new);
+       }
+
+       cprintf("%s OK SUBSCRIBE completed\r\n", parms[0]);
+}
+
+
+/*
+ * Implements the UNSUBSCRIBE command
+ *
+ */
+void imap_unsubscribe(int num_parms, char *parms[])
+{
+       int ret;
+       char roomname[ROOMNAMELEN];
+       char savedroom[ROOMNAMELEN];
+       int msgs, new;
+
+       ret = imap_grabroom(roomname, parms[2], 0);
+       if (ret != 0) {
+               cprintf
+                   ("%s NO Invalid mailbox name or location, or access denied\r\n",
+                    parms[0]);
+               return;
+       }
+
+       /*
+        * usergoto() formally takes us to the desired room.
+        */
+       if (IMAP->selected) {
+               strcpy(savedroom, CC->room.QRname);
+       }
+       usergoto(roomname, 0, 0, &msgs, &new);
+
+       /* 
+        * Now make the API call to zap the room
+        */
+       if (CtdlForgetThisRoom() == 0) {
+               cprintf("%s OK UNSUBSCRIBE completed\r\n", parms[0]);
+       } else {
+               cprintf
+                   ("%s NO You may not unsubscribe from this folder.\r\n",
+                    parms[0]);
+       }
+
+       /*
+        * If another folder is selected, go back to that room so we can resume
+        * our happy day without violent explosions.
+        */
+       if (IMAP->selected) {
+               usergoto(savedroom, 0, 0, &msgs, &new);
+       }
+}
+
+
+
+/*
+ * Implements the DELETE command
+ *
+ */
+void imap_delete(int num_parms, char *parms[])
+{
+       int ret;
+       char roomname[ROOMNAMELEN];
+       char savedroom[ROOMNAMELEN];
+       int msgs, new;
+
+       ret = imap_grabroom(roomname, parms[2], 1);
+       if (ret != 0) {
+               cprintf("%s NO Invalid mailbox name, or access denied\r\n",
+                       parms[0]);
+               return;
+       }
+
+       /*
+        * usergoto() formally takes us to the desired room, happily returning
+        * the number of messages and number of new messages.  (If another
+        * folder is selected, save its name so we can return there!!!!!)
+        */
+       if (IMAP->selected) {
+               strcpy(savedroom, CC->room.QRname);
+       }
+       usergoto(roomname, 0, 0, &msgs, &new);
+
+       /*
+        * Now delete the room.
+        */
+       if (CtdlDoIHavePermissionToDeleteThisRoom(&CC->room)) {
+               schedule_room_for_deletion(&CC->room);
+               cprintf("%s OK DELETE completed\r\n", parms[0]);
+       } else {
+               cprintf("%s NO Can't delete this folder.\r\n", parms[0]);
+       }
+
+       /*
+        * If another folder is selected, go back to that room so we can resume
+        * our happy day without violent explosions.
+        */
+       if (IMAP->selected) {
+               usergoto(savedroom, 0, 0, &msgs, &new);
+       }
+}
+
+
+/*
+ * Back end function for imap_rename()
+ */
+void imap_rename_backend(struct ctdlroom *qrbuf, void *data)
+{
+       char foldername[SIZ];
+       char newfoldername[SIZ];
+       char newroomname[ROOMNAMELEN];
+       int newfloor = 0;
+       struct irl *irlp = NULL;        /* scratch pointer */
+       struct irlparms *irlparms;
+
+       irlparms = (struct irlparms *) data;
+       imap_mailboxname(foldername, sizeof foldername, qrbuf);
+
+       /* Rename subfolders */
+       if ((!strncasecmp(foldername, irlparms->oldname,
+                         strlen(irlparms->oldname))
+            && (foldername[strlen(irlparms->oldname)] == '/'))) {
+
+               sprintf(newfoldername, "%s/%s",
+                       irlparms->newname,
+                       &foldername[strlen(irlparms->oldname) + 1]
+                   );
+
+               newfloor = imap_roomname(newroomname,
+                                        sizeof newroomname,
+                                        newfoldername) & 0xFF;
+
+               irlp = (struct irl *) malloc(sizeof(struct irl));
+               strcpy(irlp->irl_newroom, newroomname);
+               strcpy(irlp->irl_oldroom, qrbuf->QRname);
+               irlp->irl_newfloor = newfloor;
+               irlp->next = *(irlparms->irl);
+               *(irlparms->irl) = irlp;
+       }
+}
+
+
+/*
+ * Implements the RENAME command
+ *
+ */
+void imap_rename(int num_parms, char *parms[])
+{
+       char old_room[ROOMNAMELEN];
+       char new_room[ROOMNAMELEN];
+       int oldr, newr;
+       int new_floor;
+       int r;
+       struct irl *irl = NULL; /* the list */
+       struct irl *irlp = NULL;        /* scratch pointer */
+       struct irlparms irlparms;
+
+       if (strchr(parms[3], '\\') != NULL) {
+               cprintf("%s NO Invalid character in folder name\r\n",
+                       parms[0]);
+               return;
+       }
+
+       oldr = imap_roomname(old_room, sizeof old_room, parms[2]);
+       newr = imap_roomname(new_room, sizeof new_room, parms[3]);
+       new_floor = (newr & 0xFF);
+
+       r = CtdlRenameRoom(old_room, new_room, new_floor);
+
+       if (r == crr_room_not_found) {
+               cprintf("%s NO Could not locate this folder\r\n",
+                       parms[0]);
+               return;
+       }
+       if (r == crr_already_exists) {
+               cprintf("%s '%s' already exists.\r\n", parms[0], parms[2]);
+               return;
+       }
+       if (r == crr_noneditable) {
+               cprintf("%s This folder is not editable.\r\n", parms[0]);
+               return;
+       }
+       if (r == crr_invalid_floor) {
+               cprintf("%s Folder root does not exist.\r\n", parms[0]);
+               return;
+       }
+       if (r == crr_access_denied) {
+               cprintf("%s You do not have permission to edit "
+                       "this folder.\r\n", parms[0]);
+               return;
+       }
+       if (r != crr_ok) {
+               cprintf("%s NO Rename failed - undefined error %d\r\n",
+                       parms[0], r);
+               return;
+       }
+
+
+       /* If this is the INBOX, then RFC2060 says we have to just move the
+        * contents.  In a Citadel environment it's easier to rename the room
+        * (already did that) and create a new inbox.
+        */
+       if (!strcasecmp(parms[2], "INBOX")) {
+               create_room(MAILROOM, 4, "", 0, 1, 0, VIEW_MAILBOX);
+       }
+
+       /* Otherwise, do the subfolders.  Build a list of rooms to rename... */
+       else {
+               irlparms.oldname = parms[2];
+               irlparms.newname = parms[3];
+               irlparms.irl = &irl;
+               ForEachRoom(imap_rename_backend, (void *) &irlparms);
+
+               /* ... and now rename them. */
+               while (irl != NULL) {
+                       r = CtdlRenameRoom(irl->irl_oldroom,
+                                          irl->irl_newroom,
+                                          irl->irl_newfloor);
+                       if (r != crr_ok) {
+                               /* FIXME handle error returns better */
+                               lprintf(CTDL_ERR, "CtdlRenameRoom() error %d\n", r);
+                       }
+                       irlp = irl;
+                       irl = irl->next;
+                       free(irlp);
+               }
+       }
+
+       cprintf("%s OK RENAME completed\r\n", parms[0]);
+}
+
+
+
+
+/* 
+ * Main command loop for IMAP sessions.
+ */
+void imap_command_loop(void)
+{
+       char cmdbuf[SIZ];
+       char *parms[SIZ];
+       int num_parms;
+       struct timeval tv1, tv2;
+       suseconds_t total_time = 0;
+
+       gettimeofday(&tv1, NULL);
+       CC->lastcmd = time(NULL);
+       memset(cmdbuf, 0, sizeof cmdbuf);       /* Clear it, just in case */
+       flush_output();
+       if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
+               lprintf(CTDL_ERR, "Client disconnected: ending session.\r\n");
+               CC->kill_me = 1;
+               return;
+       }
+
+       if (IMAP->authstate == imap_as_expecting_password) {
+               lprintf(CTDL_INFO, "IMAP: <password>\n");
+       }
+       else if (IMAP->authstate == imap_as_expecting_plainauth) {
+               lprintf(CTDL_INFO, "IMAP: <plain_auth>\n");
+       }
+       else if (bmstrcasestr(cmdbuf, " LOGIN ")) {
+               lprintf(CTDL_INFO, "IMAP: LOGIN...\n");
+       }
+       else {
+               lprintf(CTDL_INFO, "IMAP: %s\n", cmdbuf);
+       }
+
+       while (strlen(cmdbuf) < 5)
+               strcat(cmdbuf, " ");
+
+       /* strip off l/t whitespace and CRLF */
+       if (cmdbuf[strlen(cmdbuf) - 1] == '\n')
+               cmdbuf[strlen(cmdbuf) - 1] = 0;
+       if (cmdbuf[strlen(cmdbuf) - 1] == '\r')
+               cmdbuf[strlen(cmdbuf) - 1] = 0;
+       striplt(cmdbuf);
+
+       /* If we're in the middle of a multi-line command, handle that */
+       if (IMAP->authstate == imap_as_expecting_username) {
+               imap_auth_login_user(cmdbuf);
+               return;
+       }
+       if (IMAP->authstate == imap_as_expecting_plainauth) {
+               imap_auth_plain(cmdbuf);
+               return;
+       }
+       if (IMAP->authstate == imap_as_expecting_password) {
+               imap_auth_login_pass(cmdbuf);
+               return;
+       }
+
+       /* Ok, at this point we're in normal command mode.  The first thing
+        * we do is print any incoming pages (yeah! we really do!)
+        */
+       imap_print_instant_messages();
+
+       /*
+        * Before processing the command that was just entered... if we happen
+        * to have a folder selected, we'd like to rescan that folder for new
+        * messages, and for deletions/changes of existing messages.  This
+        * could probably be optimized somehow, but IMAP sucks...
+        */
+       if (IMAP->selected) {
+               imap_rescan_msgids();
+       }
+
+       /* Now for the command set. */
+
+       /* Grab the tag, command, and parameters.  Check syntax. */
+       num_parms = imap_parameterize(parms, cmdbuf);
+       if (num_parms < 2) {
+               cprintf("BAD syntax error\r\n");
+       }
+
+       /* The commands below may be executed in any state */
+
+       else if ((!strcasecmp(parms[1], "NOOP"))
+                || (!strcasecmp(parms[1], "CHECK"))) {
+               cprintf("%s OK No operation\r\n",
+                       parms[0]);
+       }
+
+       else if (!strcasecmp(parms[1], "ID")) {
+               imap_id(num_parms, parms);
+       }
+
+
+       else if (!strcasecmp(parms[1], "LOGOUT")) {
+               if (IMAP->selected) {
+                       imap_do_expunge();      /* yes, we auto-expunge */
+               }
+               cprintf("* BYE %s logging out\r\n", config.c_fqdn);
+               cprintf("%s OK Citadel IMAP session ended.\r\n",
+                       parms[0]);
+               CC->kill_me = 1;
+               return;
+       }
+
+       else if (!strcasecmp(parms[1], "LOGIN")) {
+               imap_login(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "AUTHENTICATE")) {
+               imap_authenticate(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "CAPABILITY")) {
+               imap_capability(num_parms, parms);
+       }
+#ifdef HAVE_OPENSSL
+       else if (!strcasecmp(parms[1], "STARTTLS")) {
+               imap_starttls(num_parms, parms);
+       }
+#endif
+       else if (!CC->logged_in) {
+               cprintf("%s BAD Not logged in.\r\n", parms[0]);
+       }
+
+       /* The commans below require a logged-in state */
+
+       else if (!strcasecmp(parms[1], "SELECT")) {
+               imap_select(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "EXAMINE")) {
+               imap_select(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "LSUB")) {
+               imap_list(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "LIST")) {
+               imap_list(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "CREATE")) {
+               imap_create(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "DELETE")) {
+               imap_delete(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "RENAME")) {
+               imap_rename(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "STATUS")) {
+               imap_status(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "SUBSCRIBE")) {
+               imap_subscribe(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "UNSUBSCRIBE")) {
+               imap_unsubscribe(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "APPEND")) {
+               imap_append(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "NAMESPACE")) {
+               imap_namespace(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "SETACL")) {
+               imap_setacl(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "DELETEACL")) {
+               imap_deleteacl(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "GETACL")) {
+               imap_getacl(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "LISTRIGHTS")) {
+               imap_listrights(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "MYRIGHTS")) {
+               imap_myrights(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "GETMETADATA")) {
+               imap_getmetadata(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "SETMETADATA")) {
+               imap_setmetadata(num_parms, parms);
+       }
+
+       else if (IMAP->selected == 0) {
+               cprintf("%s BAD no folder selected\r\n", parms[0]);
+       }
+
+       /* The commands below require the SELECT state on a mailbox */
+
+       else if (!strcasecmp(parms[1], "FETCH")) {
+               imap_fetch(num_parms, parms);
+       }
+
+       else if ((!strcasecmp(parms[1], "UID"))
+                && (!strcasecmp(parms[2], "FETCH"))) {
+               imap_uidfetch(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "SEARCH")) {
+               imap_search(num_parms, parms);
+       }
+
+       else if ((!strcasecmp(parms[1], "UID"))
+                && (!strcasecmp(parms[2], "SEARCH"))) {
+               imap_uidsearch(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "STORE")) {
+               imap_store(num_parms, parms);
+       }
+
+       else if ((!strcasecmp(parms[1], "UID"))
+                && (!strcasecmp(parms[2], "STORE"))) {
+               imap_uidstore(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "COPY")) {
+               imap_copy(num_parms, parms);
+       }
+
+       else if ((!strcasecmp(parms[1], "UID"))
+                && (!strcasecmp(parms[2], "COPY"))) {
+               imap_uidcopy(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "EXPUNGE")) {
+               imap_expunge(num_parms, parms);
+       }
+
+       else if (!strcasecmp(parms[1], "CLOSE")) {
+               imap_close(num_parms, parms);
+       }
+
+       /* End of commands.  If we get here, the command is either invalid
+        * or unimplemented.
+        */
+
+       else {
+               cprintf("%s BAD command unrecognized\r\n", parms[0]);
+       }
+
+       /* If the client transmitted a message we can free it now */
+       imap_free_transmitted_message();
+
+       gettimeofday(&tv2, NULL);
+       total_time = (tv2.tv_usec + (tv2.tv_sec * 1000000)) - (tv1.tv_usec + (tv1.tv_sec * 1000000));
+       lprintf(CTDL_DEBUG, "IMAP command completed in %ld.%ld seconds\n",
+               (total_time / 1000000),
+               (total_time % 1000000)
+       );
+}
+
+
+/*
+ * This function is called to register the IMAP extension with Citadel.
+ */
+CTDL_MODULE_INIT(imap)
+{
+       CtdlRegisterServiceHook(config.c_imap_port,
+                               NULL, imap_greeting, imap_command_loop, NULL);
+#ifdef HAVE_OPENSSL
+       CtdlRegisterServiceHook(config.c_imaps_port,
+                               NULL, imaps_greeting, imap_command_loop, NULL);
+#endif
+       CtdlRegisterSessionHook(imap_cleanup_function, EVT_STOP);
+
+       /* return our Subversion id for the Log */
+       return "$Id$";
+}
diff --git a/citadel/modules/imap/serv_imap.h b/citadel/modules/imap/serv_imap.h
new file mode 100644 (file)
index 0000000..c7d932a
--- /dev/null
@@ -0,0 +1,73 @@
+/* $Id$ 
+ */
+
+
+void imap_cleanup_function(void);
+void imap_greeting(void);
+void imap_command_loop(void);
+int imap_grabroom(char *returned_roomname, char *foldername, int zapped_ok);
+void imap_free_transmitted_message(void);
+int imap_do_expunge(void);
+void imap_rescan_msgids(void);
+
+
+struct citimap {
+       int authstate;
+       char authseq[SIZ];
+       int selected;                   /* set to 1 if in the SELECTED state */
+       int readonly;                   /* mailbox is open read only */
+       int num_msgs;                   /* Number of messages being mapped */
+       int num_alloc;                  /* Number of messages for which we've allocated space */
+       time_t last_mtime;              /* For checking whether the room was modified... */
+       long *msgids;
+       unsigned int *flags;
+       char *transmitted_message;      /* for APPEND command... */
+       size_t transmitted_length;
+
+       /* Cache most recent RFC822 FETCH because client might load in pieces */
+       char *cached_rfc822_data;
+       long cached_rfc822_msgnum;
+       size_t cached_rfc822_len;
+       char cached_rfc822_withbody;    /* 1 = body cached; 0 = only headers cached */
+
+       /* Cache most recent BODY FETCH because client might load in pieces */
+       char *cached_body;
+       size_t cached_body_len;
+       char cached_bodypart[SIZ];
+       long cached_bodymsgnum;
+       char cached_body_withbody;      /* 1 = body cached; 0 = only headers cached */
+};
+
+/*
+ * values of 'authstate'
+ */
+enum {
+       imap_as_normal,
+       imap_as_expecting_username,
+       imap_as_expecting_password,
+       imap_as_expecting_plainauth
+};
+
+/* Flags for the above struct.  Note that some of these are for internal use,
+ * and are not to be reported to IMAP clients.
+ */
+#define IMAP_ANSWERED          1       /* reportable and setable */
+#define IMAP_FLAGGED           2       /* reportable and setable */
+#define IMAP_DELETED           4       /* reportable and setable */
+#define IMAP_DRAFT             8       /* reportable and setable */
+#define IMAP_SEEN              16      /* reportable and setable */
+
+#define IMAP_MASK_SETABLE      0x1f
+#define IMAP_MASK_SYSTEM       0xe0
+
+#define IMAP_SELECTED          32      /* neither reportable nor setable */
+#define IMAP_RECENT            64      /* reportable but not setable */
+
+
+#define IMAP CC->IMAP
+
+/*
+ * When loading arrays of message ID's into memory, increase the buffer to
+ * hold this many additional messages instead of calling realloc() each time.
+ */
+#define REALLOC_INCREMENT 100
diff --git a/citadel/modules/ldap/serv_ldap.h b/citadel/modules/ldap/serv_ldap.h
new file mode 100644 (file)
index 0000000..f2595db
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifdef HAVE_LDAP
+
+void ctdl_vcard_to_ldap(struct CtdlMessage *msg, int op);
+
+enum {
+       V2L_WRITE,
+       V2L_DELETE
+};
+
+#endif /* HAVE_LDAP */
index 93e93553b5a54c92618cb64125e6596b93baeced..6a03745338bea066b721904cc8edaffac9550b23 100644 (file)
@@ -45,7 +45,6 @@
 #include "msgbase.h"
 #include "tools.h"
 #include "internet_addressing.h"
-#include "serv_network.h"
 #include "clientsocket.h"
 #include "file_ops.h"
 
index c43d8aa192ceac085610b4da2eba4f147f98ac22..56c28a327485cff7c0370a7b95b3e7953b49d56b 100644 (file)
 #include "msgbase.h"
 #include "tools.h"
 #include "internet_addressing.h"
-#include "imap_tools.h"
+#include "imap_tools.h"        /* Needed for imap_parameterize */
 #include "genstamp.h"
 #include "domain.h"
 #include "clientsocket.h"
 #include "locate_host.h"
 #include "citadel_dirs.h"
 
-#ifdef HAVE_OPENSSL
-#include "serv_crypto.h"
-#endif
-
 #ifndef HAVE_SNPRINTF
 #include "snprintf.h"
 #endif
@@ -246,17 +242,15 @@ void cmd_mgsve_auth(int num_parms, char **parms, struct sdm_userdata *u)
 }
 
 
-#ifdef HAVE_OPENSSL
 /**
  * STARTTLS command chapter 2.2 
  */
 void cmd_mgsve_starttls(void)
 { /** answer with OK, and fire off tls session. */
        cprintf("OK\r\n");
-       CtdlStartTLS(NULL, NULL, NULL);
+       CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
        cmd_mgsve_caps();
 }
-#endif
 
 
 
@@ -477,7 +471,6 @@ void mgsve_auth(char *argbuf) {
 /*
  * implements the STARTTLS command (Citadel API version)
  */
-#ifdef HAVE_OPENSSL
 void _mgsve_starttls(void)
 {
        char ok_response[SIZ];
@@ -490,9 +483,8 @@ void _mgsve_starttls(void)
                "554 5.7.3 TLS not supported here\r\n");
        sprintf(error_response,
                "554 5.7.3 Internal error\r\n");
-       CtdlStartTLS(ok_response, nosup_response, error_response);
+       CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
 }
-#endif
 
 
 /* 
index 31fced57ba0b4a543b82990ead58068a291e540d..edaf01d4f86a2b2c5091bca452167f4b14403be4 100644 (file)
@@ -41,7 +41,7 @@
 #include "policy.h"
 #include "database.h"
 #include "msgbase.h"
-#include "serv_network.h"
+#include "serv_network.h"      /* Needed for defenition of FilterList */
 #include "tools.h"
 
 
index c49c5cad98da96edb683b99bcc1135a0697fc2f9..ba2faa1e1009ba4ac6190e221d094303818e9bb7 100644 (file)
@@ -56,9 +56,6 @@
 #include "serv_pop3.h"
 #include "md5.h"
 
-#ifdef HAVE_OPENSSL
-#include "serv_crypto.h"
-#endif
 
 
 #include "ctdl_module.h"
@@ -99,12 +96,10 @@ void pop3_greeting(void) {
 /*
  * POP3S is just like POP3, except it goes crypto right away.
  */
-#ifdef HAVE_OPENSSL
 void pop3s_greeting(void) {
-       CtdlStartTLS(NULL, NULL, NULL);
+       CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
        pop3_greeting();
 }
-#endif
 
 
 
@@ -589,7 +584,6 @@ void pop3_uidl(char *argbuf) {
 /*
  * implements the STLS command (Citadel API version)
  */
-#ifdef HAVE_OPENSSL
 void pop3_stls(void)
 {
        char ok_response[SIZ];
@@ -602,9 +596,8 @@ void pop3_stls(void)
                "-ERR TLS not supported here\r\n");
        sprintf(error_response,
                "-ERR Internal error\r\n");
-       CtdlStartTLS(ok_response, nosup_response, error_response);
+       CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
 }
-#endif
 
 
 
index c2329ba9168cf958524ed78e8a568efdd842bc16..f24701ac08ace625f62273c6e92823489409cff7 100644 (file)
 #include "locate_host.h"
 #include "citadel_dirs.h"
 
-#ifdef HAVE_OPENSSL
-#include "serv_crypto.h"
-#endif
-
 
 
 #ifndef HAVE_SNPRINTF
@@ -168,12 +164,10 @@ void smtp_greeting(int is_msa)
 /*
  * SMTPS is just like SMTP, except it goes crypto right away.
  */
-#ifdef HAVE_OPENSSL
 void smtps_greeting(void) {
-       CtdlStartTLS(NULL, NULL, NULL);
+       CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
        smtp_greeting(0);
 }
-#endif
 
 
 /*
@@ -797,7 +791,6 @@ void smtp_data(void) {
 /*
  * implements the STARTTLS command (Citadel API version)
  */
-#ifdef HAVE_OPENSSL
 void smtp_starttls(void)
 {
        char ok_response[SIZ];
@@ -810,10 +803,9 @@ void smtp_starttls(void)
                "554 5.7.3 TLS not supported here\r\n");
        sprintf(error_response,
                "554 5.7.3 Internal error\r\n");
-       CtdlStartTLS(ok_response, nosup_response, error_response);
+       CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
        smtp_rset(0);
 }
-#endif
 
 
 
index ba292dfebbfff248efae9d45c2ee31f8767401b9..dbedf1fd8d52892612c22b559266c58288efc0e9 100644 (file)
@@ -62,7 +62,6 @@
 #include "tools.h"
 #include "mime_parser.h"
 #include "vcard.h"
-#include "serv_ldap.h"
 #include "serv_vcard.h"
 
 
index e110a11e90b9a21319f0024493cfa07998caadd0..51bf426f110b89a4c22bc57ebe3831317c2cdf8e 100644 (file)
 #include "html.h"
 #include "genstamp.h"
 #include "internet_addressing.h"
-#include "serv_fulltext.h"
+#include "serv_fulltext.h"     /* Needed for ft_search and ft_index_message */
 #include "vcard.h"
 #include "euidindex.h"
 #include "journaling.h"
 #include "citadel_dirs.h"
-#include "serv_network.h"
 
 
 long config_msgnum;
diff --git a/citadel/serv_crypto.h b/citadel/serv_crypto.h
deleted file mode 100644 (file)
index 5017482..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/* $Id$ */
-
-/*
- * Number of days for which self-signed certs are valid.
- */
-#define SIGN_DAYS      3650    /* Ten years */
-
-/* Shared Diffie-Hellman parameters */
-#define DH_P           "1A74527AEE4EE2568E85D4FB2E65E18C9394B9C80C42507D7A6A0DBE9A9A54B05A9A96800C34C7AA5297095B69C88901EEFD127F969DCA26A54C0E0B5C5473EBAEB00957D2633ECAE3835775425DE66C0DE6D024DBB17445E06E6B0C78415E589B8814F08531D02FD43778451E7685541079CFFB79EF0D26EFEEBBB69D1E80383"
-#define DH_G           "2"
-#define DH_L           1024
-#define CIT_CIPHERS    "ALL:RC4+RSA:+SSLv2:+TLSv1:!MD5:@STRENGTH"      /* see ciphers(1) */
-
-#ifdef HAVE_OPENSSL
-void destruct_ssl(void);
-void init_ssl(void);
-void client_write_ssl (char *buf, int nbytes);
-int client_read_ssl (char *buf, int bytes, int timeout);
-void cmd_stls(char *params);
-void cmd_gtls(char *params);
-void endtls(void);
-void ssl_lock(int mode, int n, const char *file, int line);
-void CtdlStartTLS(char *ok_response, char *nosup_response, char *error_response);
-extern SSL_CTX *ssl_ctx;  
-
-#endif
index 1acd40e099cbce6bc63c9785c5bebd6243858f40..78d721704eba60274550f2f73d60415db67ea2f8 100644 (file)
@@ -23,6 +23,8 @@
 #include "tools.h"
 #include "config.h"
 
+#include "modules/crypto/serv_crypto.h"        /* Needed until a universal crypto startup hook is implimented for CtdlStartTLS */
+
 #ifndef HAVE_SNPRINTF
 #include <stdarg.h>
 #include "snprintf.h"
@@ -38,7 +40,7 @@ struct DeleteFunctionHook *DeleteHookTable = NULL;
 struct ServiceFunctionHook *ServiceHookTable = NULL;
 struct FixedOutputHook *FixedOutputTable = NULL;
 struct RoomFunctionHook *RoomHookTable = NULL;
-
+struct MaintenanceThreadHook *MaintenanceThreadHookTable = NULL;
 
 struct ProtoFunctionHook {
        void (*handler) (char *cmdbuf);
@@ -1014,3 +1016,28 @@ int PerformXmsgHooks(char *sender, char *recp, char *msg)
        }
        return total_sent;
 }
+
+void CtdlRegisterMaintenanceThread(char *name, void *(*thread_proc)(void *arg))
+{
+       struct MaintenanceThreadHook *newfcn;
+
+       newfcn = (struct MaintenanceThreadHook *)
+           malloc(sizeof(struct MaintenanceThreadHook));
+       newfcn->name = name;
+       newfcn->next = MaintenanceThreadHookTable;
+       newfcn->fcn_ptr = thread_proc;
+       MaintenanceThreadHookTable = newfcn;
+
+       lprintf(CTDL_INFO, "Registered a new maintenance thread function\n");
+}
+
+
+/*
+ * Dirty hack until we impliment a hook mechanism for this
+ */
+void CtdlModuleStartCryptoMsgs(char *ok_response, char *nosup_response, char *error_response)
+{
+#ifdef HAVE_OPENSSL
+       CtdlStartTLS (ok_response, nosup_response, error_response);
+#endif
+}
diff --git a/citadel/serv_imap.c b/citadel/serv_imap.c
deleted file mode 100644 (file)
index be4132e..0000000
+++ /dev/null
@@ -1,1604 +0,0 @@
-/*
- * $Id$ 
- *
- * IMAP server for the Citadel system
- * Copyright (C) 2000-2007 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
- * is perfect.  Indeed, with so much gratuitous complexity, *all* IMAP
- * implementations have bugs.
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.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 <sys/wait.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-#include "citadel.h"
-#include "server.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "room_ops.h"
-#include "user_ops.h"
-#include "policy.h"
-#include "database.h"
-#include "msgbase.h"
-#include "tools.h"
-#include "internet_addressing.h"
-#include "serv_imap.h"
-#include "imap_tools.h"
-#include "imap_list.h"
-#include "imap_fetch.h"
-#include "imap_search.h"
-#include "imap_store.h"
-#include "imap_acl.h"
-#include "imap_metadata.h"
-#include "imap_misc.h"
-
-#ifdef HAVE_OPENSSL
-#include "serv_crypto.h"
-#endif
-
-
-#include "ctdl_module.h"
-
-
-/* imap_rename() uses this struct containing list of rooms to rename */
-struct irl {
-       struct irl *next;
-       char irl_oldroom[ROOMNAMELEN];
-       char irl_newroom[ROOMNAMELEN];
-       int irl_newfloor;
-};
-
-/* Data which is passed between imap_rename() and imap_rename_backend() */
-struct irlparms {
-       char *oldname;
-       char *newname;
-       struct irl **irl;
-};
-
-
-/*
- * If there is a message ID map in memory, free it
- */
-void imap_free_msgids(void)
-{
-       if (IMAP->msgids != NULL) {
-               free(IMAP->msgids);
-               IMAP->msgids = NULL;
-               IMAP->num_msgs = 0;
-               IMAP->num_alloc = 0;
-       }
-       if (IMAP->flags != NULL) {
-               free(IMAP->flags);
-               IMAP->flags = NULL;
-       }
-       IMAP->last_mtime = (-1);
-}
-
-
-/*
- * If there is a transmitted message in memory, free it
- */
-void imap_free_transmitted_message(void)
-{
-       if (IMAP->transmitted_message != NULL) {
-               free(IMAP->transmitted_message);
-               IMAP->transmitted_message = NULL;
-               IMAP->transmitted_length = 0;
-       }
-}
-
-
-/*
- * Set the \Seen, \Recent. and \Answered flags, based on the sequence
- * sets stored in the visit record for this user/room.  Note that we have
- * to parse each sequence set manually here, because calling the utility
- * function is_msg_in_sequence_set() over and over again is too expensive.
- *
- * first_msg should be set to 0 to rescan the flags for every message in the
- * room, or some other value if we're only interested in an incremental
- * update.
- */
-void imap_set_seen_flags(int first_msg)
-{
-       struct visit vbuf;
-       int i;
-       int num_sets;
-       int s;
-       char setstr[64], lostr[64], histr[64];
-       long lo, hi;
-
-       if (IMAP->num_msgs < 1) return;
-       CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
-
-       for (i = first_msg; i < IMAP->num_msgs; ++i) {
-               IMAP->flags[i] = IMAP->flags[i] & ~IMAP_SEEN;
-               IMAP->flags[i] |= IMAP_RECENT;
-               IMAP->flags[i] = IMAP->flags[i] & ~IMAP_ANSWERED;
-       }
-
-       /*
-        * Do the "\Seen" flag.
-        * (Any message not "\Seen" is considered "\Recent".)
-        */
-       num_sets = num_tokens(vbuf.v_seen, ',');
-       for (s=0; s<num_sets; ++s) {
-               extract_token(setstr, vbuf.v_seen, s, ',', sizeof setstr);
-
-               extract_token(lostr, setstr, 0, ':', sizeof lostr);
-               if (num_tokens(setstr, ':') >= 2) {
-                       extract_token(histr, setstr, 1, ':', sizeof histr);
-                       if (!strcmp(histr, "*")) {
-                               snprintf(histr, sizeof histr, "%ld", LONG_MAX);
-                       }
-               } 
-               else {
-                       strcpy(histr, lostr);
-               }
-               lo = atol(lostr);
-               hi = atol(histr);
-
-               for (i = first_msg; i < IMAP->num_msgs; ++i) {
-                       if ((IMAP->msgids[i] >= lo) && (IMAP->msgids[i] <= hi)){
-                               IMAP->flags[i] |= IMAP_SEEN;
-                               IMAP->flags[i] = IMAP->flags[i] & ~IMAP_RECENT;
-                       }
-               }
-       }
-
-       /* Do the ANSWERED flag */
-       num_sets = num_tokens(vbuf.v_answered, ',');
-       for (s=0; s<num_sets; ++s) {
-               extract_token(setstr, vbuf.v_answered, s, ',', sizeof setstr);
-
-               extract_token(lostr, setstr, 0, ':', sizeof lostr);
-               if (num_tokens(setstr, ':') >= 2) {
-                       extract_token(histr, setstr, 1, ':', sizeof histr);
-                       if (!strcmp(histr, "*")) {
-                               snprintf(histr, sizeof histr, "%ld", LONG_MAX);
-                       }
-               } 
-               else {
-                       strcpy(histr, lostr);
-               }
-               lo = atol(lostr);
-               hi = atol(histr);
-
-               for (i = first_msg; i < IMAP->num_msgs; ++i) {
-                       if ((IMAP->msgids[i] >= lo) && (IMAP->msgids[i] <= hi)){
-                               IMAP->flags[i] |= IMAP_ANSWERED;
-                       }
-               }
-       }
-
-}
-
-
-
-/*
- * Back end for imap_load_msgids()
- *
- * Optimization: instead of calling realloc() to add each message, we
- * allocate space in the list for REALLOC_INCREMENT messages at a time.  This
- * allows the mapping to proceed much faster.
- */
-void imap_add_single_msgid(long msgnum, void *userdata)
-{
-
-       ++IMAP->num_msgs;
-       if (IMAP->num_msgs > IMAP->num_alloc) {
-               IMAP->num_alloc += REALLOC_INCREMENT;
-               IMAP->msgids = realloc(IMAP->msgids,
-                                       (IMAP->num_alloc * sizeof(long)) );
-               IMAP->flags = realloc(IMAP->flags,
-                                       (IMAP->num_alloc * sizeof(long)) );
-       }
-       IMAP->msgids[IMAP->num_msgs - 1] = msgnum;
-       IMAP->flags[IMAP->num_msgs - 1] = 0;
-}
-
-
-
-/*
- * Set up a message ID map for the current room (folder)
- */
-void imap_load_msgids(void)
-{
-       struct cdbdata *cdbfr;
-
-       if (IMAP->selected == 0) {
-               lprintf(CTDL_ERR,
-                       "imap_load_msgids() can't run; no room selected\n");
-               return;
-       }
-
-       imap_free_msgids();     /* If there was already a map, free it */
-
-       /* Load the message list */
-       cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
-       if (cdbfr != NULL) {
-               IMAP->msgids = malloc(cdbfr->len);
-               memcpy(IMAP->msgids, cdbfr->ptr, cdbfr->len);
-               IMAP->num_msgs = cdbfr->len / sizeof(long);
-               IMAP->num_alloc = cdbfr->len / sizeof(long);
-               cdb_free(cdbfr);
-       }
-
-       if (IMAP->num_msgs) {
-               IMAP->flags = malloc(IMAP->num_alloc * sizeof(long));
-               memset(IMAP->flags, 0, (IMAP->num_alloc * sizeof(long)) );
-       }
-
-       imap_set_seen_flags(0);
-}
-
-
-/*
- * Re-scan the selected room (folder) and see if it's been changed at all
- */
-void imap_rescan_msgids(void)
-{
-
-       int original_num_msgs = 0;
-       long original_highest = 0L;
-       int i, j, jstart;
-       int message_still_exists;
-       struct cdbdata *cdbfr;
-       long *msglist = NULL;
-       int num_msgs = 0;
-       int num_recent = 0;
-
-       if (IMAP->selected == 0) {
-               lprintf(CTDL_ERR,
-                       "imap_load_msgids() can't run; no room selected\n");
-               return;
-       }
-
-       /*
-        * Check to see if the room's contents have changed.
-        * If not, we can avoid this rescan.
-        */
-       getroom(&CC->room, CC->room.QRname);
-       if (IMAP->last_mtime == CC->room.QRmtime) {     /* No changes! */
-               return;
-       }
-
-       /* Load the *current* message list from disk, so we can compare it
-        * to what we have in memory.
-        */
-       cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
-       if (cdbfr != NULL) {
-               msglist = malloc(cdbfr->len);
-               if (msglist == NULL) {
-                       lprintf(CTDL_CRIT, "malloc() failed\n");
-                       abort();
-               }
-               memcpy(msglist, cdbfr->ptr, (size_t)cdbfr->len);
-               num_msgs = cdbfr->len / sizeof(long);
-               cdb_free(cdbfr);
-       } else {
-               num_msgs = 0;
-       }
-
-       /*
-        * Check to see if any of the messages we know about have been expunged
-        */
-       if (IMAP->num_msgs > 0) {
-               jstart = 0;
-               for (i = 0; i < IMAP->num_msgs; ++i) {
-
-                       message_still_exists = 0;
-                       if (num_msgs > 0) {
-                               for (j = jstart; j < num_msgs; ++j) {
-                                       if (msglist[j] == IMAP->msgids[i]) {
-                                               message_still_exists = 1;
-                                               jstart = j;
-                                               break;
-                                       }
-                               }
-                       }
-
-                       if (message_still_exists == 0) {
-                               cprintf("* %d EXPUNGE\r\n", i + 1);
-
-                               /* Here's some nice stupid nonsense.  When a
-                                * message is expunged, we have to slide all
-                                * the existing messages up in the message
-                                * array.
-                                */
-                               --IMAP->num_msgs;
-                               memcpy(&IMAP->msgids[i],
-                                      &IMAP->msgids[i + 1],
-                                      (sizeof(long) *
-                                       (IMAP->num_msgs - i)));
-                               memcpy(&IMAP->flags[i],
-                                      &IMAP->flags[i + 1],
-                                      (sizeof(long) *
-                                       (IMAP->num_msgs - i)));
-
-                               --i;
-                       }
-
-               }
-       }
-
-       /*
-        * Remember how many messages were here before we re-scanned.
-        */
-       original_num_msgs = IMAP->num_msgs;
-       if (IMAP->num_msgs > 0) {
-               original_highest = IMAP->msgids[IMAP->num_msgs - 1];
-       } else {
-               original_highest = 0L;
-       }
-
-       /*
-        * Now peruse the room for *new* messages only.
-        */
-       if (num_msgs > 0) {
-               for (j = 0; j < num_msgs; ++j) {
-                       if (msglist[j] > original_highest) {
-                               imap_add_single_msgid(msglist[j], NULL);
-                       }
-               }
-       }
-       imap_set_seen_flags(original_num_msgs);
-
-       /*
-        * If new messages have arrived, tell the client about them.
-        */
-       if (IMAP->num_msgs > original_num_msgs) {
-
-               for (j = 0; j < num_msgs; ++j) {
-                       if (IMAP->flags[j] & IMAP_RECENT) {
-                               ++num_recent;
-                       }
-               }
-
-               cprintf("* %d EXISTS\r\n", IMAP->num_msgs);
-               cprintf("* %d RECENT\r\n", num_recent);
-       }
-
-       if (num_msgs != 0) {
-               free(msglist);
-       }
-       IMAP->last_mtime = CC->room.QRmtime;
-}
-
-
-
-
-
-
-
-/*
- * This cleanup function blows away the temporary memory and files used by
- * the IMAP server.
- */
-void imap_cleanup_function(void)
-{
-
-       /* Don't do this stuff if this is not a IMAP session! */
-       if (CC->h_command_function != imap_command_loop)
-               return;
-
-       /* If there is a mailbox selected, auto-expunge it. */
-       if (IMAP->selected) {
-               imap_do_expunge();
-       }
-
-       lprintf(CTDL_DEBUG, "Performing IMAP cleanup hook\n");
-       imap_free_msgids();
-       imap_free_transmitted_message();
-
-       if (IMAP->cached_rfc822_data != NULL) {
-               free(IMAP->cached_rfc822_data);
-               IMAP->cached_rfc822_data = NULL;
-               IMAP->cached_rfc822_msgnum = (-1);
-               IMAP->cached_rfc822_withbody = 0;
-       }
-
-       if (IMAP->cached_body != NULL) {
-               free(IMAP->cached_body);
-               IMAP->cached_body = NULL;
-               IMAP->cached_body_len = 0;
-               IMAP->cached_bodymsgnum = (-1);
-       }
-
-       free(IMAP);
-       lprintf(CTDL_DEBUG, "Finished IMAP cleanup hook\n");
-}
-
-
-/*
- * Does the actual work of the CAPABILITY command (because we need to
- * output this stuff in other places as well)
- */
-void imap_output_capability_string(void) {
-       cprintf("CAPABILITY IMAP4REV1 NAMESPACE ID ACL AUTH=PLAIN AUTH=LOGIN");
-
-#ifdef HAVE_OPENSSL
-       if (!CC->redirect_ssl) cprintf(" STARTTLS");
-#endif
-
-       /* We are building a partial implementation of METADATA for the sole purpose
-        * of interoperating with the ical/vcard version of the Bynari Insight Connector.
-        * If you were expecting something else, comment out one or both of these
-        * extension advertisements.
-        */
-       cprintf(" METADATA");
-       /* cprintf(" LIST-EXTENDED"); */
-}
-
-/*
- * implements the CAPABILITY command
- */
-void imap_capability(int num_parms, char *parms[])
-{
-       cprintf("* ");
-       imap_output_capability_string();
-       cprintf("\r\n");
-       cprintf("%s OK CAPABILITY completed\r\n", parms[0]);
-}
-
-
-
-/*
- * Implements the ID command (specified by RFC2971)
- *
- * We ignore the client-supplied information, and output a NIL response.
- * Although this is technically a valid implementation of the extension, it
- * is quite useless.  It exists only so that we may see which clients are
- * making use of this extension.
- * 
- */
-void imap_id(int num_parms, char *parms[])
-{
-       cprintf("* ID NIL\r\n");
-       cprintf("%s OK ID completed\r\n", parms[0]);
-}
-
-
-
-/*
- * Here's where our IMAP session begins its happy day.
- */
-void imap_greeting(void)
-{
-
-       strcpy(CC->cs_clientname, "IMAP session");
-       IMAP = malloc(sizeof (struct citimap));
-       memset(IMAP, 0, sizeof(struct citimap));
-       IMAP->authstate = imap_as_normal;
-       IMAP->cached_rfc822_data = NULL;
-       IMAP->cached_rfc822_msgnum = (-1);
-       IMAP->cached_rfc822_withbody = 0;
-
-       cprintf("* OK [");
-       imap_output_capability_string();
-       cprintf("] %s IMAP4rev1 %s ready\r\n", config.c_fqdn, CITADEL);
-}
-
-/*
- * IMAPS is just like IMAP, except it goes crypto right away.
- */
-#ifdef HAVE_OPENSSL
-void imaps_greeting(void) {
-       CtdlStartTLS(NULL, NULL, NULL);
-       imap_greeting();
-}
-#endif
-
-
-/*
- * implements the LOGIN command (ordinary username/password login)
- */
-void imap_login(int num_parms, char *parms[])
-{
-       if (num_parms != 4) {
-               cprintf("%s BAD incorrect number of parameters\r\n", parms[0]);
-               return;
-       }
-
-       if (CtdlLoginExistingUser(NULL, parms[2]) == login_ok) {
-               if (CtdlTryPassword(parms[3]) == pass_ok) {
-                       cprintf("%s OK [", parms[0]);
-                       imap_output_capability_string();
-                       cprintf("] Hello, %s\r\n", CC->user.fullname);
-                       return;
-               }
-       }
-
-       cprintf("%s BAD Login incorrect\r\n", parms[0]);
-}
-
-
-/*
- * Implements the AUTHENTICATE command
- */
-void imap_authenticate(int num_parms, char *parms[])
-{
-       char buf[SIZ];
-
-       if (num_parms != 3) {
-               cprintf("%s BAD incorrect number of parameters\r\n",
-                       parms[0]);
-               return;
-       }
-
-       if (CC->logged_in) {
-               cprintf("%s BAD Already logged in.\r\n", parms[0]);
-               return;
-       }
-
-       if (!strcasecmp(parms[2], "LOGIN")) {
-               CtdlEncodeBase64(buf, "Username:", 9);
-               cprintf("+ %s\r\n", buf);
-               IMAP->authstate = imap_as_expecting_username;
-               strcpy(IMAP->authseq, parms[0]);
-               return;
-       }
-
-       if (!strcasecmp(parms[2], "PLAIN")) {
-               // CtdlEncodeBase64(buf, "Username:", 9);
-               // cprintf("+ %s\r\n", buf);
-               cprintf("+ \r\n");
-               IMAP->authstate = imap_as_expecting_plainauth;
-               strcpy(IMAP->authseq, parms[0]);
-               return;
-       }
-
-       else {
-               cprintf("%s NO AUTHENTICATE %s failed\r\n",
-                       parms[0], parms[1]);
-       }
-}
-
-void imap_auth_plain(char *cmd)
-{
-       char decoded_authstring[1024];
-       char ident[256];
-       char user[256];
-       char pass[256];
-       int result;
-
-       CtdlDecodeBase64(decoded_authstring, cmd, strlen(cmd));
-       safestrncpy(ident, decoded_authstring, sizeof ident);
-       safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
-       safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
-
-       IMAP->authstate = imap_as_normal;
-
-       if (strlen(ident) > 0) {
-               result = CtdlLoginExistingUser(user, ident);
-       }
-       else {
-               result = CtdlLoginExistingUser(NULL, user);
-       }
-
-       if (result == login_ok) {
-               if (CtdlTryPassword(pass) == pass_ok) {
-                       cprintf("%s OK authentication succeeded\r\n", IMAP->authseq);
-                       return;
-               }
-       }
-       cprintf("%s NO authentication failed\r\n", IMAP->authseq);
-}
-
-void imap_auth_login_user(char *cmd)
-{
-       char buf[SIZ];
-
-       CtdlDecodeBase64(buf, cmd, SIZ);
-       CtdlLoginExistingUser(NULL, buf);
-       CtdlEncodeBase64(buf, "Password:", 9);
-       cprintf("+ %s\r\n", buf);
-       IMAP->authstate = imap_as_expecting_password;
-       return;
-}
-
-void imap_auth_login_pass(char *cmd)
-{
-       char buf[SIZ];
-
-       CtdlDecodeBase64(buf, cmd, SIZ);
-       if (CtdlTryPassword(buf) == pass_ok) {
-               cprintf("%s OK authentication succeeded\r\n", IMAP->authseq);
-       } else {
-               cprintf("%s NO authentication failed\r\n", IMAP->authseq);
-       }
-       IMAP->authstate = imap_as_normal;
-       return;
-}
-
-
-/*
- * implements the STARTTLS command (Citadel API version)
- */
-#ifdef HAVE_OPENSSL
-void imap_starttls(int num_parms, char *parms[])
-{
-       char ok_response[SIZ];
-       char nosup_response[SIZ];
-       char error_response[SIZ];
-
-       sprintf(ok_response,
-               "%s OK begin TLS negotiation now\r\n",
-               parms[0]);
-       sprintf(nosup_response,
-               "%s NO TLS not supported here\r\n",
-               parms[0]);
-       sprintf(error_response,
-               "%s BAD Internal error\r\n",
-               parms[0]);
-       CtdlStartTLS(ok_response, nosup_response, error_response);
-}
-#endif
-
-
-/*
- * implements the SELECT command
- */
-void imap_select(int num_parms, char *parms[])
-{
-       char towhere[SIZ];
-       char augmented_roomname[ROOMNAMELEN];
-       int c = 0;
-       int ok = 0;
-       int ra = 0;
-       struct ctdlroom QRscratch;
-       int msgs, new;
-       int floornum;
-       int roomflags;
-       int i;
-
-       /* Convert the supplied folder name to a roomname */
-       i = imap_roomname(towhere, sizeof towhere, parms[2]);
-       if (i < 0) {
-               cprintf("%s NO Invalid mailbox name.\r\n", parms[0]);
-               IMAP->selected = 0;
-               return;
-       }
-       floornum = (i & 0x00ff);
-       roomflags = (i & 0xff00);
-
-       /* First try a regular match */
-       c = getroom(&QRscratch, towhere);
-
-       /* Then try a mailbox name match */
-       if (c != 0) {
-               MailboxName(augmented_roomname, sizeof augmented_roomname,
-                           &CC->user, towhere);
-               c = getroom(&QRscratch, augmented_roomname);
-               if (c == 0)
-                       strcpy(towhere, augmented_roomname);
-       }
-
-       /* If the room exists, check security/access */
-       if (c == 0) {
-               /* See if there is an existing user/room relationship */
-               CtdlRoomAccess(&QRscratch, &CC->user, &ra, NULL);
-
-               /* normal clients have to pass through security */
-               if (ra & UA_KNOWN) {
-                       ok = 1;
-               }
-       }
-
-       /* Fail here if no such room */
-       if (!ok) {
-               cprintf("%s NO ... no such room, or access denied\r\n",
-                       parms[0]);
-               return;
-       }
-
-       /* If we already had some other folder selected, auto-expunge it */
-       imap_do_expunge();
-
-       /*
-        * usergoto() formally takes us to the desired room, happily returning
-        * the number of messages and number of new messages.
-        */
-       memcpy(&CC->room, &QRscratch, sizeof(struct ctdlroom));
-       usergoto(NULL, 0, 0, &msgs, &new);
-       IMAP->selected = 1;
-
-       if (!strcasecmp(parms[1], "EXAMINE")) {
-               IMAP->readonly = 1;
-       } else {
-               IMAP->readonly = 0;
-       }
-
-       imap_load_msgids();
-       IMAP->last_mtime = CC->room.QRmtime;
-
-       cprintf("* %d EXISTS\r\n", msgs);
-       cprintf("* %d RECENT\r\n", new);
-
-       cprintf("* OK [UIDVALIDITY 1] UID validity status\r\n");
-       cprintf("* OK [UIDNEXT %ld] Predicted next UID\r\n", CitControl.MMhighest + 1);
-
-       /* Note that \Deleted is a valid flag, but not a permanent flag,
-        * because we don't maintain its state across sessions.  Citadel
-        * automatically expunges mailboxes when they are de-selected.
-        */
-       cprintf("* FLAGS (\\Deleted \\Seen \\Answered)\r\n");
-       cprintf("* OK [PERMANENTFLAGS (\\Deleted \\Seen \\Answered)] "
-               "permanent flags\r\n");
-
-       cprintf("%s OK [%s] %s completed\r\n",
-               parms[0],
-               (IMAP->readonly ? "READ-ONLY" : "READ-WRITE"), parms[1]);
-}
-
-
-
-/*
- * Does the real work for expunge.
- */
-int imap_do_expunge(void)
-{
-       int i;
-       int num_expunged = 0;
-       long *delmsgs = NULL;
-       int num_delmsgs = 0;
-
-       lprintf(CTDL_DEBUG, "imap_do_expunge() called\n");
-       if (IMAP->selected == 0) {
-               return (0);
-       }
-
-       if (IMAP->num_msgs > 0) {
-               delmsgs = malloc(IMAP->num_msgs * sizeof(long));
-               for (i = 0; i < IMAP->num_msgs; ++i) {
-                       if (IMAP->flags[i] & IMAP_DELETED) {
-                               delmsgs[num_delmsgs++] = IMAP->msgids[i];
-                       }
-               }
-               if (num_delmsgs > 0) {
-                       CtdlDeleteMessages(CC->room.QRname, delmsgs, num_delmsgs, "");
-               }
-               num_expunged += num_delmsgs;
-               free(delmsgs);
-       }
-
-       if (num_expunged > 0) {
-               imap_rescan_msgids();
-       }
-
-       lprintf(CTDL_DEBUG, "Expunged %d messages from <%s>\n",
-               num_expunged, CC->room.QRname);
-       return (num_expunged);
-}
-
-
-/*
- * implements the EXPUNGE command syntax
- */
-void imap_expunge(int num_parms, char *parms[])
-{
-       int num_expunged = 0;
-
-       num_expunged = imap_do_expunge();
-       cprintf("%s OK expunged %d messages.\r\n", parms[0], num_expunged);
-}
-
-
-/*
- * implements the CLOSE command
- */
-void imap_close(int num_parms, char *parms[])
-{
-
-       /* Yes, we always expunge on close. */
-       if (IMAP->selected) {
-               imap_do_expunge();
-       }
-
-       IMAP->selected = 0;
-       IMAP->readonly = 0;
-       imap_free_msgids();
-       cprintf("%s OK CLOSE completed\r\n", parms[0]);
-}
-
-
-/*
- * Implements the NAMESPACE command.
- */
-void imap_namespace(int num_parms, char *parms[])
-{
-       int i;
-       struct floor *fl;
-       int floors = 0;
-       char buf[SIZ];
-
-       cprintf("* NAMESPACE ");
-
-       /* All personal folders are subordinate to INBOX. */
-       cprintf("((\"INBOX/\" \"/\")) ");
-
-       /* Other users' folders ... coming soon! FIXME */
-       cprintf("NIL ");
-
-       /* Show all floors as shared namespaces.  Neato! */
-       cprintf("(");
-       for (i = 0; i < MAXFLOORS; ++i) {
-               fl = cgetfloor(i);
-               if (fl->f_flags & F_INUSE) {
-                       if (floors > 0) cprintf(" ");
-                       cprintf("(");
-                       sprintf(buf, "%s/", fl->f_name);
-                       imap_strout(buf);
-                       cprintf(" \"/\")");
-                       ++floors;
-               }
-       }
-       cprintf(")");
-
-       /* Wind it up with a newline and a completion message. */
-       cprintf("\r\n");
-       cprintf("%s OK NAMESPACE completed\r\n", parms[0]);
-}
-
-
-
-/*
- * Implements the CREATE command
- *
- */
-void imap_create(int num_parms, char *parms[])
-{
-       int ret;
-       char roomname[ROOMNAMELEN];
-       int floornum;
-       int flags;
-       int newroomtype = 0;
-       int newroomview = 0;
-
-       if (strchr(parms[2], '\\') != NULL) {
-               cprintf("%s NO Invalid character in folder name\r\n",
-                       parms[0]);
-               lprintf(CTDL_DEBUG, "invalid character in folder name\n");
-               return;
-       }
-
-       ret = imap_roomname(roomname, sizeof roomname, parms[2]);
-       if (ret < 0) {
-               cprintf("%s NO Invalid mailbox name or location\r\n",
-                       parms[0]);
-               lprintf(CTDL_DEBUG, "invalid mailbox name or location\n");
-               return;
-       }
-       floornum = (ret & 0x00ff);      /* lower 8 bits = floor number */
-       flags = (ret & 0xff00); /* upper 8 bits = flags        */
-
-       if (flags & IR_MAILBOX) {
-               if (strncasecmp(parms[2], "INBOX/", 6)) {
-                       cprintf("%s NO Personal folders must be created under INBOX\r\n", parms[0]);
-                       lprintf(CTDL_DEBUG, "not subordinate to inbox\n");
-                       return;
-               }
-       }
-
-       if (flags & IR_MAILBOX) {
-               newroomtype = 4;                /* private mailbox */
-               newroomview = VIEW_MAILBOX;
-       } else {
-               newroomtype = 0;                /* public folder */
-               newroomview = VIEW_BBS;
-       }
-
-       lprintf(CTDL_INFO, "Create new room <%s> on floor <%d> with type <%d>\n",
-               roomname, floornum, newroomtype);
-
-       ret = create_room(roomname, newroomtype, "", floornum, 1, 0, newroomview);
-       if (ret == 0) {
-               /*** DO NOT CHANGE THIS ERROR MESSAGE IN ANY WAY!  BYNARI CONNECTOR DEPENDS ON IT! ***/
-               cprintf("%s NO Mailbox already exists, or create failed\r\n", parms[0]);
-       } else {
-               cprintf("%s OK CREATE completed\r\n", parms[0]);
-       }
-       lprintf(CTDL_DEBUG, "imap_create() completed\n");
-}
-
-
-/*
- * Locate a room by its IMAP folder name, and check access to it.
- * If zapped_ok is nonzero, we can also look for the room in the zapped list.
- */
-int imap_grabroom(char *returned_roomname, char *foldername, int zapped_ok)
-{
-       int ret;
-       char augmented_roomname[ROOMNAMELEN];
-       char roomname[ROOMNAMELEN];
-       int c;
-       struct ctdlroom QRscratch;
-       int ra;
-       int ok = 0;
-
-       ret = imap_roomname(roomname, sizeof roomname, foldername);
-       if (ret < 0) {
-               return (1);
-       }
-
-       /* First try a regular match */
-       c = getroom(&QRscratch, roomname);
-
-       /* Then try a mailbox name match */
-       if (c != 0) {
-               MailboxName(augmented_roomname, sizeof augmented_roomname,
-                           &CC->user, roomname);
-               c = getroom(&QRscratch, augmented_roomname);
-               if (c == 0)
-                       strcpy(roomname, augmented_roomname);
-       }
-
-       /* If the room exists, check security/access */
-       if (c == 0) {
-               /* See if there is an existing user/room relationship */
-               CtdlRoomAccess(&QRscratch, &CC->user, &ra, NULL);
-
-               /* normal clients have to pass through security */
-               if (ra & UA_KNOWN) {
-                       ok = 1;
-               }
-               if ((zapped_ok) && (ra & UA_ZAPPED)) {
-                       ok = 1;
-               }
-       }
-
-       /* Fail here if no such room */
-       if (!ok) {
-               strcpy(returned_roomname, "");
-               return (2);
-       } else {
-               strcpy(returned_roomname, QRscratch.QRname);
-               return (0);
-       }
-}
-
-
-/*
- * Implements the STATUS command (sort of)
- *
- */
-void imap_status(int num_parms, char *parms[])
-{
-       int ret;
-       char roomname[ROOMNAMELEN];
-       char buf[SIZ];
-       char savedroom[ROOMNAMELEN];
-       int msgs, new;
-
-       ret = imap_grabroom(roomname, parms[2], 0);
-       if (ret != 0) {
-               cprintf
-                   ("%s NO Invalid mailbox name or location, or access denied\r\n",
-                    parms[0]);
-               return;
-       }
-
-       /*
-        * usergoto() formally takes us to the desired room, happily returning
-        * the number of messages and number of new messages.  (If another
-        * folder is selected, save its name so we can return there!!!!!)
-        */
-       if (IMAP->selected) {
-               strcpy(savedroom, CC->room.QRname);
-       }
-       usergoto(roomname, 0, 0, &msgs, &new);
-
-       /*
-        * Tell the client what it wants to know.  In fact, tell it *more* than
-        * it wants to know.  We happily IGnore the supplied status data item
-        * names and simply spew all possible data items.  It's far easier to
-        * code and probably saves us some processing time too.
-        */
-       imap_mailboxname(buf, sizeof buf, &CC->room);
-       cprintf("* STATUS ");
-       imap_strout(buf);
-       cprintf(" (MESSAGES %d ", msgs);
-       cprintf("RECENT %d ", new);     /* Initially, new==recent */
-       cprintf("UIDNEXT %ld ", CitControl.MMhighest + 1);
-       cprintf("UNSEEN %d)\r\n", new);
-
-       /*
-        * If another folder is selected, go back to that room so we can resume
-        * our happy day without violent explosions.
-        */
-       if (IMAP->selected) {
-               usergoto(savedroom, 0, 0, &msgs, &new);
-       }
-
-       /*
-        * Oooh, look, we're done!
-        */
-       cprintf("%s OK STATUS completed\r\n", parms[0]);
-}
-
-
-
-/*
- * Implements the SUBSCRIBE command
- *
- */
-void imap_subscribe(int num_parms, char *parms[])
-{
-       int ret;
-       char roomname[ROOMNAMELEN];
-       char savedroom[ROOMNAMELEN];
-       int msgs, new;
-
-       ret = imap_grabroom(roomname, parms[2], 1);
-       if (ret != 0) {
-               cprintf(
-                       "%s NO Error %d: invalid mailbox name or location, or access denied\r\n",
-                       parms[0],
-                       ret
-               );
-               return;
-       }
-
-       /*
-        * usergoto() formally takes us to the desired room, which has the side
-        * effect of marking the room as not-zapped ... exactly the effect
-        * we're looking for.
-        */
-       if (IMAP->selected) {
-               strcpy(savedroom, CC->room.QRname);
-       }
-       usergoto(roomname, 0, 0, &msgs, &new);
-
-       /*
-        * If another folder is selected, go back to that room so we can resume
-        * our happy day without violent explosions.
-        */
-       if (IMAP->selected) {
-               usergoto(savedroom, 0, 0, &msgs, &new);
-       }
-
-       cprintf("%s OK SUBSCRIBE completed\r\n", parms[0]);
-}
-
-
-/*
- * Implements the UNSUBSCRIBE command
- *
- */
-void imap_unsubscribe(int num_parms, char *parms[])
-{
-       int ret;
-       char roomname[ROOMNAMELEN];
-       char savedroom[ROOMNAMELEN];
-       int msgs, new;
-
-       ret = imap_grabroom(roomname, parms[2], 0);
-       if (ret != 0) {
-               cprintf
-                   ("%s NO Invalid mailbox name or location, or access denied\r\n",
-                    parms[0]);
-               return;
-       }
-
-       /*
-        * usergoto() formally takes us to the desired room.
-        */
-       if (IMAP->selected) {
-               strcpy(savedroom, CC->room.QRname);
-       }
-       usergoto(roomname, 0, 0, &msgs, &new);
-
-       /* 
-        * Now make the API call to zap the room
-        */
-       if (CtdlForgetThisRoom() == 0) {
-               cprintf("%s OK UNSUBSCRIBE completed\r\n", parms[0]);
-       } else {
-               cprintf
-                   ("%s NO You may not unsubscribe from this folder.\r\n",
-                    parms[0]);
-       }
-
-       /*
-        * If another folder is selected, go back to that room so we can resume
-        * our happy day without violent explosions.
-        */
-       if (IMAP->selected) {
-               usergoto(savedroom, 0, 0, &msgs, &new);
-       }
-}
-
-
-
-/*
- * Implements the DELETE command
- *
- */
-void imap_delete(int num_parms, char *parms[])
-{
-       int ret;
-       char roomname[ROOMNAMELEN];
-       char savedroom[ROOMNAMELEN];
-       int msgs, new;
-
-       ret = imap_grabroom(roomname, parms[2], 1);
-       if (ret != 0) {
-               cprintf("%s NO Invalid mailbox name, or access denied\r\n",
-                       parms[0]);
-               return;
-       }
-
-       /*
-        * usergoto() formally takes us to the desired room, happily returning
-        * the number of messages and number of new messages.  (If another
-        * folder is selected, save its name so we can return there!!!!!)
-        */
-       if (IMAP->selected) {
-               strcpy(savedroom, CC->room.QRname);
-       }
-       usergoto(roomname, 0, 0, &msgs, &new);
-
-       /*
-        * Now delete the room.
-        */
-       if (CtdlDoIHavePermissionToDeleteThisRoom(&CC->room)) {
-               schedule_room_for_deletion(&CC->room);
-               cprintf("%s OK DELETE completed\r\n", parms[0]);
-       } else {
-               cprintf("%s NO Can't delete this folder.\r\n", parms[0]);
-       }
-
-       /*
-        * If another folder is selected, go back to that room so we can resume
-        * our happy day without violent explosions.
-        */
-       if (IMAP->selected) {
-               usergoto(savedroom, 0, 0, &msgs, &new);
-       }
-}
-
-
-/*
- * Back end function for imap_rename()
- */
-void imap_rename_backend(struct ctdlroom *qrbuf, void *data)
-{
-       char foldername[SIZ];
-       char newfoldername[SIZ];
-       char newroomname[ROOMNAMELEN];
-       int newfloor = 0;
-       struct irl *irlp = NULL;        /* scratch pointer */
-       struct irlparms *irlparms;
-
-       irlparms = (struct irlparms *) data;
-       imap_mailboxname(foldername, sizeof foldername, qrbuf);
-
-       /* Rename subfolders */
-       if ((!strncasecmp(foldername, irlparms->oldname,
-                         strlen(irlparms->oldname))
-            && (foldername[strlen(irlparms->oldname)] == '/'))) {
-
-               sprintf(newfoldername, "%s/%s",
-                       irlparms->newname,
-                       &foldername[strlen(irlparms->oldname) + 1]
-                   );
-
-               newfloor = imap_roomname(newroomname,
-                                        sizeof newroomname,
-                                        newfoldername) & 0xFF;
-
-               irlp = (struct irl *) malloc(sizeof(struct irl));
-               strcpy(irlp->irl_newroom, newroomname);
-               strcpy(irlp->irl_oldroom, qrbuf->QRname);
-               irlp->irl_newfloor = newfloor;
-               irlp->next = *(irlparms->irl);
-               *(irlparms->irl) = irlp;
-       }
-}
-
-
-/*
- * Implements the RENAME command
- *
- */
-void imap_rename(int num_parms, char *parms[])
-{
-       char old_room[ROOMNAMELEN];
-       char new_room[ROOMNAMELEN];
-       int oldr, newr;
-       int new_floor;
-       int r;
-       struct irl *irl = NULL; /* the list */
-       struct irl *irlp = NULL;        /* scratch pointer */
-       struct irlparms irlparms;
-
-       if (strchr(parms[3], '\\') != NULL) {
-               cprintf("%s NO Invalid character in folder name\r\n",
-                       parms[0]);
-               return;
-       }
-
-       oldr = imap_roomname(old_room, sizeof old_room, parms[2]);
-       newr = imap_roomname(new_room, sizeof new_room, parms[3]);
-       new_floor = (newr & 0xFF);
-
-       r = CtdlRenameRoom(old_room, new_room, new_floor);
-
-       if (r == crr_room_not_found) {
-               cprintf("%s NO Could not locate this folder\r\n",
-                       parms[0]);
-               return;
-       }
-       if (r == crr_already_exists) {
-               cprintf("%s '%s' already exists.\r\n", parms[0], parms[2]);
-               return;
-       }
-       if (r == crr_noneditable) {
-               cprintf("%s This folder is not editable.\r\n", parms[0]);
-               return;
-       }
-       if (r == crr_invalid_floor) {
-               cprintf("%s Folder root does not exist.\r\n", parms[0]);
-               return;
-       }
-       if (r == crr_access_denied) {
-               cprintf("%s You do not have permission to edit "
-                       "this folder.\r\n", parms[0]);
-               return;
-       }
-       if (r != crr_ok) {
-               cprintf("%s NO Rename failed - undefined error %d\r\n",
-                       parms[0], r);
-               return;
-       }
-
-
-       /* If this is the INBOX, then RFC2060 says we have to just move the
-        * contents.  In a Citadel environment it's easier to rename the room
-        * (already did that) and create a new inbox.
-        */
-       if (!strcasecmp(parms[2], "INBOX")) {
-               create_room(MAILROOM, 4, "", 0, 1, 0, VIEW_MAILBOX);
-       }
-
-       /* Otherwise, do the subfolders.  Build a list of rooms to rename... */
-       else {
-               irlparms.oldname = parms[2];
-               irlparms.newname = parms[3];
-               irlparms.irl = &irl;
-               ForEachRoom(imap_rename_backend, (void *) &irlparms);
-
-               /* ... and now rename them. */
-               while (irl != NULL) {
-                       r = CtdlRenameRoom(irl->irl_oldroom,
-                                          irl->irl_newroom,
-                                          irl->irl_newfloor);
-                       if (r != crr_ok) {
-                               /* FIXME handle error returns better */
-                               lprintf(CTDL_ERR, "CtdlRenameRoom() error %d\n", r);
-                       }
-                       irlp = irl;
-                       irl = irl->next;
-                       free(irlp);
-               }
-       }
-
-       cprintf("%s OK RENAME completed\r\n", parms[0]);
-}
-
-
-
-
-/* 
- * Main command loop for IMAP sessions.
- */
-void imap_command_loop(void)
-{
-       char cmdbuf[SIZ];
-       char *parms[SIZ];
-       int num_parms;
-       struct timeval tv1, tv2;
-       suseconds_t total_time = 0;
-
-       gettimeofday(&tv1, NULL);
-       CC->lastcmd = time(NULL);
-       memset(cmdbuf, 0, sizeof cmdbuf);       /* Clear it, just in case */
-       flush_output();
-       if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
-               lprintf(CTDL_ERR, "Client disconnected: ending session.\r\n");
-               CC->kill_me = 1;
-               return;
-       }
-
-       if (IMAP->authstate == imap_as_expecting_password) {
-               lprintf(CTDL_INFO, "IMAP: <password>\n");
-       }
-       else if (IMAP->authstate == imap_as_expecting_plainauth) {
-               lprintf(CTDL_INFO, "IMAP: <plain_auth>\n");
-       }
-       else if (bmstrcasestr(cmdbuf, " LOGIN ")) {
-               lprintf(CTDL_INFO, "IMAP: LOGIN...\n");
-       }
-       else {
-               lprintf(CTDL_INFO, "IMAP: %s\n", cmdbuf);
-       }
-
-       while (strlen(cmdbuf) < 5)
-               strcat(cmdbuf, " ");
-
-       /* strip off l/t whitespace and CRLF */
-       if (cmdbuf[strlen(cmdbuf) - 1] == '\n')
-               cmdbuf[strlen(cmdbuf) - 1] = 0;
-       if (cmdbuf[strlen(cmdbuf) - 1] == '\r')
-               cmdbuf[strlen(cmdbuf) - 1] = 0;
-       striplt(cmdbuf);
-
-       /* If we're in the middle of a multi-line command, handle that */
-       if (IMAP->authstate == imap_as_expecting_username) {
-               imap_auth_login_user(cmdbuf);
-               return;
-       }
-       if (IMAP->authstate == imap_as_expecting_plainauth) {
-               imap_auth_plain(cmdbuf);
-               return;
-       }
-       if (IMAP->authstate == imap_as_expecting_password) {
-               imap_auth_login_pass(cmdbuf);
-               return;
-       }
-
-       /* Ok, at this point we're in normal command mode.  The first thing
-        * we do is print any incoming pages (yeah! we really do!)
-        */
-       imap_print_instant_messages();
-
-       /*
-        * Before processing the command that was just entered... if we happen
-        * to have a folder selected, we'd like to rescan that folder for new
-        * messages, and for deletions/changes of existing messages.  This
-        * could probably be optimized somehow, but IMAP sucks...
-        */
-       if (IMAP->selected) {
-               imap_rescan_msgids();
-       }
-
-       /* Now for the command set. */
-
-       /* Grab the tag, command, and parameters.  Check syntax. */
-       num_parms = imap_parameterize(parms, cmdbuf);
-       if (num_parms < 2) {
-               cprintf("BAD syntax error\r\n");
-       }
-
-       /* The commands below may be executed in any state */
-
-       else if ((!strcasecmp(parms[1], "NOOP"))
-                || (!strcasecmp(parms[1], "CHECK"))) {
-               cprintf("%s OK No operation\r\n",
-                       parms[0]);
-       }
-
-       else if (!strcasecmp(parms[1], "ID")) {
-               imap_id(num_parms, parms);
-       }
-
-
-       else if (!strcasecmp(parms[1], "LOGOUT")) {
-               if (IMAP->selected) {
-                       imap_do_expunge();      /* yes, we auto-expunge */
-               }
-               cprintf("* BYE %s logging out\r\n", config.c_fqdn);
-               cprintf("%s OK Citadel IMAP session ended.\r\n",
-                       parms[0]);
-               CC->kill_me = 1;
-               return;
-       }
-
-       else if (!strcasecmp(parms[1], "LOGIN")) {
-               imap_login(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "AUTHENTICATE")) {
-               imap_authenticate(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "CAPABILITY")) {
-               imap_capability(num_parms, parms);
-       }
-#ifdef HAVE_OPENSSL
-       else if (!strcasecmp(parms[1], "STARTTLS")) {
-               imap_starttls(num_parms, parms);
-       }
-#endif
-       else if (!CC->logged_in) {
-               cprintf("%s BAD Not logged in.\r\n", parms[0]);
-       }
-
-       /* The commans below require a logged-in state */
-
-       else if (!strcasecmp(parms[1], "SELECT")) {
-               imap_select(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "EXAMINE")) {
-               imap_select(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "LSUB")) {
-               imap_list(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "LIST")) {
-               imap_list(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "CREATE")) {
-               imap_create(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "DELETE")) {
-               imap_delete(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "RENAME")) {
-               imap_rename(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "STATUS")) {
-               imap_status(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "SUBSCRIBE")) {
-               imap_subscribe(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "UNSUBSCRIBE")) {
-               imap_unsubscribe(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "APPEND")) {
-               imap_append(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "NAMESPACE")) {
-               imap_namespace(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "SETACL")) {
-               imap_setacl(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "DELETEACL")) {
-               imap_deleteacl(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "GETACL")) {
-               imap_getacl(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "LISTRIGHTS")) {
-               imap_listrights(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "MYRIGHTS")) {
-               imap_myrights(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "GETMETADATA")) {
-               imap_getmetadata(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "SETMETADATA")) {
-               imap_setmetadata(num_parms, parms);
-       }
-
-       else if (IMAP->selected == 0) {
-               cprintf("%s BAD no folder selected\r\n", parms[0]);
-       }
-
-       /* The commands below require the SELECT state on a mailbox */
-
-       else if (!strcasecmp(parms[1], "FETCH")) {
-               imap_fetch(num_parms, parms);
-       }
-
-       else if ((!strcasecmp(parms[1], "UID"))
-                && (!strcasecmp(parms[2], "FETCH"))) {
-               imap_uidfetch(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "SEARCH")) {
-               imap_search(num_parms, parms);
-       }
-
-       else if ((!strcasecmp(parms[1], "UID"))
-                && (!strcasecmp(parms[2], "SEARCH"))) {
-               imap_uidsearch(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "STORE")) {
-               imap_store(num_parms, parms);
-       }
-
-       else if ((!strcasecmp(parms[1], "UID"))
-                && (!strcasecmp(parms[2], "STORE"))) {
-               imap_uidstore(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "COPY")) {
-               imap_copy(num_parms, parms);
-       }
-
-       else if ((!strcasecmp(parms[1], "UID"))
-                && (!strcasecmp(parms[2], "COPY"))) {
-               imap_uidcopy(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "EXPUNGE")) {
-               imap_expunge(num_parms, parms);
-       }
-
-       else if (!strcasecmp(parms[1], "CLOSE")) {
-               imap_close(num_parms, parms);
-       }
-
-       /* End of commands.  If we get here, the command is either invalid
-        * or unimplemented.
-        */
-
-       else {
-               cprintf("%s BAD command unrecognized\r\n", parms[0]);
-       }
-
-       /* If the client transmitted a message we can free it now */
-       imap_free_transmitted_message();
-
-       gettimeofday(&tv2, NULL);
-       total_time = (tv2.tv_usec + (tv2.tv_sec * 1000000)) - (tv1.tv_usec + (tv1.tv_sec * 1000000));
-       lprintf(CTDL_DEBUG, "IMAP command completed in %ld.%ld seconds\n",
-               (total_time / 1000000),
-               (total_time % 1000000)
-       );
-}
-
-
-/*
- * This function is called to register the IMAP extension with Citadel.
- */
-CTDL_MODULE_INIT(imap)
-{
-       CtdlRegisterServiceHook(config.c_imap_port,
-                               NULL, imap_greeting, imap_command_loop, NULL);
-#ifdef HAVE_OPENSSL
-       CtdlRegisterServiceHook(config.c_imaps_port,
-                               NULL, imaps_greeting, imap_command_loop, NULL);
-#endif
-       CtdlRegisterSessionHook(imap_cleanup_function, EVT_STOP);
-
-       /* return our Subversion id for the Log */
-       return "$Id$";
-}
diff --git a/citadel/serv_imap.h b/citadel/serv_imap.h
deleted file mode 100644 (file)
index c7d932a..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/* $Id$ 
- */
-
-
-void imap_cleanup_function(void);
-void imap_greeting(void);
-void imap_command_loop(void);
-int imap_grabroom(char *returned_roomname, char *foldername, int zapped_ok);
-void imap_free_transmitted_message(void);
-int imap_do_expunge(void);
-void imap_rescan_msgids(void);
-
-
-struct citimap {
-       int authstate;
-       char authseq[SIZ];
-       int selected;                   /* set to 1 if in the SELECTED state */
-       int readonly;                   /* mailbox is open read only */
-       int num_msgs;                   /* Number of messages being mapped */
-       int num_alloc;                  /* Number of messages for which we've allocated space */
-       time_t last_mtime;              /* For checking whether the room was modified... */
-       long *msgids;
-       unsigned int *flags;
-       char *transmitted_message;      /* for APPEND command... */
-       size_t transmitted_length;
-
-       /* Cache most recent RFC822 FETCH because client might load in pieces */
-       char *cached_rfc822_data;
-       long cached_rfc822_msgnum;
-       size_t cached_rfc822_len;
-       char cached_rfc822_withbody;    /* 1 = body cached; 0 = only headers cached */
-
-       /* Cache most recent BODY FETCH because client might load in pieces */
-       char *cached_body;
-       size_t cached_body_len;
-       char cached_bodypart[SIZ];
-       long cached_bodymsgnum;
-       char cached_body_withbody;      /* 1 = body cached; 0 = only headers cached */
-};
-
-/*
- * values of 'authstate'
- */
-enum {
-       imap_as_normal,
-       imap_as_expecting_username,
-       imap_as_expecting_password,
-       imap_as_expecting_plainauth
-};
-
-/* Flags for the above struct.  Note that some of these are for internal use,
- * and are not to be reported to IMAP clients.
- */
-#define IMAP_ANSWERED          1       /* reportable and setable */
-#define IMAP_FLAGGED           2       /* reportable and setable */
-#define IMAP_DELETED           4       /* reportable and setable */
-#define IMAP_DRAFT             8       /* reportable and setable */
-#define IMAP_SEEN              16      /* reportable and setable */
-
-#define IMAP_MASK_SETABLE      0x1f
-#define IMAP_MASK_SYSTEM       0xe0
-
-#define IMAP_SELECTED          32      /* neither reportable nor setable */
-#define IMAP_RECENT            64      /* reportable but not setable */
-
-
-#define IMAP CC->IMAP
-
-/*
- * When loading arrays of message ID's into memory, increase the buffer to
- * hold this many additional messages instead of calling realloc() each time.
- */
-#define REALLOC_INCREMENT 100
diff --git a/citadel/serv_ldap.h b/citadel/serv_ldap.h
deleted file mode 100644 (file)
index f2595db..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * $Id$
- *
- */
-
-#ifdef HAVE_LDAP
-
-void ctdl_vcard_to_ldap(struct CtdlMessage *msg, int op);
-
-enum {
-       V2L_WRITE,
-       V2L_DELETE
-};
-
-#endif /* HAVE_LDAP */
index c455cff538c8f72f21b03baf53bb0add7296d0ce..11cd6319b8a982bc28c4b10159e16df7005d5b75 100644 (file)
@@ -451,6 +451,15 @@ struct RoomFunctionHook {
 extern struct RoomFunctionHook *RoomHookTable;
 
 
+struct MaintenanceThreadHook {
+       struct MaintenanceThreadHook *next;
+       char *name;
+       void *(*fcn_ptr) (void *arg);
+       pthread_t MaintenanceThread_tid;
+};
+extern struct MaintenanceThreadHook *MaintenanceThreadHookTable;
+
+
 
 /* Defines the relationship of a user to a particular room */
 struct visit {
index 23ef71d91df4f136fe26746fd6f40a3330e764e1..318f72e2b5f149041b5bb42ae125f23d8a98f5b0 100644 (file)
@@ -297,7 +297,7 @@ int main(int argc, char **argv)
        }
        end_critical_section(S_WORKER_LIST);
 
-       /* Create the indexer thread. */
+       /* Create the maintenance threads. */
        create_maintenance_threads();
 
        /* This thread is now useless.  It can't be turned into a worker
index 64ea7ee6b3e0e229b905dedaf093944f3a1ac492..1ce836558162ea1e661e56839048bfacad7a848f 100644 (file)
@@ -61,8 +61,7 @@
 #include "database.h"
 #include "housekeeping.h"
 #include "tools.h"
-#include "serv_crypto.h"
-#include "serv_fulltext.h"
+#include "modules/crypto/serv_crypto.h"        /* Needed for init_ssl, client_write_ssl, client_read_ssl, destruct_ssl */
 
 #ifdef HAVE_SYS_SELECT_H
 #include <sys/select.h>
@@ -94,8 +93,6 @@ struct CitContext masterCC;
 time_t last_purge = 0;                         /* Last dead session purge */
 static int num_threads = 0;                    /* Current number of threads */
 int num_sessions = 0;                          /* Current number of sessions */
-pthread_t indexer_thread_tid;
-pthread_t checkpoint_thread_tid;
 
 int syslog_facility = LOG_DAEMON;
 int enable_syslog = 0;
@@ -980,8 +977,7 @@ void DestroyWorkerList(void)
 }
 
 /*
- * Create the indexer thread and begin its operation.
- * Then create the checkpoint thread and begin its operation.
+ * Create the maintenance threads and begin their operation.
  */
 void create_maintenance_threads(void) {
        int ret;
@@ -1004,17 +1000,23 @@ void create_maintenance_threads(void) {
                pthread_attr_destroy(&attr);
                return;
        }
+       
+       struct MaintenanceThreadHook *fcn;
 
-       if ((ret = pthread_create(&indexer_thread_tid, &attr, indexer_thread, NULL) != 0)) {
-               lprintf(CTDL_ALERT, "Can't create thread: %s\n", strerror(ret));
-       }
+       lprintf(CTDL_DEBUG, "Performing startup of maintenance thread hooks\n");
 
-       if ((ret = pthread_create(&checkpoint_thread_tid, &attr, checkpoint_thread, NULL) != 0)) {
-               lprintf(CTDL_ALERT, "Can't create thread: %s\n", strerror(ret));
+       for (fcn = MaintenanceThreadHookTable; fcn != NULL; fcn = fcn->next) {
+               if ((ret = pthread_create(&(fcn->MaintenanceThread_tid), &attr, fcn->fcn_ptr, NULL) != 0)) {
+                       lprintf(CTDL_ALERT, "Can't create thread: %s\n", strerror(ret));
+               }
+               else
+               {
+                       lprintf(CTDL_NOTICE, "Spawned a new maintenance thread \"%s\" (%ld). \n", fcn->name,
+                               fcn->MaintenanceThread_tid);
+               }
        }
 
-       lprintf(CTDL_NOTICE, "Spawned indexer (%ld) and checkpoint (%ld) thread. \n", 
-               indexer_thread_tid, checkpoint_thread_tid);
+
        pthread_attr_destroy(&attr);
 }
 
index a1e551c9f468467b6fb86b010ae101c405bafe8d..54d889384be131e951fee142a9c94ab0c27a1bf8 100644 (file)
@@ -106,7 +106,5 @@ void dump_heap(void);
 #endif
 
 void create_maintenance_threads(void);
-extern pthread_t indexer_thread_tid;
-extern pthread_t checkpoint_thread_tid;
 
 #endif /* SYSDEP_DECLS_H */