/*
* IMAP server for the Citadel system
*
- * Copyright (C) 2000-2011 by Art Cancro and others.
+ * Copyright (C) 2000-2021 by Art Cancro and others.
* This code is released under the terms of the GNU General Public License.
*
* WARNING: the IMAP protocol is badly designed. No implementation of it
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdep.h"
#include <pwd.h>
#include <errno.h>
#include <sys/types.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
-#endif
-
+#include <time.h>
#include <sys/wait.h>
#include <ctype.h>
#include <string.h>
#include "imap_misc.h"
#include "ctdl_module.h"
-
HashList *ImapCmds = NULL;
void registerImapCMD(const char *First, long FLen,
const char *Second, long SLen,
}
}
-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 = IMAP;
+ citimap *Imap = CCCIMAP;
if (num_parms < 1)
return NULL;
StrBufPlain(Imap->Reply, CKEY(Params[1]));
StrBufUpCase(Imap->Reply);
- syslog(LOG_DEBUG, "---- Looking up [%s] -----",
- ChrPtr(Imap->Reply));
+ syslog(LOG_DEBUG, "---- Looking up [%s] -----", ChrPtr(Imap->Reply));
if (GetHash(ImapCmds, SKEY(Imap->Reply), &v))
{
syslog(LOG_DEBUG, "Found.");
return NULL;
}
- syslog(LOG_DEBUG, "---- Looking up [%s] -----",
- ChrPtr(Imap->Reply));
+ syslog(LOG_DEBUG, "---- Looking up [%s] -----", ChrPtr(Imap->Reply));
StrBufAppendBufPlain(Imap->Reply, CKEY(Params[2]), 0);
StrBufUpCase(Imap->Reply);
if (GetHash(ImapCmds, SKEY(Imap->Reply), &v))
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->flags = realloc(Imap->flags, (Imap->num_alloc * sizeof(unsigned int)) );
}
Imap->msgids[Imap->num_msgs - 1] = msgnum;
Imap->flags[Imap->num_msgs - 1] = 0;
*/
void imap_load_msgids(void)
{
+ struct CitContext *CCC = CC;
struct cdbdata *cdbfr;
- citimap *Imap = IMAP;
+ citimap *Imap = CCCIMAP;
if (Imap->selected == 0) {
syslog(LOG_ERR, "imap_load_msgids() can't run; no room selected");
/* 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->msgids = (long*)cdbfr->ptr;
Imap->num_msgs = cdbfr->len / sizeof(long);
Imap->num_alloc = cdbfr->len / sizeof(long);
+ cdbfr->ptr = NULL;
+ cdbfr->len = 0;
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->flags = malloc(Imap->num_alloc * sizeof(unsigned int));
+ memset(Imap->flags, 0, (Imap->num_alloc * sizeof(unsigned int)) );
}
imap_set_seen_flags(0);
*/
void imap_rescan_msgids(void)
{
- citimap *Imap = IMAP;
+ struct CitContext *CCC = CC;
+ citimap *Imap = CCCIMAP;
int original_num_msgs = 0;
long original_highest = 0L;
int i, j, jstart;
*/
cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
if (cdbfr != NULL) {
- msglist = malloc(cdbfr->len + 1);
- if (msglist == NULL) {
- syslog(LOG_CRIT, "IMAP: malloc() failed");
- CC->kill_me = KILLME_MALLOC_FAILED;
- return;
- }
- memcpy(msglist, cdbfr->ptr, (size_t)cdbfr->len);
+ msglist = (long*)cdbfr->ptr;
+ cdbfr->ptr = NULL;
num_msgs = cdbfr->len / sizeof(long);
+ cdbfr->len = 0;
cdb_free(cdbfr);
} else {
num_msgs = 0;
(Imap->num_msgs - i)));
memmove(&Imap->flags[i],
&Imap->flags[i + 1],
- (sizeof(long) *
+ (sizeof(unsigned int) *
(Imap->num_msgs - i)));
-
--i;
}
*/
void imap_cleanup_function(void)
{
- citimap *Imap = IMAP;
+ struct CitContext *CCC = CC;
+ citimap *Imap = CCCIMAP;
/* Don't do this stuff if this is not a Imap session! */
if (CC->h_command_function != imap_command_loop)
IAPuts("* OK [");
imap_output_capability_string();
- IAPrintf("] %s IMAP4rev1 %s ready\r\n", config.c_fqdn, CITADEL);
+ IAPrintf("] %s IMAP4rev1 %s ready\r\n", CtdlGetConfigStr("c_fqdn"), CITADEL);
IUnbuffer();
}
return;
}
case 4:
- if (CtdlLoginExistingUser(NULL, Params[2].Key) == login_ok) {
+ if (CtdlLoginExistingUser(Params[2].Key) == login_ok) {
if (CtdlTryPassword(Params[3].Key, Params[3].len) == pass_ok) {
/* hm, thats not doable by IReply :-( */
IAPrintf("%s OK [", Params[0].Key);
}
else
{
- IReplyPrintf("NO AUTHENTICATE %s failed",
- Params[3].Key);
+ IReplyPrintf("NO AUTHENTICATE %s failed", Params[3].Key);
+ return;
}
}
IReply("BAD Login incorrect");
+ return;
default:
IReply("BAD incorrect number of parameters");
return;
}
if (!strcasecmp(Params[2].Key, "LOGIN")) {
- CtdlEncodeBase64(UsrBuf, "Username:", 9, 0);
+ size_t len = CtdlEncodeBase64(UsrBuf, "Username:", 9, 0);
+ if (UsrBuf[len - 1] == '\n') {
+ UsrBuf[len - 1] = '\0';
+ }
+
IAPrintf("+ %s\r\n", UsrBuf);
IMAP->authstate = imap_as_expecting_username;
strcpy(IMAP->authseq, Params[0].Key);
}
if (!strcasecmp(Params[2].Key, "PLAIN")) {
- // CtdlEncodeBase64(UsrBuf, "Username:", 9, 0);
+ // size_t len = CtdlEncodeBase64(UsrBuf, "Username:", 9, 0);
+ // if (UsrBuf[len - 1] == '\n') {
+ // UsrBuf[len - 1] = '\0';
+ // }
// IAPuts("+ %s\r\n", UsrBuf);
IAPuts("+ \r\n");
IMAP->authstate = imap_as_expecting_plainauth;
{
citimap *Imap = IMAP;
const char *decoded_authstring;
- char ident[256];
- char user[256];
- char pass[256];
+ char ident[256] = "";
+ char user[256] = "";
+ char pass[256] = "";
int result;
- long len;
+ long decoded_len;
+ long len = 0;
+ long plen = 0;
memset(pass, 0, sizeof(pass));
- StrBufDecodeBase64(Imap->Cmd.CmdBuf);
+ decoded_len = StrBufDecodeBase64(Imap->Cmd.CmdBuf);
+
+ if (decoded_len > 0)
+ {
+ decoded_authstring = ChrPtr(Imap->Cmd.CmdBuf);
+
+ len = safestrncpy(ident, decoded_authstring, sizeof ident);
+
+ decoded_len -= len - 1;
+ decoded_authstring += len + 1;
- decoded_authstring = ChrPtr(Imap->Cmd.CmdBuf);
- safestrncpy(ident, decoded_authstring, sizeof ident);
- safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
- len = safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
- if (len < 0)
- len = sizeof(pass) - 1;
+ if (decoded_len > 0)
+ {
+ len = safestrncpy(user, decoded_authstring, sizeof user);
+ decoded_authstring += len + 1;
+ decoded_len -= len - 1;
+ }
+
+ if (decoded_len > 0)
+ {
+ plen = safestrncpy(pass, decoded_authstring, sizeof pass);
+
+ if (plen < 0)
+ plen = sizeof(pass) - 1;
+ }
+ }
Imap->authstate = imap_as_normal;
if (!IsEmptyStr(ident)) {
- result = CtdlLoginExistingUser(user, ident);
+ result = CtdlLoginExistingUser(ident);
}
else {
- result = CtdlLoginExistingUser(NULL, user);
+ result = CtdlLoginExistingUser(user);
}
if (result == login_ok) {
- if (CtdlTryPassword(pass, len) == pass_ok) {
+ if (CtdlTryPassword(pass, plen) == pass_ok) {
IAPrintf("%s OK authentication succeeded\r\n", Imap->authseq);
return;
}
switch (state){
case imap_as_expecting_username:
StrBufDecodeBase64(Imap->Cmd.CmdBuf);
- CtdlLoginExistingUser(NULL, ChrPtr(Imap->Cmd.CmdBuf));
- CtdlEncodeBase64(PWBuf, "Password:", 9, 0);
+ CtdlLoginExistingUser(ChrPtr(Imap->Cmd.CmdBuf));
+ size_t len = CtdlEncodeBase64(PWBuf, "Password:", 9, 0);
+ if (PWBuf[len - 1] == '\n') {
+ PWBuf[len - 1] = '\0';
+ }
+
IAPrintf("+ %s\r\n", PWBuf);
Imap->authstate = imap_as_expecting_password;
return;
case imap_as_expecting_multilineusername:
extract_token(PWBuf, ChrPtr(Imap->Cmd.CmdBuf), 1, ' ', sizeof(PWBuf));
- CtdlLoginExistingUser(NULL, ChrPtr(Imap->Cmd.CmdBuf));
+ CtdlLoginExistingUser(ChrPtr(Imap->Cmd.CmdBuf));
IAPuts("+ go ahead\r\n");
Imap->authstate = imap_as_expecting_multilinepassword;
return;
* the number of messages and number of new messages.
*/
memcpy(&CC->room, &QRscratch, sizeof(struct ctdlroom));
- CtdlUserGoto(NULL, 0, 0, &msgs, &new);
+ CtdlUserGoto(NULL, 0, 0, &msgs, &new, NULL, NULL);
Imap->selected = 1;
if (!strcasecmp(Params[1].Key, "EXAMINE")) {
IAPrintf("* %d RECENT\r\n", new);
IAPrintf("* OK [UIDVALIDITY %ld] UID validity status\r\n", GLOBAL_UIDVALIDITY_VALUE);
- IAPrintf("* OK [UIDNEXT %ld] Predicted next UID\r\n", CitControl.MMhighest + 1);
+ IAPrintf("* OK [UIDNEXT %ld] Predicted next UID\r\n", CtdlGetConfigLong("MMhighest") + 1);
/* Technically, \Deleted is a valid flag, but not a permanent flag,
* because we don't maintain its state across sessions. Citadel
*/
int imap_do_expunge(void)
{
- citimap *Imap = IMAP;
+ struct CitContext *CCC = CC;
+ citimap *Imap = CCCIMAP;
int i;
int num_expunged = 0;
long *delmsgs = NULL;
*/
void imap_namespace(int num_parms, ConstStr *Params)
{
+ long len;
int i;
struct floor *fl;
int floors = 0;
if (fl->f_flags & F_INUSE) {
/* if (floors > 0) IAPuts(" "); samjam says this confuses javamail */
IAPuts("(");
- snprintf(Namespace, sizeof(Namespace), "%s/", fl->f_name);
- plain_imap_strout(Namespace);
+ len = snprintf(Namespace, sizeof(Namespace), "%s/", fl->f_name);
+ IPutStr(Namespace, len);
IAPuts(" \"/\")");
++floors;
}
if (strchr(Params[2].Key, '\\') != NULL) {
IReply("NO Invalid character in folder name");
- syslog(LOG_DEBUG, "invalid character in folder name");
+ syslog(LOG_ERR, "invalid character in folder name");
return;
}
ret = imap_roomname(roomname, sizeof roomname, Params[2].Key);
if (ret < 0) {
IReply("NO Invalid mailbox name or location");
- syslog(LOG_DEBUG, "invalid mailbox name or location");
+ syslog(LOG_ERR, "invalid mailbox name or location");
return;
}
floornum = (ret & 0x00ff); /* lower 8 bits = floor number */
if (flags & IR_MAILBOX) {
if (strncasecmp(Params[2].Key, "INBOX/", 6)) {
IReply("NO Personal folders must be created under INBOX");
- syslog(LOG_DEBUG, "not subordinate to inbox");
+ syslog(LOG_ERR, "not subordinate to inbox");
return;
}
}
newroomview = VIEW_BBS;
}
- syslog(LOG_INFO, "IMAP: Create new room <%s> on floor <%d> with type <%d>",
- roomname, floornum, newroomtype);
+ syslog(LOG_INFO, "Create new room <%s> on floor <%d> with type <%d>",
+ roomname, floornum, newroomtype);
ret = CtdlCreateRoom(roomname, newroomtype, "", floornum, 1, 0, newroomview);
if (ret == 0) {
*/
void imap_status(int num_parms, ConstStr *Params)
{
+ long len;
int ret;
char roomname[ROOMNAMELEN];
char imaproomname[SIZ];
if (IMAP->selected) {
strcpy(savedroom, CC->room.QRname);
}
- CtdlUserGoto(roomname, 0, 0, &msgs, &new);
+ CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);
/*
* Tell the client what it wants to know. In fact, tell it *more* than
* names and simply spew all possible data items. It's far easier to
* code and probably saves us some processing time too.
*/
- imap_mailboxname(imaproomname, sizeof imaproomname, &CC->room);
+ len = imap_mailboxname(imaproomname, sizeof imaproomname, &CC->room);
IAPuts("* STATUS ");
- plain_imap_strout(imaproomname);
+ IPutStr(imaproomname, len);
IAPrintf(" (MESSAGES %d ", msgs);
IAPrintf("RECENT %d ", new); /* Initially, new==recent */
- IAPrintf("UIDNEXT %ld ", CitControl.MMhighest + 1);
+ IAPrintf("UIDNEXT %ld ", CtdlGetConfigLong("MMhighest") + 1);
IAPrintf("UNSEEN %d)\r\n", new);
/*
* our happy day without violent explosions.
*/
if (IMAP->selected) {
- CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
+ CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
}
/*
if (IMAP->selected) {
strcpy(savedroom, CC->room.QRname);
}
- CtdlUserGoto(roomname, 0, 0, &msgs, &new);
+ CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);
/*
* If another folder is selected, go back to that room so we can resume
* our happy day without violent explosions.
*/
if (IMAP->selected) {
- CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
+ CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
}
IReply("OK SUBSCRIBE completed");
if (IMAP->selected) {
strcpy(savedroom, CC->room.QRname);
}
- CtdlUserGoto(roomname, 0, 0, &msgs, &new);
+ CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);
/*
* Now make the API call to zap the room
* our happy day without violent explosions.
*/
if (IMAP->selected) {
- CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
+ CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
}
}
if (IMAP->selected) {
strcpy(savedroom, CC->room.QRname);
}
- CtdlUserGoto(roomname, 0, 0, &msgs, &new);
+ CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);
/*
* Now delete the room.
* our happy day without violent explosions.
*/
if (IMAP->selected) {
- CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
+ CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
}
}
irl->irl_newfloor);
if (r != crr_ok) {
/* FIXME handle error returns better */
- syslog(LOG_ERR, "IMAP: CtdlRenameRoom() error %d", r);
+ syslog(LOG_ERR, "CtdlRenameRoom() error %d", r);
}
irlp = irl;
irl = irl->next;
*/
void imap_command_loop(void)
{
+ struct CitContext *CCC = CC;
struct timeval tv1, tv2;
suseconds_t total_time = 0;
citimap *Imap;
const imap_handler_hook *h;
gettimeofday(&tv1, NULL);
- CC->lastcmd = time(NULL);
- Imap = IMAP;
+ CCC->lastcmd = time(NULL);
+ Imap = CCCIMAP;
flush_output();
if (Imap->Cmd.CmdBuf == NULL)
FlushStrBuf(Imap->Cmd.CmdBuf);
if (CtdlClientGetLine(Imap->Cmd.CmdBuf) < 1) {
- syslog(LOG_ERR, "IMAP: client disconnected: ending session.");
+ syslog(LOG_ERR, "client disconnected: ending session.");
CC->kill_me = KILLME_CLIENT_DISCONNECTED;
return;
}
if (Imap->authstate == imap_as_expecting_password) {
- syslog(LOG_INFO, "IMAP: <password>");
+ syslog(LOG_INFO, "<password>");
}
else if (Imap->authstate == imap_as_expecting_plainauth) {
- syslog(LOG_INFO, "IMAP: <plain_auth>");
+ syslog(LOG_INFO, "<plain_auth>");
}
else if ((Imap->authstate == imap_as_expecting_multilineusername) ||
cbmstrcasestr(ChrPtr(Imap->Cmd.CmdBuf), " LOGIN ")) {
- syslog(LOG_INFO, "IMAP: LOGIN...");
+ syslog(LOG_INFO, "LOGIN...");
}
else {
- syslog(LOG_INFO, "IMAP: %s", ChrPtr(Imap->Cmd.CmdBuf));
+ syslog(LOG_DEBUG, "%s", ChrPtr(Imap->Cmd.CmdBuf));
}
pchs = ChrPtr(Imap->Cmd.CmdBuf);
for (i=0; i < Imap->Cmd.num_parms; i++) {
if (Imap->Cmd.Params[i].len != strlen(Imap->Cmd.Params[i].Key))
syslog(LOG_DEBUG, "*********** %ld != %ld : %s",
- Imap->Cmd.Params[i].len,
- strlen(Imap->Cmd.Params[i].Key),
+ Imap->Cmd.Params[i].len,
+ strlen(Imap->Cmd.Params[i].Key),
Imap->Cmd.Params[i].Key);
else
syslog(LOG_DEBUG, "%ld : %s",
- Imap->Cmd.Params[i].len,
- Imap->Cmd.Params[i].Key);
+ Imap->Cmd.Params[i].len,
+ Imap->Cmd.Params[i].Key);
}}
#endif
gettimeofday(&tv2, NULL);
total_time = (tv2.tv_usec + (tv2.tv_sec * 1000000)) - (tv1.tv_usec + (tv1.tv_sec * 1000000));
syslog(LOG_DEBUG, "IMAP command completed in %ld.%ld seconds",
- (total_time / 1000000),
- (total_time % 1000000)
- );
+ (total_time / 1000000),
+ (total_time % 1000000)
+ );
}
void imap_noop (int num_parms, ConstStr *Params)
if (IMAP->selected) {
imap_do_expunge(); /* yes, we auto-expunge at logout */
}
- IAPrintf("* BYE %s logging out\r\n", config.c_fqdn);
+ IAPrintf("* BYE %s logging out\r\n", CtdlGetConfigStr("c_fqdn"));
IReply("OK Citadel IMAP session ended.");
CC->kill_me = KILLME_CLIENT_LOGGED_OUT;
return;
const char *CitadelServiceIMAP="IMAP";
const char *CitadelServiceIMAPS="IMAPS";
-
/*
* This function is called to register the IMAP extension with Citadel.
*/
if (!threading)
{
- CtdlRegisterServiceHook(config.c_imap_port,
+ CtdlRegisterServiceHook(CtdlGetConfigInt("c_imap_port"),
NULL, imap_greeting, imap_command_loop, NULL, CitadelServiceIMAP);
#ifdef HAVE_OPENSSL
- CtdlRegisterServiceHook(config.c_imaps_port,
+ CtdlRegisterServiceHook(CtdlGetConfigInt("c_imaps_port"),
NULL, imaps_greeting, imap_command_loop, NULL, CitadelServiceIMAPS);
#endif
- CtdlRegisterSessionHook(imap_cleanup_function, EVT_STOP);
- CtdlRegisterCleanupHook(imap_cleanup);
+ CtdlRegisterSessionHook(imap_cleanup_function, EVT_STOP, PRIO_STOP + 30);
}
/* return our module name for the log */