X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Fimap%2Fserv_imap.c;h=55a80e8b0241f82f0aff512920a8c999e3855bba;hb=fa222fcfed9856832eb86a3cbaae7a044ff6142b;hp=b7d44b5579310712d81e9c87422925e29a341e52;hpb=a605e70c6ecfccfe650a85ef4a32ebbf9cd4385f;p=citadel.git diff --git a/citadel/modules/imap/serv_imap.c b/citadel/modules/imap/serv_imap.c index b7d44b557..55a80e8b0 100644 --- a/citadel/modules/imap/serv_imap.c +++ b/citadel/modules/imap/serv_imap.c @@ -1,13 +1,26 @@ /* - * $Id$ - * * IMAP server for the Citadel system - * Copyright (C) 2000-2007 by Art Cancro and others. + * + * Copyright (C) 2000-2011 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. + * + * This program is open source software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "sysdep.h" @@ -35,17 +48,15 @@ #include #include #include +#include #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" @@ -57,9 +68,81 @@ #include "imap_metadata.h" #include "imap_misc.h" - #include "ctdl_module.h" +int IMAPDebugEnabled = 0; +HashList *ImapCmds = NULL; +void registerImapCMD(const char *First, long FLen, + const char *Second, long SLen, + imap_handler H, + int Flags) +{ + imap_handler_hook *h; + + h = (imap_handler_hook*) malloc(sizeof(imap_handler_hook)); + memset(h, 0, sizeof(imap_handler_hook)); + + h->Flags = Flags; + h->h = H; + if (SLen == 0) { + Put(ImapCmds, First, FLen, h, NULL); + } + else { + char CMD[SIZ]; + memcpy(CMD, First, FLen); + memcpy(CMD+FLen, Second, SLen); + CMD[FLen+SLen] = '\0'; + Put(ImapCmds, CMD, FLen + SLen, h, NULL); + } +} + +void imap_cleanup(void) +{ + DeleteHash(&ImapCmds); +} +const imap_handler_hook *imap_lookup(int num_parms, ConstStr *Params) +{ + struct CitContext *CCC = CC; + void *v; + citimap *Imap = CCCIMAP; + + if (num_parms < 1) + return NULL; + + /* we abuse the Reply-buffer for uppercasing... */ + StrBufPlain(Imap->Reply, CKEY(Params[1])); + StrBufUpCase(Imap->Reply); + + IMAP_syslog(LOG_DEBUG, "---- Looking up [%s] -----", + ChrPtr(Imap->Reply)); + if (GetHash(ImapCmds, SKEY(Imap->Reply), &v)) + { + IMAPM_syslog(LOG_DEBUG, "Found."); + FlushStrBuf(Imap->Reply); + return (imap_handler_hook *) v; + } + + if (num_parms == 1) + { + IMAPM_syslog(LOG_DEBUG, "NOT Found."); + FlushStrBuf(Imap->Reply); + return NULL; + } + + IMAP_syslog(LOG_DEBUG, "---- Looking up [%s] -----", + ChrPtr(Imap->Reply)); + StrBufAppendBufPlain(Imap->Reply, CKEY(Params[2]), 0); + StrBufUpCase(Imap->Reply); + if (GetHash(ImapCmds, SKEY(Imap->Reply), &v)) + { + IMAPM_syslog(LOG_DEBUG, "Found."); + FlushStrBuf(Imap->Reply); + return (imap_handler_hook *) v; + } + IMAPM_syslog(LOG_DEBUG, "NOT Found."); + FlushStrBuf(Imap->Reply); + return NULL; +} /* imap_rename() uses this struct containing list of rooms to rename */ struct irl { @@ -70,11 +153,13 @@ struct irl { }; /* Data which is passed between imap_rename() and imap_rename_backend() */ -struct irlparms { - char *oldname; - char *newname; +typedef struct __irlparms { + const char *oldname; + long oldnamelen; + const char *newname; + long newnamelen; struct irl **irl; -}; +}irlparms; /* @@ -82,17 +167,18 @@ struct irlparms { */ 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); + citimap *Imap = IMAP; + 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); } @@ -101,11 +187,7 @@ void imap_free_msgids(void) */ void imap_free_transmitted_message(void) { - if (IMAP->transmitted_message != NULL) { - free(IMAP->transmitted_message); - IMAP->transmitted_message = NULL; - IMAP->transmitted_length = 0; - } + FreeStrBuf(&IMAP->TransmittedMessage); } @@ -121,20 +203,21 @@ void imap_free_transmitted_message(void) */ void imap_set_seen_flags(int first_msg) { - struct visit vbuf; + citimap *Imap = IMAP; + 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; + 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; + 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; } /* @@ -158,10 +241,10 @@ void imap_set_seen_flags(int first_msg) 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; + 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; } } } @@ -184,9 +267,9 @@ void imap_set_seen_flags(int first_msg) 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; + for (i = first_msg; i < Imap->num_msgs; ++i) { + if ((Imap->msgids[i] >= lo) && (Imap->msgids[i] <= hi)){ + Imap->flags[i] |= IMAP_ANSWERED; } } } @@ -204,17 +287,16 @@ void imap_set_seen_flags(int first_msg) */ void imap_add_single_msgid(long msgnum, void *userdata) { + citimap *Imap = IMAP; - ++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->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(unsigned int *)) ); } - IMAP->msgids[IMAP->num_msgs - 1] = msgnum; - IMAP->flags[IMAP->num_msgs - 1] = 0; + Imap->msgids[Imap->num_msgs - 1] = msgnum; + Imap->flags[Imap->num_msgs - 1] = 0; } @@ -224,11 +306,12 @@ void imap_add_single_msgid(long msgnum, void *userdata) */ void imap_load_msgids(void) { + struct CitContext *CCC = CC; struct cdbdata *cdbfr; + citimap *Imap = CCCIMAP; - if (IMAP->selected == 0) { - lprintf(CTDL_ERR, - "imap_load_msgids() can't run; no room selected\n"); + if (Imap->selected == 0) { + IMAPM_syslog(LOG_ERR, "imap_load_msgids() can't run; no room selected"); return; } @@ -237,16 +320,16 @@ void imap_load_msgids(void) /* 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); + 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)) ); + if (Imap->num_msgs) { + Imap->flags = malloc(Imap->num_alloc * sizeof(unsigned int *)); + memset(Imap->flags, 0, (Imap->num_alloc * sizeof(long)) ); } imap_set_seen_flags(0); @@ -258,7 +341,8 @@ void imap_load_msgids(void) */ void imap_rescan_msgids(void) { - + struct CitContext *CCC = CC; + citimap *Imap = CCCIMAP; int original_num_msgs = 0; long original_highest = 0L; int i, j, jstart; @@ -268,9 +352,8 @@ void imap_rescan_msgids(void) 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"); + if (Imap->selected == 0) { + IMAPM_syslog(LOG_ERR, "imap_load_msgids() can't run; no room selected"); return; } @@ -278,8 +361,8 @@ void imap_rescan_msgids(void) * 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! */ + CtdlGetRoom(&CC->room, CC->room.QRname); + if (Imap->last_mtime == CC->room.QRmtime) { /* No changes! */ return; } @@ -288,10 +371,11 @@ void imap_rescan_msgids(void) */ cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long)); if (cdbfr != NULL) { - msglist = malloc(cdbfr->len); + msglist = malloc(cdbfr->len + 1); if (msglist == NULL) { - lprintf(CTDL_CRIT, "malloc() failed\n"); - abort(); + IMAPM_syslog(LOG_CRIT, "malloc() failed"); + CC->kill_me = KILLME_MALLOC_FAILED; + return; } memcpy(msglist, cdbfr->ptr, (size_t)cdbfr->len); num_msgs = cdbfr->len / sizeof(long); @@ -303,14 +387,14 @@ void imap_rescan_msgids(void) /* * Check to see if any of the messages we know about have been expunged */ - if (IMAP->num_msgs > 0) { + if (Imap->num_msgs > 0) { jstart = 0; - for (i = 0; i < IMAP->num_msgs; ++i) { + 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]) { + if (msglist[j] == Imap->msgids[i]) { message_still_exists = 1; jstart = j; break; @@ -319,22 +403,22 @@ void imap_rescan_msgids(void) } if (message_still_exists == 0) { - cprintf("* %d EXPUNGE\r\n", i + 1); + IAPrintf("* %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))); + --Imap->num_msgs; + memmove(&Imap->msgids[i], + &Imap->msgids[i + 1], + (sizeof(long) * + (Imap->num_msgs - i))); + memmove(&Imap->flags[i], + &Imap->flags[i + 1], + (sizeof(long) * + (Imap->num_msgs - i))); --i; } @@ -345,15 +429,17 @@ void imap_rescan_msgids(void) /* * 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]; + 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. + * This logic is probably the cause of Bug # 368 + * [ http://bugzilla.citadel.org/show_bug.cgi?id=368 ] */ if (num_msgs > 0) { for (j = 0; j < num_msgs; ++j) { @@ -367,66 +453,64 @@ void imap_rescan_msgids(void) /* * If new messages have arrived, tell the client about them. */ - if (IMAP->num_msgs > original_num_msgs) { + if (Imap->num_msgs > original_num_msgs) { for (j = 0; j < num_msgs; ++j) { - if (IMAP->flags[j] & IMAP_RECENT) { + if (Imap->flags[j] & IMAP_RECENT) { ++num_recent; } } - cprintf("* %d EXISTS\r\n", IMAP->num_msgs); - cprintf("* %d RECENT\r\n", num_recent); + IAPrintf("* %d EXISTS\r\n", Imap->num_msgs); + IAPrintf("* %d RECENT\r\n", num_recent); } - if (num_msgs != 0) { + if (msglist != NULL) { free(msglist); } - IMAP->last_mtime = CC->room.QRmtime; + 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) { + struct CitContext *CCC = CC; + citimap *Imap = CCCIMAP; - /* Don't do this stuff if this is not a IMAP session! */ + /* 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) { + if (Imap->selected) { imap_do_expunge(); } - lprintf(CTDL_DEBUG, "Performing IMAP cleanup hook\n"); + IMAPM_syslog(LOG_DEBUG, "Performing IMAP cleanup hook"); 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_rfc822 != NULL) { + FreeStrBuf(&Imap->cached_rfc822); + 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); + 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"); + FreeStrBuf(&Imap->Cmd.CmdBuf); + FreeStrBuf(&Imap->Reply); + if (Imap->Cmd.Params != NULL) free(Imap->Cmd.Params); + free(Imap); + IMAPM_syslog(LOG_DEBUG, "Finished IMAP cleanup hook"); } @@ -435,38 +519,47 @@ void imap_cleanup_function(void) * output this stuff in other places as well) */ void imap_output_capability_string(void) { - cprintf("CAPABILITY IMAP4REV1 NAMESPACE ID AUTH=PLAIN AUTH=LOGIN"); + IAPuts("CAPABILITY IMAP4REV1 NAMESPACE ID AUTH=PLAIN AUTH=LOGIN UIDPLUS"); #ifdef HAVE_OPENSSL - if (!CC->redirect_ssl) cprintf(" STARTTLS"); + if (!CC->redirect_ssl) IAPuts(" STARTTLS"); #endif #ifndef DISABLE_IMAP_ACL - cprintf(" ACL"); + IAPuts(" ACL"); #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. + * It is not a full RFC5464 implementation, but it should refuse non-Bynari + * metadata in a compatible and graceful way. + */ + IAPuts(" METADATA"); + + /* + * LIST-EXTENDED was originally going to be required by the METADATA extension. + * It was mercifully removed prior to the finalization of RFC5464. We started + * implementing this but stopped when we learned that it would not be needed. + * If you uncomment this declaration you are responsible for writing a lot of new + * code. + * + * IAPuts(" LIST-EXTENDED") */ - cprintf(" METADATA"); - /* cprintf(" LIST-EXTENDED"); */ } + /* * implements the CAPABILITY command */ -void imap_capability(int num_parms, char *parms[]) +void imap_capability(int num_parms, ConstStr *Params) { - cprintf("* "); + IAPuts("* "); imap_output_capability_string(); - cprintf("\r\n"); - cprintf("%s OK CAPABILITY completed\r\n", parms[0]); + IAPuts("\r\n"); + IReply("OK CAPABILITY completed"); } - /* * Implements the ID command (specified by RFC2971) * @@ -476,40 +569,52 @@ void imap_capability(int num_parms, char *parms[]) * making use of this extension. * */ -void imap_id(int num_parms, char *parms[]) +void imap_id(int num_parms, ConstStr *Params) { - cprintf("* ID NIL\r\n"); - cprintf("%s OK ID completed\r\n", parms[0]); + IAPuts("* ID NIL\r\n"); + IReply("OK ID completed"); } - /* * Here's where our IMAP session begins its happy day. */ void imap_greeting(void) { + citimap *Imap; + CitContext *CCC = CC; + + strcpy(CCC->cs_clientname, "IMAP session"); + CCC->session_specific_data = malloc(sizeof(citimap)); + Imap = (citimap *)CCC->session_specific_data; + memset(Imap, 0, sizeof(citimap)); + Imap->authstate = imap_as_normal; + Imap->cached_rfc822_msgnum = (-1); + Imap->cached_rfc822_withbody = 0; + Imap->Reply = NewStrBufPlain(NULL, SIZ * 10); /* 40k */ + + if (CCC->nologin) + { + IAPuts("* BYE; Server busy, try later\r\n"); + CCC->kill_me = KILLME_NOLOGIN; + IUnbuffer(); + return; + } - 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 ["); + IAPuts("* OK ["); imap_output_capability_string(); - cprintf("] %s IMAP4rev1 %s ready\r\n", config.c_fqdn, CITADEL); + IAPrintf("] %s IMAP4rev1 %s ready\r\n", config.c_fqdn, CITADEL); + IUnbuffer(); } + /* * IMAPS is just like IMAP, except it goes crypto right away. */ void imaps_greeting(void) { CtdlModuleStartCryptoMsgs(NULL, NULL, NULL); #ifdef HAVE_OPENSSL - if (!CC->redirect_ssl) CC->kill_me = 1; /* kill session if no crypto */ + if (!CC->redirect_ssl) CC->kill_me = KILLME_NO_CRYPTO; /* kill session if no crypto */ #endif imap_greeting(); } @@ -518,81 +623,108 @@ void imaps_greeting(void) { /* * implements the LOGIN command (ordinary username/password login) */ -void imap_login(int num_parms, char *parms[]) +void imap_login(int num_parms, ConstStr *Params) { - 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); + switch (num_parms) { + case 3: + if (Params[2].Key[0] == '{') { + IAPuts("+ go ahead\r\n"); + IMAP->authstate = imap_as_expecting_multilineusername; + strcpy(IMAP->authseq, Params[0].Key); + return; + } + else { + IReply("BAD incorrect number of parameters"); return; } + case 4: + if (CtdlLoginExistingUser(NULL, Params[2].Key) == login_ok) { + if (CtdlTryPassword(Params[3].Key, Params[3].len) == pass_ok) { + /* hm, thats not doable by IReply :-( */ + IAPrintf("%s OK [", Params[0].Key); + imap_output_capability_string(); + IAPrintf("] Hello, %s\r\n", CC->user.fullname); + return; + } + else + { + IReplyPrintf("NO AUTHENTICATE %s failed", + Params[3].Key); + } + } + + IReply("BAD Login incorrect"); + default: + IReply("BAD incorrect number of parameters"); + return; } - cprintf("%s BAD Login incorrect\r\n", parms[0]); } /* * Implements the AUTHENTICATE command */ -void imap_authenticate(int num_parms, char *parms[]) +void imap_authenticate(int num_parms, ConstStr *Params) { - char buf[SIZ]; + char UsrBuf[SIZ]; if (num_parms != 3) { - cprintf("%s BAD incorrect number of parameters\r\n", - parms[0]); + IReply("BAD incorrect number of parameters"); return; } if (CC->logged_in) { - cprintf("%s BAD Already logged in.\r\n", parms[0]); + IReply("BAD Already logged in."); return; } - if (!strcasecmp(parms[2], "LOGIN")) { - CtdlEncodeBase64(buf, "Username:", 9); - cprintf("+ %s\r\n", buf); + if (!strcasecmp(Params[2].Key, "LOGIN")) { + CtdlEncodeBase64(UsrBuf, "Username:", 9, 0); + IAPrintf("+ %s\r\n", UsrBuf); IMAP->authstate = imap_as_expecting_username; - strcpy(IMAP->authseq, parms[0]); + strcpy(IMAP->authseq, Params[0].Key); return; } - if (!strcasecmp(parms[2], "PLAIN")) { - // CtdlEncodeBase64(buf, "Username:", 9); - // cprintf("+ %s\r\n", buf); - cprintf("+ \r\n"); + if (!strcasecmp(Params[2].Key, "PLAIN")) { + // CtdlEncodeBase64(UsrBuf, "Username:", 9, 0); + // IAPuts("+ %s\r\n", UsrBuf); + IAPuts("+ \r\n"); IMAP->authstate = imap_as_expecting_plainauth; - strcpy(IMAP->authseq, parms[0]); + strcpy(IMAP->authseq, Params[0].Key); return; } else { - cprintf("%s NO AUTHENTICATE %s failed\r\n", - parms[0], parms[1]); + IReplyPrintf("NO AUTHENTICATE %s failed", + Params[1].Key); } } -void imap_auth_plain(char *cmd) + +void imap_auth_plain(void) { - char decoded_authstring[1024]; + citimap *Imap = IMAP; + const char *decoded_authstring; char ident[256]; char user[256]; char pass[256]; int result; + long len; + + memset(pass, 0, sizeof(pass)); + StrBufDecodeBase64(Imap->Cmd.CmdBuf); - CtdlDecodeBase64(decoded_authstring, cmd, strlen(cmd)); + decoded_authstring = ChrPtr(Imap->Cmd.CmdBuf); 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); + len = safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass); + if (len < 0) + len = sizeof(pass) - 1; - IMAP->authstate = imap_as_normal; + Imap->authstate = imap_as_normal; if (!IsEmptyStr(ident)) { result = CtdlLoginExistingUser(user, ident); @@ -602,37 +734,66 @@ void imap_auth_plain(char *cmd) } if (result == login_ok) { - if (CtdlTryPassword(pass) == pass_ok) { - cprintf("%s OK authentication succeeded\r\n", IMAP->authseq); + if (CtdlTryPassword(pass, len) == pass_ok) { + IAPrintf("%s OK authentication succeeded\r\n", Imap->authseq); return; } } - cprintf("%s NO authentication failed\r\n", IMAP->authseq); + IAPrintf("%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_user(long state) +{ + char PWBuf[SIZ]; + citimap *Imap = IMAP; + + switch (state){ + case imap_as_expecting_username: + StrBufDecodeBase64(Imap->Cmd.CmdBuf); + CtdlLoginExistingUser(NULL, ChrPtr(Imap->Cmd.CmdBuf)); + CtdlEncodeBase64(PWBuf, "Password:", 9, 0); + IAPrintf("+ %s\r\n", PWBuf); + + Imap->authstate = imap_as_expecting_password; + return; + case imap_as_expecting_multilineusername: + extract_token(PWBuf, ChrPtr(Imap->Cmd.CmdBuf), 1, ' ', sizeof(PWBuf)); + CtdlLoginExistingUser(NULL, ChrPtr(Imap->Cmd.CmdBuf)); + IAPuts("+ go ahead\r\n"); + Imap->authstate = imap_as_expecting_multilinepassword; + 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); +void imap_auth_login_pass(long state) +{ + citimap *Imap = IMAP; + const char *pass = NULL; + long len = 0; + + switch (state) { + default: + case imap_as_expecting_password: + StrBufDecodeBase64(Imap->Cmd.CmdBuf); + pass = ChrPtr(Imap->Cmd.CmdBuf); + len = StrLength(Imap->Cmd.CmdBuf); + break; + case imap_as_expecting_multilinepassword: + pass = ChrPtr(Imap->Cmd.CmdBuf); + len = StrLength(Imap->Cmd.CmdBuf); + break; + } + if (len > USERNAME_SIZE) + StrBufCutAt(Imap->Cmd.CmdBuf, USERNAME_SIZE, NULL); + + if (CtdlTryPassword(pass, len) == pass_ok) { + IAPrintf("%s OK authentication succeeded\r\n", Imap->authseq); } else { - cprintf("%s NO authentication failed\r\n", IMAP->authseq); + IAPrintf("%s NO authentication failed\r\n", Imap->authseq); } - IMAP->authstate = imap_as_normal; + Imap->authstate = imap_as_normal; return; } @@ -640,21 +801,15 @@ void imap_auth_login_pass(char *cmd) /* * implements the STARTTLS command (Citadel API version) */ -void imap_starttls(int num_parms, char *parms[]) +void imap_starttls(int num_parms, ConstStr *Params) { 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]); + snprintf(ok_response, SIZ, "%s OK begin TLS negotiation now\r\n", Params[0].Key); + snprintf(nosup_response, SIZ, "%s NO TLS not supported here\r\n", Params[0].Key); + snprintf(error_response, SIZ, "%s BAD Internal error\r\n", Params[0].Key); CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response); } @@ -662,39 +817,36 @@ void imap_starttls(int num_parms, char *parms[]) /* * implements the SELECT command */ -void imap_select(int num_parms, char *parms[]) +void imap_select(int num_parms, ConstStr *Params) { - char towhere[SIZ]; + citimap *Imap = IMAP; + char towhere[ROOMNAMELEN]; 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]); + i = imap_roomname(towhere, sizeof towhere, Params[2].Key); if (i < 0) { - cprintf("%s NO Invalid mailbox name.\r\n", parms[0]); - IMAP->selected = 0; + IReply("NO Invalid mailbox name."); + Imap->selected = 0; return; } - floornum = (i & 0x00ff); - roomflags = (i & 0xff00); /* First try a regular match */ - c = getroom(&QRscratch, towhere); + c = CtdlGetRoom(&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); + CtdlMailboxName(augmented_roomname, sizeof augmented_roomname, &CC->user, towhere); + c = CtdlGetRoom(&QRscratch, augmented_roomname); + if (c == 0) { + safestrncpy(towhere, augmented_roomname, sizeof(towhere)); + } } /* If the room exists, check security/access */ @@ -710,8 +862,7 @@ void imap_select(int num_parms, char *parms[]) /* Fail here if no such room */ if (!ok) { - cprintf("%s NO ... no such room, or access denied\r\n", - parms[0]); + IReply("NO ... no such room, or access denied"); return; } @@ -719,63 +870,68 @@ void imap_select(int num_parms, char *parms[]) imap_do_expunge(); /* - * usergoto() formally takes us to the desired room, happily returning + * CtdlUserGoto() 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; + CtdlUserGoto(NULL, 0, 0, &msgs, &new); + Imap->selected = 1; - if (!strcasecmp(parms[1], "EXAMINE")) { - IMAP->readonly = 1; + if (!strcasecmp(Params[1].Key, "EXAMINE")) { + Imap->readonly = 1; } else { - IMAP->readonly = 0; + Imap->readonly = 0; } imap_load_msgids(); - IMAP->last_mtime = CC->room.QRmtime; + Imap->last_mtime = CC->room.QRmtime; - cprintf("* %d EXISTS\r\n", msgs); - cprintf("* %d RECENT\r\n", new); + IAPrintf("* %d EXISTS\r\n", msgs); + IAPrintf("* %d RECENT\r\n", new); - cprintf("* OK [UIDVALIDITY %ld] UID validity status\r\n", GLOBAL_UIDVALIDITY_VALUE); - cprintf("* OK [UIDNEXT %ld] Predicted next UID\r\n", CitControl.MMhighest + 1); + IAPrintf("* OK [UIDVALIDITY %ld] UID validity status\r\n", GLOBAL_UIDVALIDITY_VALUE); + IAPrintf("* OK [UIDNEXT %ld] Predicted next UID\r\n", CitControl.MMhighest + 1); - /* Note that \Deleted is a valid flag, but not a permanent flag, + /* Technically, \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. + * + * Unfortunately, omitting \Deleted as a PERMANENTFLAGS flag causes + * some clients (particularly Thunderbird) to misbehave -- they simply + * elect not to transmit the flag at all. So we have to advertise + * \Deleted as a PERMANENTFLAGS flag, even though it technically isn't. */ - cprintf("* FLAGS (\\Deleted \\Seen \\Answered)\r\n"); - cprintf("* OK [PERMANENTFLAGS (\\Deleted \\Seen \\Answered)] " - "permanent flags\r\n"); + IAPuts("* FLAGS (\\Deleted \\Seen \\Answered)\r\n"); + IAPuts("* 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]); + IReplyPrintf("OK [%s] %s completed", + (Imap->readonly ? "READ-ONLY" : "READ-WRITE"), Params[1].Key + ); } - /* * Does the real work for expunge. */ int imap_do_expunge(void) { + struct CitContext *CCC = CC; + citimap *Imap = CCCIMAP; 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) { + IMAPM_syslog(LOG_DEBUG, "imap_do_expunge() called"); + 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 (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) { @@ -789,8 +945,7 @@ int imap_do_expunge(void) imap_rescan_msgids(); } - lprintf(CTDL_DEBUG, "Expunged %d messages from <%s>\n", - num_expunged, CC->room.QRname); + IMAP_syslog(LOG_DEBUG, "Expunged %d messages from <%s>", num_expunged, CC->room.QRname); return (num_expunged); } @@ -798,19 +953,19 @@ int imap_do_expunge(void) /* * implements the EXPUNGE command syntax */ -void imap_expunge(int num_parms, char *parms[]) +void imap_expunge(int num_parms, ConstStr *Params) { int num_expunged = 0; num_expunged = imap_do_expunge(); - cprintf("%s OK expunged %d messages.\r\n", parms[0], num_expunged); + IReplyPrintf("OK expunged %d messages.", num_expunged); } /* * implements the CLOSE command */ -void imap_close(int num_parms, char *parms[]) +void imap_close(int num_parms, ConstStr *Params) { /* Yes, we always expunge on close. */ @@ -821,84 +976,88 @@ void imap_close(int num_parms, char *parms[]) IMAP->selected = 0; IMAP->readonly = 0; imap_free_msgids(); - cprintf("%s OK CLOSE completed\r\n", parms[0]); + IReply("OK CLOSE completed"); } /* * Implements the NAMESPACE command. */ -void imap_namespace(int num_parms, char *parms[]) +void imap_namespace(int num_parms, ConstStr *Params) { int i; struct floor *fl; int floors = 0; - char buf[SIZ]; + char Namespace[SIZ]; - cprintf("* NAMESPACE "); + IAPuts("* NAMESPACE "); /* All personal folders are subordinate to INBOX. */ - cprintf("((\"INBOX/\" \"/\")) "); + IAPuts("((\"INBOX/\" \"/\")) "); /* Other users' folders ... coming soon! FIXME */ - cprintf("NIL "); + IAPuts("NIL "); /* Show all floors as shared namespaces. Neato! */ - cprintf("("); + IAPuts("("); for (i = 0; i < MAXFLOORS; ++i) { - fl = cgetfloor(i); + fl = CtdlGetCachedFloor(i); if (fl->f_flags & F_INUSE) { - if (floors > 0) cprintf(" "); - cprintf("("); - sprintf(buf, "%s/", fl->f_name); - imap_strout(buf); - cprintf(" \"/\")"); + /* if (floors > 0) IAPuts(" "); samjam says this confuses javamail */ + IAPuts("("); + snprintf(Namespace, sizeof(Namespace), "%s/", fl->f_name); + plain_imap_strout(Namespace); + IAPuts(" \"/\")"); ++floors; } } - cprintf(")"); + IAPuts(")"); /* Wind it up with a newline and a completion message. */ - cprintf("\r\n"); - cprintf("%s OK NAMESPACE completed\r\n", parms[0]); + IAPuts("\r\n"); + IReply("OK NAMESPACE completed"); } - /* * Implements the CREATE command * */ -void imap_create(int num_parms, char *parms[]) +void imap_create(int num_parms, ConstStr *Params) { + struct CitContext *CCC = CC; int ret; char roomname[ROOMNAMELEN]; int floornum; int flags; int newroomtype = 0; int newroomview = 0; + char *notification_message = NULL; + + if (num_parms < 3) { + IReply("NO A foder name must be specified"); + return; + } - 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"); + if (strchr(Params[2].Key, '\\') != NULL) { + IReply("NO Invalid character in folder name"); + IMAPM_syslog(LOG_ERR, "invalid character in folder name"); return; } - ret = imap_roomname(roomname, sizeof roomname, parms[2]); + ret = imap_roomname(roomname, sizeof roomname, Params[2].Key); if (ret < 0) { - cprintf("%s NO Invalid mailbox name or location\r\n", - parms[0]); - lprintf(CTDL_DEBUG, "invalid mailbox name or location\n"); + IReply("NO Invalid mailbox name or location"); + IMAPM_syslog(LOG_ERR, "invalid mailbox name or location"); 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"); + if (strncasecmp(Params[2].Key, "INBOX/", 6)) { + IReply("NO Personal folders must be created under INBOX"); + IMAPM_syslog(LOG_ERR, "not subordinate to inbox"); return; } } @@ -911,17 +1070,29 @@ void imap_create(int num_parms, char *parms[]) newroomview = VIEW_BBS; } - lprintf(CTDL_INFO, "Create new room <%s> on floor <%d> with type <%d>\n", - roomname, floornum, newroomtype); + IMAP_syslog(LOG_INFO, "Create new room <%s> on floor <%d> with type <%d>", + roomname, floornum, newroomtype); - ret = create_room(roomname, newroomtype, "", floornum, 1, 0, newroomview); + ret = CtdlCreateRoom(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]); + IReply("NO Mailbox already exists, or create failed"); } else { - cprintf("%s OK CREATE completed\r\n", parms[0]); + IReply("OK CREATE completed"); + /* post a message in Aide> describing the new room */ + notification_message = malloc(1024); + snprintf(notification_message, 1024, + "A new room called \"%s\" has been created by %s%s%s%s\n", + roomname, + CC->user.fullname, + ((ret & QR_MAILBOX) ? " [personal]" : ""), + ((ret & QR_PRIVATE) ? " [private]" : ""), + ((ret & QR_GUESSNAME) ? " [hidden]" : "") + ); + CtdlAideMessage(notification_message, "Room Creation Message"); + free(notification_message); } - lprintf(CTDL_DEBUG, "imap_create() completed\n"); + IMAPM_syslog(LOG_DEBUG, "imap_create() completed"); } @@ -929,7 +1100,7 @@ void imap_create(int num_parms, char *parms[]) * 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 imap_grabroom(char *returned_roomname, const char *foldername, int zapped_ok) { int ret; char augmented_roomname[ROOMNAMELEN]; @@ -945,15 +1116,15 @@ int imap_grabroom(char *returned_roomname, char *foldername, int zapped_ok) } /* First try a regular match */ - c = getroom(&QRscratch, roomname); + c = CtdlGetRoom(&QRscratch, roomname); /* Then try a mailbox name match */ if (c != 0) { - MailboxName(augmented_roomname, sizeof augmented_roomname, + CtdlMailboxName(augmented_roomname, sizeof augmented_roomname, &CC->user, roomname); - c = getroom(&QRscratch, augmented_roomname); + c = CtdlGetRoom(&QRscratch, augmented_roomname); if (c == 0) - strcpy(roomname, augmented_roomname); + safestrncpy(roomname, augmented_roomname, sizeof(roomname)); } /* If the room exists, check security/access */ @@ -975,7 +1146,7 @@ int imap_grabroom(char *returned_roomname, char *foldername, int zapped_ok) strcpy(returned_roomname, ""); return (2); } else { - strcpy(returned_roomname, QRscratch.QRname); + safestrncpy(returned_roomname, QRscratch.QRname, ROOMNAMELEN); return (0); } } @@ -985,31 +1156,29 @@ int imap_grabroom(char *returned_roomname, char *foldername, int zapped_ok) * Implements the STATUS command (sort of) * */ -void imap_status(int num_parms, char *parms[]) +void imap_status(int num_parms, ConstStr *Params) { int ret; char roomname[ROOMNAMELEN]; - char buf[SIZ]; + char imaproomname[SIZ]; char savedroom[ROOMNAMELEN]; int msgs, new; - ret = imap_grabroom(roomname, parms[2], 0); + ret = imap_grabroom(roomname, Params[2].Key, 1); if (ret != 0) { - cprintf - ("%s NO Invalid mailbox name or location, or access denied\r\n", - parms[0]); + IReply("NO Invalid mailbox name or location, or access denied"); return; } /* - * usergoto() formally takes us to the desired room, happily returning + * CtdlUserGoto() 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); + CtdlUserGoto(roomname, 0, 0, &msgs, &new); /* * Tell the client what it wants to know. In fact, tell it *more* than @@ -1017,70 +1186,68 @@ void imap_status(int num_parms, char *parms[]) * 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); - + imap_mailboxname(imaproomname, sizeof imaproomname, &CC->room); + IAPuts("* STATUS "); + plain_imap_strout(imaproomname); + IAPrintf(" (MESSAGES %d ", msgs); + IAPrintf("RECENT %d ", new); /* Initially, new==recent */ + IAPrintf("UIDNEXT %ld ", CitControl.MMhighest + 1); + IAPrintf("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); + CtdlUserGoto(savedroom, 0, 0, &msgs, &new); } /* * Oooh, look, we're done! */ - cprintf("%s OK STATUS completed\r\n", parms[0]); + IReply("OK STATUS completed"); } - /* * Implements the SUBSCRIBE command * */ -void imap_subscribe(int num_parms, char *parms[]) +void imap_subscribe(int num_parms, ConstStr *Params) { int ret; char roomname[ROOMNAMELEN]; char savedroom[ROOMNAMELEN]; int msgs, new; - ret = imap_grabroom(roomname, parms[2], 1); + ret = imap_grabroom(roomname, Params[2].Key, 1); if (ret != 0) { - cprintf( - "%s NO Error %d: invalid mailbox name or location, or access denied\r\n", - parms[0], + IReplyPrintf( + "NO Error %d: invalid mailbox name or location, or access denied", ret ); return; } /* - * usergoto() formally takes us to the desired room, which has the side + * CtdlUserGoto() 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); + CtdlUserGoto(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); + CtdlUserGoto(savedroom, 0, 0, &msgs, &new); } - cprintf("%s OK SUBSCRIBE completed\r\n", parms[0]); + IReply("OK SUBSCRIBE completed"); } @@ -1088,38 +1255,34 @@ void imap_subscribe(int num_parms, char *parms[]) * Implements the UNSUBSCRIBE command * */ -void imap_unsubscribe(int num_parms, char *parms[]) +void imap_unsubscribe(int num_parms, ConstStr *Params) { int ret; char roomname[ROOMNAMELEN]; char savedroom[ROOMNAMELEN]; int msgs, new; - ret = imap_grabroom(roomname, parms[2], 0); + ret = imap_grabroom(roomname, Params[2].Key, 1); if (ret != 0) { - cprintf - ("%s NO Invalid mailbox name or location, or access denied\r\n", - parms[0]); + IReply("NO Invalid mailbox name or location, or access denied"); return; } /* - * usergoto() formally takes us to the desired room. + * CtdlUserGoto() formally takes us to the desired room. */ if (IMAP->selected) { strcpy(savedroom, CC->room.QRname); } - usergoto(roomname, 0, 0, &msgs, &new); + CtdlUserGoto(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]); + IReply("OK UNSUBSCRIBE completed"); } else { - cprintf - ("%s NO You may not unsubscribe from this folder.\r\n", - parms[0]); + IReply("NO You may not unsubscribe from this folder."); } /* @@ -1127,48 +1290,46 @@ void imap_unsubscribe(int num_parms, char *parms[]) * our happy day without violent explosions. */ if (IMAP->selected) { - usergoto(savedroom, 0, 0, &msgs, &new); + CtdlUserGoto(savedroom, 0, 0, &msgs, &new); } } - /* * Implements the DELETE command * */ -void imap_delete(int num_parms, char *parms[]) +void imap_delete(int num_parms, ConstStr *Params) { int ret; char roomname[ROOMNAMELEN]; char savedroom[ROOMNAMELEN]; int msgs, new; - ret = imap_grabroom(roomname, parms[2], 1); + ret = imap_grabroom(roomname, Params[2].Key, 1); if (ret != 0) { - cprintf("%s NO Invalid mailbox name, or access denied\r\n", - parms[0]); + IReply("NO Invalid mailbox name, or access denied"); return; } /* - * usergoto() formally takes us to the desired room, happily returning + * CtdlUserGoto() 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); + CtdlUserGoto(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]); + CtdlScheduleRoomForDeletion(&CC->room); + IReply("OK DELETE completed"); } else { - cprintf("%s NO Can't delete this folder.\r\n", parms[0]); + IReply("NO Can't delete this folder."); } /* @@ -1176,7 +1337,7 @@ void imap_delete(int num_parms, char *parms[]) * our happy day without violent explosions. */ if (IMAP->selected) { - usergoto(savedroom, 0, 0, &msgs, &new); + CtdlUserGoto(savedroom, 0, 0, &msgs, &new); } } @@ -1191,19 +1352,19 @@ void imap_rename_backend(struct ctdlroom *qrbuf, void *data) char newroomname[ROOMNAMELEN]; int newfloor = 0; struct irl *irlp = NULL; /* scratch pointer */ - struct irlparms *irlparms; + irlparms *myirlparms; - irlparms = (struct irlparms *) data; + myirlparms = (irlparms *) data; imap_mailboxname(foldername, sizeof foldername, qrbuf); /* Rename subfolders */ - if ((!strncasecmp(foldername, irlparms->oldname, - strlen(irlparms->oldname)) - && (foldername[strlen(irlparms->oldname)] == '/'))) { + if ((!strncasecmp(foldername, myirlparms->oldname, + myirlparms->oldnamelen) + && (foldername[myirlparms->oldnamelen] == '/'))) { sprintf(newfoldername, "%s/%s", - irlparms->newname, - &foldername[strlen(irlparms->oldname) + 1] + myirlparms->newname, + &foldername[myirlparms->oldnamelen + 1] ); newfloor = imap_roomname(newroomname, @@ -1214,8 +1375,8 @@ void imap_rename_backend(struct ctdlroom *qrbuf, void *data) strcpy(irlp->irl_newroom, newroomname); strcpy(irlp->irl_oldroom, qrbuf->QRname); irlp->irl_newfloor = newfloor; - irlp->next = *(irlparms->irl); - *(irlparms->irl) = irlp; + irlp->next = *(myirlparms->irl); + *(myirlparms->irl) = irlp; } } @@ -1224,72 +1385,70 @@ void imap_rename_backend(struct ctdlroom *qrbuf, void *data) * Implements the RENAME command * */ -void imap_rename(int num_parms, char *parms[]) +void imap_rename(int num_parms, ConstStr *Params) { char old_room[ROOMNAMELEN]; char new_room[ROOMNAMELEN]; - int oldr, newr; + int newr; int new_floor; int r; struct irl *irl = NULL; /* the list */ struct irl *irlp = NULL; /* scratch pointer */ - struct irlparms irlparms; + irlparms irlparms; + char aidemsg[1024]; - if (strchr(parms[3], '\\') != NULL) { - cprintf("%s NO Invalid character in folder name\r\n", - parms[0]); + if (strchr(Params[3].Key, '\\') != NULL) { + IReply("NO Invalid character in folder name"); return; } - oldr = imap_roomname(old_room, sizeof old_room, parms[2]); - newr = imap_roomname(new_room, sizeof new_room, parms[3]); + imap_roomname(old_room, sizeof old_room, Params[2].Key); + newr = imap_roomname(new_room, sizeof new_room, Params[3].Key); 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]); + IReply("NO Could not locate this folder"); return; } if (r == crr_already_exists) { - cprintf("%s '%s' already exists.\r\n", parms[0], parms[2]); + IReplyPrintf("NO '%s' already exists."); return; } if (r == crr_noneditable) { - cprintf("%s This folder is not editable.\r\n", parms[0]); + IReply("NO This folder is not editable."); return; } if (r == crr_invalid_floor) { - cprintf("%s Folder root does not exist.\r\n", parms[0]); + IReply("NO Folder root does not exist."); return; } if (r == crr_access_denied) { - cprintf("%s You do not have permission to edit " - "this folder.\r\n", parms[0]); + IReply("NO You do not have permission to edit this folder."); return; } if (r != crr_ok) { - cprintf("%s NO Rename failed - undefined error %d\r\n", - parms[0], r); + IReplyPrintf("NO Rename failed - undefined error %d", 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); + if (!strcasecmp(Params[2].Key, "INBOX")) { + CtdlCreateRoom(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.oldname = Params[2].Key; + irlparms.oldnamelen = Params[2].len; + irlparms.newname = Params[3].Key; + irlparms.newnamelen = Params[3].len; irlparms.irl = &irl; - ForEachRoom(imap_rename_backend, (void *) &irlparms); + CtdlForEachRoom(imap_rename_backend, (void *) &irlparms); /* ... and now rename them. */ while (irl != NULL) { @@ -1297,8 +1456,9 @@ void imap_rename(int num_parms, char *parms[]) irl->irl_newroom, irl->irl_newfloor); if (r != crr_ok) { + struct CitContext *CCC = CC; /* FIXME handle error returns better */ - lprintf(CTDL_ERR, "CtdlRenameRoom() error %d\n", r); + IMAP_syslog(LOG_ERR, "CtdlRenameRoom() error %d", r); } irlp = irl; irl = irl->next; @@ -1306,10 +1466,15 @@ void imap_rename(int num_parms, char *parms[]) } } - cprintf("%s OK RENAME completed\r\n", parms[0]); -} - + snprintf(aidemsg, sizeof aidemsg, "IMAP folder \"%s\" renamed to \"%s\" by %s\n", + Params[2].Key, + Params[3].Key, + CC->curr_user + ); + CtdlAideMessage(aidemsg, "IMAP folder rename"); + IReply("OK RENAME completed"); +} /* @@ -1317,293 +1482,249 @@ void imap_rename(int num_parms, char *parms[]) */ void imap_command_loop(void) { - char cmdbuf[SIZ]; - char *parms[SIZ]; - int num_parms; + struct CitContext *CCC = CC; struct timeval tv1, tv2; suseconds_t total_time = 0; + citimap *Imap; + const char *pchs, *pche; + const imap_handler_hook *h; gettimeofday(&tv1, NULL); - CC->lastcmd = time(NULL); - memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */ + CCC->lastcmd = time(NULL); + Imap = CCCIMAP; + flush_output(); - if (client_getln(cmdbuf, sizeof cmdbuf) < 1) { - lprintf(CTDL_ERR, "Client disconnected: ending session.\r\n"); - CC->kill_me = 1; + if (Imap->Cmd.CmdBuf == NULL) + Imap->Cmd.CmdBuf = NewStrBufPlain(NULL, SIZ); + else + FlushStrBuf(Imap->Cmd.CmdBuf); + + if (CtdlClientGetLine(Imap->Cmd.CmdBuf) < 1) { + IMAPM_syslog(LOG_ERR, "client disconnected: ending session."); + CC->kill_me = KILLME_CLIENT_DISCONNECTED; return; } - if (IMAP->authstate == imap_as_expecting_password) { - lprintf(CTDL_INFO, "IMAP: \n"); + if (Imap->authstate == imap_as_expecting_password) { + IMAPM_syslog(LOG_INFO, ""); } - else if (IMAP->authstate == imap_as_expecting_plainauth) { - lprintf(CTDL_INFO, "IMAP: \n"); + else if (Imap->authstate == imap_as_expecting_plainauth) { + IMAPM_syslog(LOG_INFO, ""); } - else if (bmstrcasestr(cmdbuf, " LOGIN ")) { - lprintf(CTDL_INFO, "IMAP: LOGIN...\n"); + else if ((Imap->authstate == imap_as_expecting_multilineusername) || + cbmstrcasestr(ChrPtr(Imap->Cmd.CmdBuf), " LOGIN ")) { + IMAPM_syslog(LOG_INFO, "LOGIN..."); } else { - lprintf(CTDL_INFO, "IMAP: %s\n", cmdbuf); + IMAP_syslog(LOG_DEBUG, "%s", ChrPtr(Imap->Cmd.CmdBuf)); } - while (strlen(cmdbuf) < 5) - strcat(cmdbuf, " "); + pchs = ChrPtr(Imap->Cmd.CmdBuf); + pche = pchs + StrLength(Imap->Cmd.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); + while ((pche > pchs) && + ((*pche == '\n') || + (*pche == '\r'))) + { + pche --; + StrBufCutRight(Imap->Cmd.CmdBuf, 1); + } + StrBufTrim(Imap->Cmd.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); + switch (Imap->authstate){ + case imap_as_expecting_username: + imap_auth_login_user(imap_as_expecting_username); + IUnbuffer(); return; - } - if (IMAP->authstate == imap_as_expecting_plainauth) { - imap_auth_plain(cmdbuf); + case imap_as_expecting_multilineusername: + imap_auth_login_user(imap_as_expecting_multilineusername); + IUnbuffer(); return; - } - if (IMAP->authstate == imap_as_expecting_password) { - imap_auth_login_pass(cmdbuf); + case imap_as_expecting_plainauth: + imap_auth_plain(); + IUnbuffer(); + return; + case imap_as_expecting_password: + imap_auth_login_pass(imap_as_expecting_password); + IUnbuffer(); return; + case imap_as_expecting_multilinepassword: + imap_auth_login_pass(imap_as_expecting_multilinepassword); + IUnbuffer(); + return; + default: + break; } - /* Ok, at this point we're in normal command mode. The first thing - * we do is print any incoming pages (yeah! we really do!) + /* Ok, at this point we're in normal command mode. + * If the command just submitted does not contain a literal, we + * might think about delivering some untagged stuff... */ - 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(); - } + /* Grab the tag, command, and parameters. */ + imap_parameterize(&Imap->Cmd); +#if 0 +/* debug output the parsed vector */ + { + int i; + IMAP_syslog(LOG_DEBUG, "----- %ld params", Imap->Cmd.num_parms); + + for (i=0; i < Imap->Cmd.num_parms; i++) { + if (Imap->Cmd.Params[i].len != strlen(Imap->Cmd.Params[i].Key)) + IMAP_syslog(LOG_DEBUG, "*********** %ld != %ld : %s", + Imap->Cmd.Params[i].len, + strlen(Imap->Cmd.Params[i].Key), + Imap->Cmd.Params[i].Key); + else + IMAP_syslog(LOG_DEBUG, "%ld : %s", + Imap->Cmd.Params[i].len, + Imap->Cmd.Params[i].Key); + }} +#endif /* Now for the command set. */ + h = imap_lookup(Imap->Cmd.num_parms, Imap->Cmd.Params); - /* 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]); + if (h == NULL) + { + IReply("BAD command unrecognized"); + goto BAIL; } - else if (!strcasecmp(parms[1], "ID")) { - imap_id(num_parms, parms); - } + /* RFC3501 says that we cannot output untagged data during these commands */ + if ((h->Flags & I_FLAG_UNTAGGED) == 0) { + /* we can put any additional untagged stuff right here in the future */ - else if (!strcasecmp(parms[1], "LOGOUT")) { - if (IMAP->selected) { - imap_do_expunge(); /* yes, we auto-expunge */ + /* + * 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 better with some deep thought... + */ + if (Imap->selected) { + imap_rescan_msgids(); } - 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); + /* does our command require a logged-in state */ + if ((!CC->logged_in) && ((h->Flags & I_FLAG_LOGGED_IN) != 0)) { + IReply("BAD Not logged in."); + goto BAIL; } - else if (!strcasecmp(parms[1], "AUTHENTICATE")) { - imap_authenticate(num_parms, parms); + /* does our command require the SELECT state on a mailbox */ + if ((Imap->selected == 0) && ((h->Flags & I_FLAG_SELECT) != 0)){ + IReply("BAD no folder selected"); + goto BAIL; } + h->h(Imap->Cmd.num_parms, Imap->Cmd.Params); - 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], "UID")) && (!strcasecmp(parms[2], "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. - */ + /* If the client transmitted a message we can free it now */ - else { - cprintf("%s BAD command unrecognized\r\n", parms[0]); - } +BAIL: + IUnbuffer(); - /* 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) - ); + IMAP_syslog(LOG_DEBUG, "IMAP command completed in %ld.%ld seconds", + (total_time / 1000000), + (total_time % 1000000) + ); } +void imap_noop (int num_parms, ConstStr *Params) +{ + IReply("OK No operation"); +} + +void imap_logout(int num_parms, ConstStr *Params) +{ + if (IMAP->selected) { + imap_do_expunge(); /* yes, we auto-expunge at logout */ + } + IAPrintf("* BYE %s logging out\r\n", config.c_fqdn); + IReply("OK Citadel IMAP session ended."); + CC->kill_me = KILLME_CLIENT_LOGGED_OUT; + return; +} const char *CitadelServiceIMAP="IMAP"; const char *CitadelServiceIMAPS="IMAPS"; +void SetIMAPDebugEnabled(const int n) +{ + IMAPDebugEnabled = n; +} /* * This function is called to register the IMAP extension with Citadel. */ CTDL_MODULE_INIT(imap) { - CtdlRegisterServiceHook(config.c_imap_port, - NULL, imap_greeting, imap_command_loop, NULL, CitadelServiceIMAP); + if (ImapCmds == NULL) + ImapCmds = NewHash(1, NULL); + + RegisterImapCMD("NOOP", "", imap_noop, I_FLAG_NONE); + RegisterImapCMD("CHECK", "", imap_noop, I_FLAG_NONE); + RegisterImapCMD("ID", "", imap_id, I_FLAG_NONE); + RegisterImapCMD("LOGOUT", "", imap_logout, I_FLAG_NONE); + RegisterImapCMD("LOGIN", "", imap_login, I_FLAG_NONE); + RegisterImapCMD("AUTHENTICATE", "", imap_authenticate, I_FLAG_NONE); + RegisterImapCMD("CAPABILITY", "", imap_capability, I_FLAG_NONE); #ifdef HAVE_OPENSSL - CtdlRegisterServiceHook(config.c_imaps_port, - NULL, imaps_greeting, imap_command_loop, NULL, CitadelServiceIMAPS); + RegisterImapCMD("STARTTLS", "", imap_starttls, I_FLAG_NONE); #endif - CtdlRegisterSessionHook(imap_cleanup_function, EVT_STOP); - /* return our Subversion id for the Log */ - return "$Id$"; + /* The commans below require a logged-in state */ + RegisterImapCMD("SELECT", "", imap_select, I_FLAG_LOGGED_IN); + RegisterImapCMD("EXAMINE", "", imap_select, I_FLAG_LOGGED_IN); + RegisterImapCMD("LSUB", "", imap_list, I_FLAG_LOGGED_IN); + RegisterImapCMD("LIST", "", imap_list, I_FLAG_LOGGED_IN); + RegisterImapCMD("CREATE", "", imap_create, I_FLAG_LOGGED_IN); + RegisterImapCMD("DELETE", "", imap_delete, I_FLAG_LOGGED_IN); + RegisterImapCMD("RENAME", "", imap_rename, I_FLAG_LOGGED_IN); + RegisterImapCMD("STATUS", "", imap_status, I_FLAG_LOGGED_IN); + RegisterImapCMD("SUBSCRIBE", "", imap_subscribe, I_FLAG_LOGGED_IN); + RegisterImapCMD("UNSUBSCRIBE", "", imap_unsubscribe, I_FLAG_LOGGED_IN); + RegisterImapCMD("APPEND", "", imap_append, I_FLAG_LOGGED_IN); + RegisterImapCMD("NAMESPACE", "", imap_namespace, I_FLAG_LOGGED_IN); + RegisterImapCMD("SETACL", "", imap_setacl, I_FLAG_LOGGED_IN); + RegisterImapCMD("DELETEACL", "", imap_deleteacl, I_FLAG_LOGGED_IN); + RegisterImapCMD("GETACL", "", imap_getacl, I_FLAG_LOGGED_IN); + RegisterImapCMD("LISTRIGHTS", "", imap_listrights, I_FLAG_LOGGED_IN); + RegisterImapCMD("MYRIGHTS", "", imap_myrights, I_FLAG_LOGGED_IN); + RegisterImapCMD("GETMETADATA", "", imap_getmetadata, I_FLAG_LOGGED_IN); + RegisterImapCMD("SETMETADATA", "", imap_setmetadata, I_FLAG_LOGGED_IN); + + /* The commands below require the SELECT state on a mailbox */ + RegisterImapCMD("FETCH", "", imap_fetch, I_FLAG_LOGGED_IN | I_FLAG_SELECT | I_FLAG_UNTAGGED); + RegisterImapCMD("UID", "FETCH", imap_uidfetch, I_FLAG_LOGGED_IN | I_FLAG_SELECT); + RegisterImapCMD("SEARCH", "", imap_search, I_FLAG_LOGGED_IN | I_FLAG_SELECT | I_FLAG_UNTAGGED); + RegisterImapCMD("UID", "SEARCH", imap_uidsearch, I_FLAG_LOGGED_IN | I_FLAG_SELECT); + RegisterImapCMD("STORE", "", imap_store, I_FLAG_LOGGED_IN | I_FLAG_SELECT | I_FLAG_UNTAGGED); + RegisterImapCMD("UID", "STORE", imap_uidstore, I_FLAG_LOGGED_IN | I_FLAG_SELECT); + RegisterImapCMD("COPY", "", imap_copy, I_FLAG_LOGGED_IN | I_FLAG_SELECT); + RegisterImapCMD("UID", "COPY", imap_uidcopy, I_FLAG_LOGGED_IN | I_FLAG_SELECT); + RegisterImapCMD("EXPUNGE", "", imap_expunge, I_FLAG_LOGGED_IN | I_FLAG_SELECT); + RegisterImapCMD("UID", "EXPUNGE", imap_expunge, I_FLAG_LOGGED_IN | I_FLAG_SELECT); + RegisterImapCMD("CLOSE", "", imap_close, I_FLAG_LOGGED_IN | I_FLAG_SELECT); + + if (!threading) + { + CtdlRegisterDebugFlagHook(HKEY("imapsrv"), SetIMAPDebugEnabled, &IMAPDebugEnabled); + CtdlRegisterServiceHook(config.c_imap_port, + NULL, imap_greeting, imap_command_loop, NULL, CitadelServiceIMAP); +#ifdef HAVE_OPENSSL + CtdlRegisterServiceHook(config.c_imaps_port, + NULL, imaps_greeting, imap_command_loop, NULL, CitadelServiceIMAPS); +#endif + CtdlRegisterSessionHook(imap_cleanup_function, EVT_STOP, PRIO_STOP + 30); + CtdlRegisterCleanupHook(imap_cleanup); + } + + /* return our module name for the log */ + return "imap"; }