#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <syslog.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sasl/sasl.h>
#include "acl.h"
#include "annotate.h"
#include "append.h"
#include "auth.h"
#include "backend.h"
#include "charset.h"
#include "exitcodes.h"
#include "idle.h"
#include "global.h"
#include "imap_err.h"
#include "imapd.h"
#include "imapurl.h"
#include "imparse.h"
#include "iptostring.h"
#include "mailbox.h"
#include "message.h"
#include "mboxname.h"
#include "mboxlist.h"
#include "mbdump.h"
#include "mkgmtime.h"
#include "mupdate-client.h"
#include "quota.h"
#include "telemetry.h"
#include "tls.h"
#include "user.h"
#include "util.h"
#include "version.h"
#include "xmalloc.h"
#include "pushstats.h"
#include "AppleOD.h"
extern void seen_done(void);
extern int optind;
extern char *optarg;
const int config_need_data = CONFIG_NEED_PARTITION_DATA;
static char shutdownfilename[1024];
static int imaps = 0;
static sasl_ssf_t extprops_ssf = 0;
struct protstream *imapd_out = NULL;
struct protstream *imapd_in = NULL;
static char imapd_clienthost[NI_MAXHOST*2+1] = "[local]";
static int imapd_logfd = -1;
char *imapd_userid;
static char *imapd_magicplus = NULL;
struct auth_state *imapd_authstate = 0;
static int imapd_userisadmin = 0;
static int imapd_userisproxyadmin = 0;
static sasl_conn_t *imapd_saslconn;
static int imapd_starttls_done = 0;
#ifdef HAVE_SSL
static SSL *tls_conn = NULL;
#endif
struct appendstage {
struct stagemsg *stage;
char **flag;
int nflags, flagalloc;
time_t internaldate;
} **stage = NULL;
unsigned long numstage = 0;
static struct proxy_context imapd_proxyctx = {
1, 1, &imapd_authstate, &imapd_userisadmin, &imapd_userisproxyadmin
};
static struct mailbox mboxstruct;
static struct mailbox *imapd_mailbox;
int imapd_exists = -1;
static struct namespace imapd_namespace;
static const char *monthname[] = {
"jan", "feb", "mar", "apr", "may", "jun",
"jul", "aug", "sep", "oct", "nov", "dec"
};
static const int max_monthdays[] = {
31, 29, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31
};
void motd_file(int fd);
void shut_down(int code);
void fatal(const char *s, int code);
void cmdloop(void);
void cmd_login(char *tag, char *user);
void cmd_authenticate(char *tag, char *authtype, char *resp);
void cmd_noop(char *tag, char *cmd);
void cmd_capability(char *tag);
void cmd_append(char *tag, char *name);
void cmd_select(char *tag, char *cmd, char *name);
void cmd_close(char *tag);
void cmd_fetch(char *tag, char *sequence, int usinguid);
void cmd_partial(const char *tag, const char *msgno, char *data,
const char *start, const char *count);
void cmd_store(char *tag, char *sequence, char *operation, int usinguid);
void cmd_search(char *tag, int usinguid);
void cmd_sort(char *tag, int usinguid);
void cmd_thread(char *tag, int usinguid);
void cmd_copy(char *tag, char *sequence, char *name, int usinguid);
void cmd_expunge(char *tag, char *sequence);
void cmd_create(char *tag, char *name, char *partition, int localonly);
void cmd_delete(char *tag, char *name, int localonly);
void cmd_dump(char *tag, char *name, int uid_start);
void cmd_undump(char *tag, char *name);
void cmd_xfer(char *tag, char *name, char *toserver, char *topart);
void cmd_rename(const char *tag, char *oldname,
char *newname, char *partition);
void cmd_reconstruct(const char *tag, const char *name, int recursive);
void cmd_find(char *tag, char *namespace, char *pattern);
void cmd_list(char *tag, int subscribed, char *reference, char *pattern);
void cmd_changesub(char *tag, char *namespace, char *name, int add);
void cmd_getacl(const char *tag, const char *name);
void cmd_listrights(char *tag, char *name, char *identifier);
void cmd_myrights(const char *tag, const char *name);
void cmd_setacl(const char *tag, const char *name,
const char *identifier, const char *rights);
void cmd_getquota(const char *tag, const char *name);
void cmd_getquotaroot(const char *tag, const char *name);
void cmd_setquota(const char *tag, const char *quotaroot);
void cmd_status(char *tag, char *name);
void cmd_getuids(char *tag, char *startuid);
void cmd_unselect(char* tag);
void cmd_namespace(char* tag);
void cmd_mupdatepush(char *tag, char *name);
void cmd_id(char* tag);
extern void id_getcmdline(int argc, char **argv);
extern void id_response(struct protstream *pout);
void cmd_idle(char* tag);
void idle_update(idle_flags_t flags);
void cmd_starttls(char *tag, int imaps);
#ifdef ENABLE_X_NETSCAPE_HACK
void cmd_netscrape(char* tag);
#endif
void cmd_getannotation(char* tag, char *mboxpat);
void cmd_setannotation(char* tag, char *mboxpat);
int getannotatefetchdata(char *tag,
struct strlist **entries, struct strlist **attribs);
int getannotatestoredata(char *tag, struct entryattlist **entryatts);
void annotate_response(struct entryattlist *l);
#ifdef ENABLE_LISTEXT
int getlistopts(char *tag, int *listopts);
#endif
int getsearchprogram(char *tag, struct searchargs *searchargs,
int *charset, int parsecharset);
int getsearchcriteria(char *tag, struct searchargs *searchargs,
int *charset, int parsecharset);
int getsearchdate(time_t *start, time_t *end);
int getsortcriteria(char *tag, struct sortcrit **sortcrit);
int getdatetime(time_t *date);
void printstring(const char *s);
void printastring(const char *s);
void appendfieldlist(struct fieldlist **l, char *section,
struct strlist *fields, char *trail,
void *d, size_t size);
void freefieldlist(struct fieldlist *l);
void freestrlist(struct strlist *l);
void appendsearchargs(struct searchargs *s, struct searchargs *s1,
struct searchargs *s2);
void freesearchargs(struct searchargs *s);
static void freesortcrit(struct sortcrit *s);
static int mailboxdata(char *name, int matchlen, int maycreate, void *rock);
static int listdata(char *name, int matchlen, int maycreate, void *rock);
static void mstringdata(char *cmd, char *name, int matchlen, int maycreate,
int listopts);
extern void setproctitle_init(int argc, char **argv, char **envp);
extern int proc_register(const char *progname, const char *clienthost,
const char *userid, const char *mailbox);
extern void proc_cleanup(void);
extern int saslserver(sasl_conn_t *conn, const char *mech,
const char *init_resp, const char *resp_prefix,
const char *continuation, const char *empty_resp,
struct protstream *pin, struct protstream *pout,
int *sasl_result, char **success_data);
static int reset_saslconn(sasl_conn_t **conn);
static struct
{
char *ipremoteport;
char *iplocalport;
sasl_ssf_t ssf;
char *authid;
} saslprops = {NULL,NULL,0,NULL};
static int imapd_canon_user(sasl_conn_t *conn, void *context,
const char *user, unsigned ulen,
unsigned flags, const char *user_realm,
char *out, unsigned out_max, unsigned *out_ulen)
{
char userbuf[MAX_MAILBOX_NAME+1], *p;
size_t n;
int r;
if (!ulen) ulen = strlen(user);
if (config_getswitch(IMAPOPT_IMAPMAGICPLUS)) {
if (ulen > MAX_MAILBOX_NAME) {
sasl_seterror(conn, 0, "buffer overflow while canonicalizing");
return SASL_BUFOVER;
}
memcpy(userbuf, user, ulen);
userbuf[ulen] = '\0';
user = userbuf;
if ((p = strchr(userbuf, '+')) &&
(n = config_virtdomains ? strcspn(p, "@") : strlen(p)) == 1) {
if (flags & SASL_CU_AUTHZID) {
if (imapd_magicplus) free(imapd_magicplus);
imapd_magicplus = xstrndup(p, n);
}
memmove(p, p+n, strlen(p+n)+1);
ulen -= n;
}
}
r = mysasl_canon_user(conn, context, user, ulen, flags, user_realm,
out, out_max, out_ulen);
if (!r && imapd_magicplus && flags == SASL_CU_AUTHZID) {
n = strlen(imapd_magicplus);
if (*out_ulen + n > out_max) {
sasl_seterror(conn, 0, "buffer overflow while canonicalizing");
r = SASL_BUFOVER;
}
else {
p = (config_virtdomains && (p = strchr(out, '@'))) ?
p : out + *out_ulen;
memmove(p+n, p, strlen(p)+1);
memcpy(p, imapd_magicplus, n);
*out_ulen += n;
}
}
return r;
}
static int imapd_proxy_policy(sasl_conn_t *conn,
void *context,
const char *requested_user, unsigned rlen,
const char *auth_identity, unsigned alen,
const char *def_realm,
unsigned urlen,
struct propctx *propctx)
{
if (config_getswitch(IMAPOPT_IMAPMAGICPLUS)) {
char userbuf[MAX_MAILBOX_NAME+1], *p;
size_t n;
if (!rlen) rlen = strlen(requested_user);
if (rlen > MAX_MAILBOX_NAME) {
sasl_seterror(conn, 0, "buffer overflow while proxying");
return SASL_BUFOVER;
}
memcpy(userbuf, requested_user, rlen);
userbuf[rlen] = '\0';
requested_user = userbuf;
if ((p = strchr(userbuf, '+'))) {
n = config_virtdomains ? strcspn(p, "@") : strlen(p);
memmove(p, p+n, strlen(p+n)+1);
rlen -= n;
}
}
return mysasl_proxy_policy(conn, context, requested_user, rlen,
auth_identity, alen, def_realm, urlen, propctx);
}
static const struct sasl_callback mysasl_cb[] = {
{ SASL_CB_GETOPT, &mysasl_config, NULL },
{ SASL_CB_PROXY_POLICY, &imapd_proxy_policy, (void*) &imapd_proxyctx },
{ SASL_CB_CANON_USER, &imapd_canon_user, NULL },
{ SASL_CB_LIST_END, NULL, NULL }
};
static void imapd_refer(const char *tag,
const char *server,
const char *mailbox)
{
char url[MAX_MAILBOX_PATH+1];
if(!strcmp(imapd_userid, "anonymous")) {
imapurl_toURL(url, server, mailbox, "ANONYMOUS");
} else {
imapurl_toURL(url, server, mailbox, "*");
}
prot_printf(imapd_out, "%s NO [REFERRAL %s] Remote mailbox.\r\n",
tag, url);
}
static int mlookup(const char *tag, const char *ext_name,
const char *name, int *flags, char **pathp, char **partp,
char **aclp, struct txn **tid)
{
int r, mbtype;
char *remote, *acl;
r = mboxlist_detail(name, &mbtype, pathp, &remote, &acl, tid);
if(partp) *partp = remote;
if(aclp) *aclp = acl;
if(flags) *flags = mbtype;
if(r) return r;
if(mbtype & MBTYPE_RESERVE) return IMAP_MAILBOX_RESERVED;
if(mbtype & MBTYPE_MOVING) {
if(!imapd_userisadmin &&
(!acl || !(cyrus_acl_myrights(imapd_authstate,acl) & ACL_LOOKUP))) {
r = IMAP_MAILBOX_NONEXISTENT;
} else if(tag && ext_name && remote && *remote) {
char *c = NULL;
c = strchr(remote, '!');
if(c) *c = '\0';
imapd_refer(tag, remote, ext_name);
r = IMAP_MAILBOX_MOVED;
} else if(config_mupdate_server) {
r = IMAP_SERVER_UNAVAILABLE;
} else {
r = IMAP_MAILBOX_NOTSUPPORTED;
}
}
return r;
}
static void imapd_reset(void)
{
proc_cleanup();
if (imapd_mailbox) {
index_closemailbox(imapd_mailbox);
mailbox_close(imapd_mailbox);
imapd_mailbox = 0;
}
if (imapd_in) {
prot_NONBLOCK(imapd_in);
prot_fill(imapd_in);
prot_free(imapd_in);
}
if (imapd_out) {
prot_flush(imapd_out);
prot_free(imapd_out);
}
imapd_in = imapd_out = NULL;
#ifdef HAVE_SSL
if (tls_conn) {
if (tls_reset_servertls(&tls_conn) == -1) {
fatal("tls_reset() failed", EC_TEMPFAIL);
}
tls_conn = NULL;
}
#endif
cyrus_reset_stdio();
strcpy(imapd_clienthost, "[local]");
if (imapd_logfd != -1) {
close(imapd_logfd);
imapd_logfd = -1;
}
if (imapd_userid != NULL) {
free(imapd_userid);
imapd_userid = NULL;
}
if (imapd_magicplus != NULL) {
free(imapd_magicplus);
imapd_magicplus = NULL;
}
if (imapd_authstate) {
auth_freestate(imapd_authstate);
imapd_authstate = NULL;
}
imapd_userisadmin = 0;
imapd_userisproxyadmin = 0;
if (imapd_saslconn) {
sasl_dispose(&imapd_saslconn);
imapd_saslconn = NULL;
}
imapd_starttls_done = 0;
if(saslprops.iplocalport) {
free(saslprops.iplocalport);
saslprops.iplocalport = NULL;
}
if(saslprops.ipremoteport) {
free(saslprops.ipremoteport);
saslprops.ipremoteport = NULL;
}
if(saslprops.authid) {
free(saslprops.authid);
saslprops.authid = NULL;
}
saslprops.ssf = 0;
imapd_exists = -1;
}
int service_init(int argc, char **argv, char **envp)
{
int ret;
int opt;
if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
setproctitle_init(argc, argv, envp);
signals_set_shutdown(&shut_down);
signal(SIGPIPE, SIG_IGN);
global_sasl_init(1, 1, mysasl_cb);
ret = snprintf(shutdownfilename, sizeof(shutdownfilename),
"%s/msg/shutdown", config_dir);
if(ret < 0 || ret >= sizeof(shutdownfilename)) {
fatal("shutdownfilename buffer too small (configdirectory too long)",
EC_CONFIG);
}
mboxlist_init(0);
mboxlist_open(NULL);
mailbox_initialize();
quotadb_init(0);
quotadb_open(NULL);
idle_enabled();
snmp_connect();
snmp_set_str(SERVER_NAME_VERSION,CYRUS_VERSION);
while ((opt = getopt(argc, argv, "sp:")) != EOF) {
switch (opt) {
case 's':
imaps = 1;
if (!tls_enabled()) {
syslog(LOG_ERR, "imaps: required OpenSSL options not present");
fatal("imaps: required OpenSSL options not present",
EC_CONFIG);
}
break;
case 'p':
extprops_ssf = atoi(optarg);
break;
default:
break;
}
}
annotatemore_init(0, NULL, NULL);
annotatemore_open(NULL);
return 0;
}
#ifdef ID_SAVE_CMDLINE
int service_main(int argc, char **argv, char **envp __attribute__((unused)))
#else
int service_main(int argc __attribute__((unused)),
char **argv __attribute__((unused)),
char **envp __attribute__((unused)))
#endif
{
socklen_t salen;
int timeout;
sasl_security_properties_t *secprops = NULL;
struct sockaddr_storage imapd_localaddr, imapd_remoteaddr;
char localip[60], remoteip[60];
char hbuf[NI_MAXHOST];
int niflags;
int imapd_haveaddr = 0;
signals_poll();
#ifdef ID_SAVE_CMDLINE
id_getcmdline(argc, argv);
#endif
imapd_in = prot_new(0, 0);
imapd_out = prot_new(1, 1);
salen = sizeof(imapd_remoteaddr);
if (getpeername(0, (struct sockaddr *)&imapd_remoteaddr, &salen) == 0 &&
(imapd_remoteaddr.ss_family == AF_INET ||
imapd_remoteaddr.ss_family == AF_INET6)) {
if (getnameinfo((struct sockaddr *)&imapd_remoteaddr, salen,
hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD) == 0) {
strncpy(imapd_clienthost, hbuf, sizeof(hbuf));
strlcat(imapd_clienthost, " ", sizeof(imapd_clienthost));
imapd_clienthost[sizeof(imapd_clienthost)-30] = '\0';
} else {
imapd_clienthost[0] = '\0';
}
niflags = NI_NUMERICHOST;
#ifdef NI_WITHSCOPEID
if (((struct sockaddr *)&imapd_remoteaddr)->sa_family == AF_INET6)
niflags |= NI_WITHSCOPEID;
#endif
if (getnameinfo((struct sockaddr *)&imapd_remoteaddr, salen, hbuf,
sizeof(hbuf), NULL, 0, niflags) != 0)
strlcpy(hbuf, "unknown", sizeof(hbuf));
strlcat(imapd_clienthost, "[", sizeof(imapd_clienthost));
strlcat(imapd_clienthost, hbuf, sizeof(imapd_clienthost));
strlcat(imapd_clienthost, "]", sizeof(imapd_clienthost));
salen = sizeof(imapd_localaddr);
if (getsockname(0, (struct sockaddr *)&imapd_localaddr, &salen) == 0) {
if(iptostring((struct sockaddr *)&imapd_remoteaddr, salen,
remoteip, sizeof(remoteip)) == 0
&& iptostring((struct sockaddr *)&imapd_localaddr, salen,
localip, sizeof(localip)) == 0) {
imapd_haveaddr = 1;
}
}
}
if (sasl_server_new("imap", config_servername,
NULL, NULL, NULL, NULL, 0,
&imapd_saslconn) != SASL_OK) {
fatal("SASL failed initializing: sasl_server_new()", EC_TEMPFAIL);
}
secprops = mysasl_secprops(SASL_SEC_NOPLAINTEXT);
sasl_setprop(imapd_saslconn, SASL_SEC_PROPS, secprops);
sasl_setprop(imapd_saslconn, SASL_SSF_EXTERNAL, &extprops_ssf);
if (imapd_haveaddr) {
sasl_setprop(imapd_saslconn, SASL_IPREMOTEPORT, remoteip);
saslprops.ipremoteport = xstrdup(remoteip);
sasl_setprop(imapd_saslconn, SASL_IPLOCALPORT, localip);
saslprops.iplocalport = xstrdup(localip);
}
proc_register("imapd", imapd_clienthost, NULL, NULL);
timeout = config_getint(IMAPOPT_TIMEOUT);
if (timeout < 30) timeout = 30;
prot_settimeout(imapd_in, timeout*60);
prot_setflushonread(imapd_in, imapd_out);
if (imaps == 1) cmd_starttls(NULL, 1);
snmp_increment(TOTAL_CONNECTIONS, 1);
snmp_increment(ACTIVE_CONNECTIONS, 1);
char *skipCommand = getenv( "IMAP_LIMIT_REACHED" );
pid_t pid = getpid();
if (skipCommand == NULL)
cmdloop();
else
{
prot_printf( imapd_out, "* BYE %s Cyrus IMAP4 server connection limit reached\r\n", config_servername );
syslog(LOG_INFO, "Process %d is over the server connection limit. Signalling process to exit.",pid);
}
prot_flush(imapd_out);
snmp_increment(ACTIVE_CONNECTIONS, -1);
imapd_reset();
if (skipCommand != NULL)
{
kill( pid, SA_NOCLDSTOP);
syslog(LOG_INFO, "Process %d is Done with kill.",pid);
}
return 0;
}
void service_abort(int error)
{
shut_down(error);
}
void motd_file(fd)
int fd;
{
struct protstream *motd_in;
char buf[1024];
char *p;
motd_in = prot_new(fd, 0);
prot_fgets(buf, sizeof(buf), motd_in);
if ((p = strchr(buf, '\r'))!=NULL) *p = 0;
if ((p = strchr(buf, '\n'))!=NULL) *p = 0;
for(p = buf; *p == '['; p++);
prot_printf(imapd_out, "* OK [ALERT] %s\r\n", p);
}
void shut_down(int code) __attribute__((noreturn));
void shut_down(int code)
{
proc_cleanup();
if (imapd_mailbox) {
index_closemailbox(imapd_mailbox);
mailbox_close(imapd_mailbox);
}
seen_done();
mboxlist_close();
mboxlist_done();
quotadb_close();
quotadb_done();
annotatemore_close();
annotatemore_done();
if (imapd_in) {
prot_NONBLOCK(imapd_in);
prot_fill(imapd_in);
prot_free(imapd_in);
}
if (imapd_out) {
prot_flush(imapd_out);
prot_free(imapd_out);
snmp_increment(ACTIVE_CONNECTIONS, -1);
}
#ifdef HAVE_SSL
tls_shutdown_serverengine();
#endif
cyrus_done();
exit(code);
}
void fatal(const char *s, int code)
{
static int recurse_code = 0;
if (recurse_code) {
proc_cleanup();
snmp_increment(ACTIVE_CONNECTIONS, -1);
exit(recurse_code);
}
recurse_code = code;
if (imapd_out) {
prot_printf(imapd_out, "* BYE Fatal error: %s\r\n", s);
prot_flush(imapd_out);
}
if (stage) {
while (numstage) {
struct appendstage *curstage = stage[--numstage];
append_removestage(curstage->stage);
while (curstage->nflags--) {
free(curstage->flag[curstage->nflags]);
}
if (curstage->flag) free((char *) curstage->flag);
free(curstage);
}
free(stage);
}
syslog(LOG_ERR, "Fatal error: %s", s);
shut_down(code);
}
void cmdloop()
{
int fd;
char motdfilename[1024];
int c;
int ret;
int usinguid, havepartition, havenamespace, recursive;
static struct buf tag, cmd, arg1, arg2, arg3, arg4;
char *p, shut[1024];
const char *err;
prot_printf(imapd_out,
"* OK %s Cyrus IMAP4 %s server ready\r\n", config_servername,
CYRUS_VERSION);
ret = snprintf(motdfilename, sizeof(motdfilename), "%s/msg/motd",
config_dir);
if(ret < 0 || ret >= sizeof(motdfilename)) {
fatal("motdfilename buffer too small (configdirectory too long)",
EC_CONFIG);
}
if ((fd = open(motdfilename, O_RDONLY, 0)) != -1) {
motd_file(fd);
close(fd);
}
for (;;) {
if ( !imapd_userisadmin && imapd_userid
&& shutdown_file(shut, sizeof(shut))) {
for (p = shut; *p == '['; p++);
prot_printf(imapd_out, "* BYE [ALERT] %s\r\n", p);
shut_down(0);
}
signals_poll();
c = getword(imapd_in, &tag);
if (c == EOF) {
if ((err = prot_error(imapd_in))!=NULL
&& strcmp(err, PROT_EOF_STRING)) {
syslog(LOG_WARNING, "%s, closing connection", err);
prot_printf(imapd_out, "* BYE %s\r\n", err);
}
return;
}
if (c != ' ' || !imparse_isatom(tag.s) || (tag.s[0] == '*' && !tag.s[1])) {
prot_printf(imapd_out, "* BAD Invalid tag\r\n");
eatline(imapd_in, c);
continue;
}
c = getword(imapd_in, &cmd);
if (!cmd.s[0]) {
prot_printf(imapd_out, "%s BAD Null command\r\n", tag.s);
eatline(imapd_in, c);
continue;
}
if (islower((unsigned char) cmd.s[0]))
cmd.s[0] = toupper((unsigned char) cmd.s[0]);
for (p = &cmd.s[1]; *p; p++) {
if (isupper((unsigned char) *p)) *p = tolower((unsigned char) *p);
}
if (!imapd_userid && !strchr("ALNCIS", cmd.s[0])) goto nologin;
switch (cmd.s[0]) {
case 'A':
if (!strcmp(cmd.s, "Authenticate")) {
int haveinitresp = 0;
if (c != ' ') goto missingargs;
c = getword(imapd_in, &arg1);
if (!imparse_isatom(arg1.s)) {
prot_printf(imapd_out, "%s BAD Invalid authenticate mechanism\r\n", tag.s);
eatline(imapd_in, c);
continue;
}
if (c == ' ') {
haveinitresp = 1;
c = getword(imapd_in, &arg2);
if (c == EOF) goto missingargs;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
if (imapd_userid) {
prot_printf(imapd_out, "%s BAD Already authenticated\r\n", tag.s);
continue;
}
cmd_authenticate(tag.s, arg1.s, haveinitresp ? arg2.s : NULL);
snmp_increment(AUTHENTICATE_COUNT, 1);
}
else if (!imapd_userid) goto nologin;
else if (!strcmp(cmd.s, "Append")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c != ' ') goto missingargs;
cmd_append(tag.s, arg1.s);
snmp_increment(APPEND_COUNT, 1);
}
else goto badcmd;
break;
case 'B':
if (!strcmp(cmd.s, "Bboard")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_select(tag.s, cmd.s, arg1.s);
snmp_increment(BBOARD_COUNT, 1);
}
else goto badcmd;
break;
case 'C':
if (!strcmp(cmd.s, "Capability")) {
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_capability(tag.s);
snmp_increment(CAPABILITY_COUNT, 1);
}
else if (!imapd_userid) goto nologin;
else if (!strcmp(cmd.s, "Check")) {
if (!imapd_mailbox) goto nomailbox;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_noop(tag.s, cmd.s);
snmp_increment(CHECK_COUNT, 1);
}
else if (!strcmp(cmd.s, "Copy")) {
if (!imapd_mailbox) goto nomailbox;
usinguid = 0;
if (c != ' ') goto missingargs;
copy:
c = getword(imapd_in, &arg1);
if (c == '\r') goto missingargs;
if (c != ' ' || !imparse_issequence(arg1.s)) goto badsequence;
c = getastring(imapd_in, imapd_out, &arg2);
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_copy(tag.s, arg1.s, arg2.s, usinguid);
snmp_increment(COPY_COUNT, 1);
}
else if (!strcmp(cmd.s, "Create")) {
havepartition = 0;
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c == EOF) goto missingargs;
if (c == ' ') {
havepartition = 1;
c = getword(imapd_in, &arg2);
if (!imparse_isatom(arg2.s)) goto badpartition;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_create(tag.s, arg1.s, havepartition ? arg2.s : 0, 0);
snmp_increment(CREATE_COUNT, 1);
}
else if (!strcmp(cmd.s, "Close")) {
if (!imapd_mailbox) goto nomailbox;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_close(tag.s);
snmp_increment(CLOSE_COUNT, 1);
}
else goto badcmd;
break;
case 'D':
if (!strcmp(cmd.s, "Delete")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_delete(tag.s, arg1.s, 0);
snmp_increment(DELETE_COUNT, 1);
}
else if (!strcmp(cmd.s, "Deleteacl")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg2);
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_setacl(tag.s, arg1.s, arg2.s, NULL);
snmp_increment(DELETEACL_COUNT, 1);
}
else if (!strcmp(cmd.s, "Dump")) {
int uid_start = 0;
if(c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if(c == ' ') {
c = getastring(imapd_in, imapd_out, &arg2);
if(!imparse_isnumber(arg2.s)) goto extraargs;
uid_start = atoi(arg2.s);
}
if(c == '\r') c = prot_getc(imapd_in);
if(c != '\n') goto extraargs;
cmd_dump(tag.s, arg1.s, uid_start);
}
else goto badcmd;
break;
case 'E':
if (!strcmp(cmd.s, "Expunge")) {
if (!imapd_mailbox) goto nomailbox;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_expunge(tag.s, 0);
snmp_increment(EXPUNGE_COUNT, 1);
}
else if (!strcmp(cmd.s, "Examine")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_select(tag.s, cmd.s, arg1.s);
snmp_increment(EXAMINE_COUNT, 1);
}
else goto badcmd;
break;
case 'F':
if (!strcmp(cmd.s, "Fetch")) {
if (!imapd_mailbox) goto nomailbox;
usinguid = 0;
if (c != ' ') goto missingargs;
fetch:
c = getword(imapd_in, &arg1);
if (c == '\r') goto missingargs;
if (c != ' ' || !imparse_issequence(arg1.s)) goto badsequence;
cmd_fetch(tag.s, arg1.s, usinguid);
snmp_increment(FETCH_COUNT, 1);
}
else if (!strcmp(cmd.s, "Find")) {
c = getword(imapd_in, &arg1);
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg2);
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_find(tag.s, arg1.s, arg2.s);
snmp_increment(FIND_COUNT, 1);
}
else goto badcmd;
break;
case 'G':
if (!strcmp(cmd.s, "Getacl")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_getacl(tag.s, arg1.s);
snmp_increment(GETACL_COUNT, 1);
}
else if (!strcmp(cmd.s, "Getannotation")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c != ' ') goto missingargs;
cmd_getannotation(tag.s, arg1.s);
snmp_increment(GETANNOTATION_COUNT, 1);
}
else if (!strcmp(cmd.s, "Getquota")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_getquota(tag.s, arg1.s);
snmp_increment(GETQUOTA_COUNT, 1);
}
else if (!strcmp(cmd.s, "Getquotaroot")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_getquotaroot(tag.s, arg1.s);
snmp_increment(GETQUOTAROOT_COUNT, 1);
}
else goto badcmd;
break;
case 'I':
if (!strcmp(cmd.s, "Id")) {
if (c != ' ') goto missingargs;
cmd_id(tag.s);
snmp_increment(ID_COUNT, 1);
}
else if (!imapd_userid) goto nologin;
else if (!strcmp(cmd.s, "Idle")) {
if (!idle_enabled()) {
goto badcmd;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_idle(tag.s);
snmp_increment(IDLE_COUNT, 1);
}
else goto badcmd;
break;
case 'L':
if (!strcmp(cmd.s, "Login")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if(c != ' ') goto missingargs;
cmd_login(tag.s, arg1.s);
snmp_increment(LOGIN_COUNT, 1);
}
else if (!strcmp(cmd.s, "Logout")) {
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
snmp_increment(LOGOUT_COUNT, 1);
prot_printf(imapd_out, "* BYE %s\r\n",
error_message(IMAP_BYE_LOGOUT));
prot_printf(imapd_out, "%s OK %s\r\n", tag.s,
error_message(IMAP_OK_COMPLETED));
return;
}
else if (!imapd_userid) goto nologin;
else if (!strcmp(cmd.s, "List")) {
int listopts = LIST_CHILDREN;
#ifdef ENABLE_LISTEXT
c = prot_getc(imapd_in);
if (c == '(') {
c = getlistopts(tag.s, &listopts);
if (c == EOF) {
eatline(imapd_in, c);
continue;
}
}
else
prot_ungetc(c, imapd_in);
#endif
if (imapd_magicplus) listopts |= LIST_SUBSCRIBED;
c = getastring(imapd_in, imapd_out, &arg1);
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg2);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_list(tag.s, listopts, arg1.s, arg2.s);
snmp_increment(LIST_COUNT, 1);
}
else if (!strcmp(cmd.s, "Lsub")) {
c = getastring(imapd_in, imapd_out, &arg1);
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg2);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_list(tag.s, LIST_LSUB | LIST_CHILDREN, arg1.s, arg2.s);
snmp_increment(LSUB_COUNT, 1);
}
else if (!strcmp(cmd.s, "Listrights")) {
c = getastring(imapd_in, imapd_out, &arg1);
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg2);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_listrights(tag.s, arg1.s, arg2.s);
snmp_increment(LISTRIGHTS_COUNT, 1);
}
else if (!strcmp(cmd.s, "Localcreate")) {
havepartition = 0;
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c == EOF) goto missingargs;
if (c == ' ') {
havepartition = 1;
c = getword(imapd_in, &arg2);
if (!imparse_isatom(arg2.s)) goto badpartition;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_create(tag.s, arg1.s, havepartition ? arg2.s : NULL, 1);
}
else if (!strcmp(cmd.s, "Localdelete")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_delete(tag.s, arg1.s, 1);
}
else goto badcmd;
break;
case 'M':
if (!strcmp(cmd.s, "Myrights")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_myrights(tag.s, arg1.s);
}
else if (!strcmp(cmd.s, "Mupdatepush")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if(c == EOF) goto missingargs;
if(c == '\r') c = prot_getc(imapd_in);
if(c != '\n') goto extraargs;
cmd_mupdatepush(tag.s, arg1.s);
} else goto badcmd;
break;
case 'N':
if (!strcmp(cmd.s, "Noop")) {
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_noop(tag.s, cmd.s);
}
#ifdef ENABLE_X_NETSCAPE_HACK
else if (!strcmp(cmd.s, "Netscape")) {
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_netscrape(tag.s);
}
#endif
else if (!imapd_userid) goto nologin;
else if (!strcmp(cmd.s, "Namespace")) {
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_namespace(tag.s);
}
else goto badcmd;
break;
case 'P':
if (!strcmp(cmd.s, "Partial")) {
if (!imapd_mailbox) goto nomailbox;
if (c != ' ') goto missingargs;
c = getword(imapd_in, &arg1);
if (c != ' ') goto missingargs;
c = getword(imapd_in, &arg2);
if (c != ' ') goto missingargs;
c = getword(imapd_in, &arg3);
if (c != ' ') goto missingargs;
c = getword(imapd_in, &arg4);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_partial(tag.s, arg1.s, arg2.s, arg3.s, arg4.s);
}
else goto badcmd;
break;
case 'R':
if (!strcmp(cmd.s, "Rename")) {
havepartition = 0;
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg2);
if (c == EOF) goto missingargs;
if (c == ' ') {
havepartition = 1;
c = getword(imapd_in, &arg3);
if (!imparse_isatom(arg3.s)) goto badpartition;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_rename(tag.s, arg1.s, arg2.s, havepartition ? arg3.s : 0);
} else if(!strcmp(cmd.s, "Reconstruct")) {
recursive = 0;
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if(c == ' ') {
c = getword(imapd_in, &arg2);
if(!imparse_isatom(arg2.s))
goto extraargs;
else if(!strcasecmp(arg2.s, "RECURSIVE"))
recursive = 1;
else
goto extraargs;
}
if(c == '\r') c = prot_getc(imapd_in);
if(c != '\n') goto extraargs;
cmd_reconstruct(tag.s, arg1.s, recursive);
}
else if (!strcmp(cmd.s, "Rlist")) {
c = getastring(imapd_in, imapd_out, &arg1);
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg2);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_list(tag.s, LIST_CHILDREN | LIST_REMOTE, arg1.s, arg2.s);
}
else if (!strcmp(cmd.s, "Rlsub")) {
c = getastring(imapd_in, imapd_out, &arg1);
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg2);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_list(tag.s, LIST_LSUB | LIST_CHILDREN | LIST_REMOTE,
arg1.s, arg2.s);
} else goto badcmd;
break;
case 'S':
if (!strcmp(cmd.s, "Starttls")) {
if (!tls_enabled()) {
goto badcmd;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
if (imapd_userid != NULL) {
prot_printf(imapd_out,
"%s BAD Can't Starttls after authentication\r\n", tag.s);
continue;
}
if (imapd_starttls_done == 1) {
prot_printf(imapd_out,
"%s BAD Already did a successful Starttls\r\n",
tag.s);
continue;
}
cmd_starttls(tag.s, 0);
snmp_increment(STARTTLS_COUNT, 1);
continue;
}
if (!imapd_userid) {
goto nologin;
} else if (!strcmp(cmd.s, "Store")) {
if (!imapd_mailbox) goto nomailbox;
usinguid = 0;
if (c != ' ') goto missingargs;
store:
c = getword(imapd_in, &arg1);
if (c != ' ' || !imparse_issequence(arg1.s)) goto badsequence;
c = getword(imapd_in, &arg2);
if (c != ' ') goto missingargs;
cmd_store(tag.s, arg1.s, arg2.s, usinguid);
snmp_increment(STORE_COUNT, 1);
}
else if (!strcmp(cmd.s, "Select")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_select(tag.s, cmd.s, arg1.s);
snmp_increment(SELECT_COUNT, 1);
}
else if (!strcmp(cmd.s, "Search")) {
if (!imapd_mailbox) goto nomailbox;
usinguid = 0;
if (c != ' ') goto missingargs;
search:
cmd_search(tag.s, usinguid);
snmp_increment(SEARCH_COUNT, 1);
}
else if (!strcmp(cmd.s, "Subscribe")) {
if (c != ' ') goto missingargs;
havenamespace = 0;
c = getastring(imapd_in, imapd_out, &arg1);
if (c == ' ') {
havenamespace = 1;
c = getastring(imapd_in, imapd_out, &arg2);
}
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
if (havenamespace) {
cmd_changesub(tag.s, arg1.s, arg2.s, 1);
}
else {
cmd_changesub(tag.s, (char *)0, arg1.s, 1);
}
snmp_increment(SUBSCRIBE_COUNT, 1);
}
else if (!strcmp(cmd.s, "Setacl")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg2);
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg3);
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_setacl(tag.s, arg1.s, arg2.s, arg3.s);
snmp_increment(SETACL_COUNT, 1);
}
else if (!strcmp(cmd.s, "Setannotation")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c != ' ') goto missingargs;
cmd_setannotation(tag.s, arg1.s);
snmp_increment(SETANNOTATION_COUNT, 1);
}
else if (!strcmp(cmd.s, "Setquota")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c != ' ') goto missingargs;
cmd_setquota(tag.s, arg1.s);
snmp_increment(SETQUOTA_COUNT, 1);
}
else if (!strcmp(cmd.s, "Sort")) {
if (!imapd_mailbox) goto nomailbox;
usinguid = 0;
if (c != ' ') goto missingargs;
sort:
cmd_sort(tag.s, usinguid);
snmp_increment(SORT_COUNT, 1);
}
else if (!strcmp(cmd.s, "Status")) {
if (c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if (c != ' ') goto missingargs;
cmd_status(tag.s, arg1.s);
snmp_increment(STATUS_COUNT, 1);
}
else goto badcmd;
break;
case 'T':
if (!strcmp(cmd.s, "Thread")) {
if (!imapd_mailbox) goto nomailbox;
usinguid = 0;
if (c != ' ') goto missingargs;
thread:
cmd_thread(tag.s, usinguid);
snmp_increment(THREAD_COUNT, 1);
}
else goto badcmd;
break;
case 'U':
if (!strcmp(cmd.s, "Uid")) {
if (!imapd_mailbox) goto nomailbox;
usinguid = 1;
if (c != ' ') goto missingargs;
c = getword(imapd_in, &arg1);
if (c != ' ') goto missingargs;
lcase(arg1.s);
if (!strcmp(arg1.s, "fetch")) {
goto fetch;
}
else if (!strcmp(arg1.s, "store")) {
goto store;
}
else if (!strcmp(arg1.s, "search")) {
goto search;
}
else if (!strcmp(arg1.s, "sort")) {
goto sort;
}
else if (!strcmp(arg1.s, "thread")) {
goto thread;
}
else if (!strcmp(arg1.s, "copy")) {
goto copy;
}
else if (!strcmp(arg1.s, "expunge")) {
c = getword(imapd_in, &arg1);
if (!imparse_issequence(arg1.s)) goto badsequence;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_expunge(tag.s, arg1.s);
snmp_increment(EXPUNGE_COUNT, 1);
}
else {
prot_printf(imapd_out, "%s BAD Unrecognized UID subcommand\r\n", tag.s);
eatline(imapd_in, c);
}
}
else if (!strcmp(cmd.s, "Unsubscribe")) {
if (c != ' ') goto missingargs;
havenamespace = 0;
c = getastring(imapd_in, imapd_out, &arg1);
if (c == ' ') {
havenamespace = 1;
c = getastring(imapd_in, imapd_out, &arg2);
}
if (c == EOF) goto missingargs;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
if (havenamespace) {
cmd_changesub(tag.s, arg1.s, arg2.s, 0);
}
else {
cmd_changesub(tag.s, (char *)0, arg1.s, 0);
}
snmp_increment(UNSUBSCRIBE_COUNT, 1);
}
else if (!strcmp(cmd.s, "Unselect")) {
if (!imapd_mailbox) goto nomailbox;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_unselect(tag.s);
snmp_increment(UNSELECT_COUNT, 1);
}
else if (!strcmp(cmd.s, "Undump")) {
if(c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if(c != ' ') goto missingargs;
cmd_undump(tag.s, arg1.s);
}
else goto badcmd;
break;
case 'X':
if (!strcmp(cmd.s, "Xfer")) {
int havepartition = 0;
if(c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg1);
if(c != ' ') goto missingargs;
c = getastring(imapd_in, imapd_out, &arg2);
if(c == ' ') {
c = getastring(imapd_in, imapd_out, &arg3);
if (!imparse_isatom(arg3.s)) goto badpartition;
havepartition = 1;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_xfer(tag.s, arg1.s, arg2.s,
(havepartition ? arg3.s : NULL));
}
else goto badcmd;
break;
default:
badcmd:
prot_printf(imapd_out, "%s BAD Unrecognized command\r\n", tag.s);
eatline(imapd_in, c);
}
continue;
nologin:
prot_printf(imapd_out, "%s BAD Please login first\r\n", tag.s);
eatline(imapd_in, c);
continue;
nomailbox:
prot_printf(imapd_out, "%s BAD Please select a mailbox first\r\n", tag.s);
eatline(imapd_in, c);
continue;
missingargs:
prot_printf(imapd_out, "%s BAD Missing required argument to %s\r\n", tag.s, cmd.s);
eatline(imapd_in, c);
continue;
extraargs:
prot_printf(imapd_out, "%s BAD Unexpected extra arguments to %s\r\n", tag.s, cmd.s);
eatline(imapd_in, c);
continue;
badsequence:
prot_printf(imapd_out, "%s BAD Invalid sequence in %s\r\n", tag.s, cmd.s);
eatline(imapd_in, c);
continue;
badpartition:
prot_printf(imapd_out, "%s BAD Invalid partition name in %s\r\n",
tag.s, cmd.s);
eatline(imapd_in, c);
continue;
}
}
void cmd_login(char *tag, char *user)
{
char userbuf[MAX_MAILBOX_NAME+1];
unsigned userlen;
const char *canon_user = userbuf;
char c;
struct buf passwdbuf;
char *passwd;
const char *reply = NULL;
int plaintextloginpause;
int r;
char mailboxname[ MAX_MAILBOX_NAME + 1 ] = "\0";
struct od_user_opts useropts;
if (imapd_userid) {
eatline(imapd_in, ' ');
prot_printf(imapd_out, "%s BAD Already logged in\r\n", tag);
return;
}
if ( config_getswitch( IMAPOPT_IMAP_AUTH_CLEAR ) == 0 )
{
eatline(imapd_in, ' ');
prot_printf( imapd_out, "%s NO Auth method not enabled\r\n", tag );
return;
}
odGetUserOpts( user, &useropts );
if ( useropts.fRecName[ 0 ] == '\0' )
{
strlcpy( useropts.fRecName, user, sizeof(useropts.fRecName) );
}
r = imapd_canon_user(imapd_saslconn, NULL, useropts.fRecName, 0,
SASL_CU_AUTHID | SASL_CU_AUTHZID, NULL,
userbuf, sizeof(userbuf), &userlen);
if (r) {
syslog(LOG_NOTICE, "badlogin: %s plaintext %s invalid user",
imapd_clienthost, beautify_string(user));
prot_printf(imapd_out, "%s NO %s\r\n", tag,
error_message(IMAP_INVALID_USER));
return;
}
if ((imapd_starttls_done == 0) &&
(config_getswitch(IMAPOPT_ALLOWPLAINTEXT) == 0) &&
!is_userid_anonymous(canon_user)) {
eatline(imapd_in, ' ');
prot_printf(imapd_out, "%s NO Login only available under a layer\r\n",
tag);
return;
}
memset(&passwdbuf,0,sizeof(struct buf));
c = getastring(imapd_in, imapd_out, &passwdbuf);
if(c == '\r') c = prot_getc(imapd_in);
if (c != '\n') {
freebuf(&passwdbuf);
prot_printf(imapd_out,
"%s BAD Unexpected extra arguments to LOGIN\r\n",
tag);
eatline(imapd_in, c);
return;
}
passwd = passwdbuf.s;
if ( !is_userid_anonymous( user ) )
{
if ( useropts.fRecName[ 0 ] == '\0' )
{
syslog( LOG_NOTICE, "badlogin from: %s plaintext user: %s. unknown user",
imapd_clienthost, beautify_string( user ) );
prot_printf( imapd_out, "%s NO unknown user or bad password\r\n", tag);
freebuf( &passwdbuf );
return;
}
if ( useropts.fAcctState != eAcctEnabled )
{
if ( useropts.fAcctState == eAcctNotMember )
{
syslog( LOG_NOTICE, "badlogin from: %s. plaintext user: %s. service ACL is not enabled for this user",
imapd_clienthost, beautify_string( user ) );
prot_printf( imapd_out, "%s NO Mail service ACL is not enabled for this user\r\n", tag );
}
else if ( useropts.fAcctState == eAcctForwarded )
{
syslog( LOG_NOTICE, "badlogin from: %s. plaintext user: %s. auto-forwarding is enabled for this user",
imapd_clienthost, beautify_string( user ) );
prot_printf( imapd_out, "%s NO Auto-forwarding is enabled for this user\r\n", tag);
}
else
{
syslog( LOG_NOTICE, "badlogin from: %s. plaintext user: %s. mail is not enabled for this user",
imapd_clienthost, beautify_string( user ) );
prot_printf( imapd_out, "%s NO Mail is not enabled for this user\r\n", tag);
}
freebuf( &passwdbuf );
return;
}
if ( useropts.fIMAPLogin != eAcctProtocolEnabled )
{
syslog( LOG_NOTICE, "badlogin from: %s. plaintext user: %s. IMAP access is not enabled for this user",
imapd_clienthost, beautify_string( user ) );
prot_printf( imapd_out, "%s NO IMAP access is not enabled for this user\r\n", tag );
freebuf( &passwdbuf );
return;
}
}
if (is_userid_anonymous(canon_user)) {
if (config_getswitch(IMAPOPT_ALLOWANONYMOUSLOGIN)) {
passwd = beautify_string(passwd);
if (strlen(passwd) > 500) passwd[500] = '\0';
syslog(LOG_NOTICE, "login: %s anonymous %s",
imapd_clienthost, passwd);
reply = "Anonymous access granted";
imapd_userid = xstrdup("anonymous");
}
else {
syslog(LOG_NOTICE, "badlogin: %s anonymous login refused",
imapd_clienthost);
prot_printf(imapd_out, "%s NO %s\r\n", tag,
error_message(IMAP_ANONYMOUS_NOT_PERMITTED));
freebuf(&passwdbuf);
return;
}
}
else if ( (config_getswitch(IMAPOPT_APPLE_AUTH) == 0) &&
((r = sasl_checkpass(imapd_saslconn,
canon_user,
strlen(canon_user),
passwd,
strlen(passwd))) != SASL_OK) ) {
syslog(LOG_NOTICE, "badlogin: %s plaintext %s %s",
imapd_clienthost, canon_user, sasl_errdetail(imapd_saslconn));
sleep(3);
if ((reply = sasl_errstring(r, NULL, NULL)) != NULL) {
prot_printf(imapd_out, "%s NO Login failed: %s\r\n", tag, reply);
} else {
prot_printf(imapd_out, "%s NO Login failed: %d\r\n", tag, r);
}
snmp_increment_args(AUTHENTICATION_NO, 1,
VARIABLE_AUTH, 0 ,
VARIABLE_LISTEND);
freebuf(&passwdbuf);
return;
}
else if ( config_getswitch( IMAPOPT_APPLE_AUTH ) )
{
const char *canon_user = auth_canonifyid( useropts.fRecName, 0 );
if ( r = odCheckPass( canon_user, passwd ) )
{
syslog( LOG_NOTICE, "badlogin from: %s. login user: %s (error = %d)",
imapd_clienthost, canon_user, r );
sleep( 3 );
prot_printf( imapd_out, "%s NO Login failed.\r\n", tag );
snmp_increment_args( AUTHENTICATION_NO, 1,
VARIABLE_AUTH, 0,
VARIABLE_LISTEND );
freebuf(&passwdbuf);
return;
}
else
{
reply = "User logged in";
imapd_userid = xstrdup(canon_user);
snmp_increment_args(AUTHENTICATION_YES, 1,
VARIABLE_AUTH, 0 ,
VARIABLE_LISTEND);
syslog(LOG_NOTICE, "login: %s %s plaintext%s %s", imapd_clienthost,
canon_user, imapd_starttls_done ? "+TLS" : "",
reply ? reply : "");
plaintextloginpause = config_getint(IMAPOPT_PLAINTEXTLOGINPAUSE);
if (plaintextloginpause != 0 && !imapd_starttls_done)
{
sleep(plaintextloginpause);
}
}
}
else {
r = sasl_getprop(imapd_saslconn, SASL_USERNAME,
(const void **) &canon_user);
if(r != SASL_OK) {
if ((reply = sasl_errstring(r, NULL, NULL)) != NULL) {
prot_printf(imapd_out, "%s NO Login failed: %s\r\n",
tag, reply);
} else {
prot_printf(imapd_out, "%s NO Login failed: %d\r\n", tag, r);
}
snmp_increment_args(AUTHENTICATION_NO, 1,
VARIABLE_AUTH, 0 ,
VARIABLE_LISTEND);
freebuf(&passwdbuf);
return;
}
reply = "User logged in";
imapd_userid = xstrdup(canon_user);
snmp_increment_args(AUTHENTICATION_YES, 1,
VARIABLE_AUTH, 0 ,
VARIABLE_LISTEND);
syslog(LOG_NOTICE, "login: %s %s%s plaintext%s %s", imapd_clienthost,
imapd_userid, imapd_magicplus ? imapd_magicplus : "",
imapd_starttls_done ? "+TLS" : "",
reply ? reply : "");
plaintextloginpause = config_getint(IMAPOPT_PLAINTEXTLOGINPAUSE);
if (plaintextloginpause != 0 && !imapd_starttls_done) {
sleep(plaintextloginpause);
}
}
imapd_authstate = auth_newstate(imapd_userid);
imapd_userisadmin = global_authisa(imapd_authstate, IMAPOPT_ADMINS);
prot_printf(imapd_out, "%s OK %s\r\n", tag, reply);
imapd_logfd = telemetry_log(imapd_userid, imapd_in, imapd_out, 0);
if ((r = mboxname_init_namespace(&imapd_namespace,
imapd_userisadmin || imapd_userisproxyadmin)) != 0) {
syslog(LOG_ERR, error_message(r));
fatal(error_message(r), EC_CONFIG);
}
mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid,
config_virtdomains ?
strcspn(imapd_userid, "@") : 0);
if ( !imapd_userisadmin )
{
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, "INBOX", imapd_userid, mailboxname);
if ( !r )
{
char *partition = NULL;
if ( useropts.fAltDataLoc[ 0 ] != '\0' )
{
partition = useropts.fAltDataLoc;
}
r = mboxlist_createmailbox( mailboxname, MAILBOX_FORMAT_NORMAL, partition, 1, imapd_userid, imapd_authstate, 0, 0, 0 );
if ( r && (r !=IMAP_MAILBOX_EXISTS) )
{
syslog( LOG_DEBUG, "mboxlist_createmailbox error(%d) for mailbox(%s)", r, mailboxname);
}
}
if ( r == IMAP_MAILBOX_EXISTS )
{
if ( useropts.fDiskQuota == 0 )
{
mboxlist_setquota( mailboxname, INT_MAX, 0 );
syslog( LOG_DEBUG, "quota set to \"unlimited\" for mailbox %s ", mailboxname);
}
else
{
mboxlist_setquota( mailboxname, useropts.fDiskQuota * 1024, 0 );
syslog( LOG_DEBUG, "quota set to %lu for mailbox %s ", useropts.fDiskQuota * 1024, mailboxname);
}
}
}
proc_register("imapd", imapd_clienthost, imapd_userid, (char *)0);
freebuf(&passwdbuf);
return;
}
void
cmd_authenticate(char *tag, char *authtype, char *resp)
{
int sasl_result;
const int *ssfp;
char *ssfmsg=NULL;
struct od_user_opts useropts;
const char *auth_user;
const char *canon_user;
int r;
char mailboxname[ MAX_MAILBOX_NAME + 1 ] = "\0";
if ( config_getswitch( IMAPOPT_APPLE_AUTH ) == 0 )
{
r = saslserver(imapd_saslconn, authtype, resp, "", "+ ", "",
imapd_in, imapd_out, &sasl_result, NULL);
if (r) {
const char *errorstring = NULL;
switch (r) {
case IMAP_SASL_CANCEL:
prot_printf(imapd_out,
"%s BAD Client canceled authentication\r\n", tag);
break;
case IMAP_SASL_PROTERR:
errorstring = prot_error(imapd_in);
prot_printf(imapd_out,
"%s NO Error reading client response: %s\r\n",
tag, errorstring ? errorstring : "");
break;
default:
errorstring = sasl_errstring(sasl_result, NULL, NULL);
syslog(LOG_NOTICE, "badlogin: %s %s [%s]",
imapd_clienthost, authtype, sasl_errdetail(imapd_saslconn));
snmp_increment_args(AUTHENTICATION_NO, 1,
VARIABLE_AUTH, 0,
VARIABLE_LISTEND);
sleep(3);
if (errorstring) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, errorstring);
} else {
prot_printf(imapd_out, "%s NO Error authenticating\r\n", tag);
}
}
reset_saslconn(&imapd_saslconn);
return;
}
sasl_result = sasl_getprop(imapd_saslconn, SASL_USERNAME,
(const void **) &canon_user);
if (sasl_result != SASL_OK) {
prot_printf(imapd_out, "%s NO weird SASL error %d SASL_USERNAME\r\n",
tag, sasl_result);
syslog(LOG_ERR, "weird SASL error %d getting SASL_USERNAME",
sasl_result);
reset_saslconn(&imapd_saslconn);
return;
}
}
else
{
if ( (strcasecmp( authtype, "gssapi" ) == 0) &&
(config_getswitch( IMAPOPT_IMAP_AUTH_GSSAPI ) == 0 ) )
{
r = 1;
}
else if ( (strcasecmp( authtype, "cram-md5" ) == 0) &&
(config_getswitch( IMAPOPT_IMAP_AUTH_CRAM_MD5 ) == 0) )
{
r = 1;
}
else if ( (strcasecmp( authtype, "plain" ) == 0) &&
(config_getswitch( IMAPOPT_IMAP_AUTH_PLAIN ) == 0) )
{
r = 1;
}
else if ( (strcasecmp( authtype, "login" ) == 0) &&
(config_getswitch( IMAPOPT_IMAP_AUTH_LOGIN ) == 0) )
{
r = 1;
}
if ( r == 1 )
{
prot_printf( imapd_out, "%s NO Auth method (%s) not enabled\r\n", tag, authtype );
return;
}
r = odDoAuthenticate( authtype, NULL, "+ ", kXMLIMAP_Principal, imapd_in, imapd_out, (char **)&auth_user );
if ( r )
{
switch ( r )
{
case ODA_AUTH_CANCEL:
prot_printf(imapd_out, "%s NO Client canceled authentication\r\n", tag );
break;
case ODA_PROTOCOL_ERROR:
prot_printf( imapd_out, "%s NO Error reading client response\r\n", tag );
break;
default:
syslog( LOG_NOTICE, "badlogin: %s %s", imapd_clienthost, authtype );
snmp_increment_args( AUTHENTICATION_NO, 1, VARIABLE_AUTH, 0, VARIABLE_LISTEND );
prot_printf( imapd_out, "%s NO Error authenticating\r\n", tag );
sleep( 3 );
}
return;
}
odGetUserOpts( auth_user, &useropts );
if ( useropts.fRecName[ 0 ] == '\0' )
{
syslog( LOG_NOTICE, "badlogin from: %s plaintext user: %s. unknown user",
imapd_clienthost, beautify_string( auth_user ) );
prot_printf( imapd_out, "%s NO unknown user or bad password\r\n", tag);
return;
}
if ( useropts.fAcctState != eAcctEnabled )
{
if ( useropts.fAcctState == eAcctNotMember )
{
syslog( LOG_NOTICE, "badlogin from: %s. plaintext user: %s. service ACL is not enabled for this user",
imapd_clienthost, beautify_string( auth_user ) );
prot_printf( imapd_out, "%s NO Mail service ACL is not enabled for this user\r\n", tag );
}
else if ( useropts.fAcctState == eAcctForwarded )
{
syslog( LOG_NOTICE, "badlogin from: %s. plaintext user: %s. auto-forwarding is enabled for this user",
imapd_clienthost, beautify_string( auth_user ) );
prot_printf( imapd_out, "%s NO Auto-forwarding is enabled for this user\r\n", tag);
}
else
{
syslog( LOG_NOTICE, "badlogin from: %s. plaintext user: %s. mail is not enabled for this user",
imapd_clienthost, beautify_string( auth_user ) );
prot_printf( imapd_out, "%s NO Mail is not enabled for this user\r\n", tag);
}
return;
}
if ( useropts.fIMAPLogin != eAcctProtocolEnabled )
{
syslog( LOG_NOTICE, "badlogin from: %s. plaintext user: %s. IMAP access is not enabled for this user",
imapd_clienthost, beautify_string( auth_user ) );
prot_printf( imapd_out, "%s NO IMAP access is not enabled for this user\r\n", tag );
return;
}
canon_user = auth_canonifyid( useropts.fRecName, 0 );
}
if (config_getswitch(IMAPOPT_IMAPMAGICPLUS) && strchr(canon_user, '+')) {
char userbuf[MAX_MAILBOX_NAME+1];
unsigned userlen;
sasl_result = imapd_canon_user(imapd_saslconn, NULL, canon_user, 0,
SASL_CU_AUTHID | SASL_CU_AUTHZID,
NULL, userbuf, sizeof(userbuf), &userlen);
if (sasl_result != SASL_OK) {
prot_printf(imapd_out,
"%s NO SASL canonification error %d\r\n",
tag, sasl_result);
reset_saslconn(&imapd_saslconn);
return;
}
imapd_userid = xstrdup(userbuf);
} else {
imapd_userid = xstrdup(canon_user);
}
proc_register("imapd", imapd_clienthost, imapd_userid, (char *)0);
syslog(LOG_NOTICE, "login: %s %s%s %s%s %s", imapd_clienthost,
imapd_userid, imapd_magicplus ? imapd_magicplus : "",
authtype, imapd_starttls_done ? "+TLS" : "", "User logged in");
sasl_getprop(imapd_saslconn, SASL_SSF, (const void **) &ssfp);
if (imapd_starttls_done) {
switch(*ssfp) {
case 0: ssfmsg = "tls protection"; break;
case 1: ssfmsg = "tls plus integrity protection"; break;
default: ssfmsg = "tls plus privacy protection"; break;
}
} else {
switch(*ssfp) {
case 0: ssfmsg = "no protection"; break;
case 1: ssfmsg = "integrity protection"; break;
default: ssfmsg = "privacy protection"; break;
}
}
snmp_increment_args(AUTHENTICATION_YES, 1,
VARIABLE_AUTH, 0,
VARIABLE_LISTEND);
prot_printf(imapd_out, "%s OK Success (%s)\r\n", tag, ssfmsg);
prot_setsasl(imapd_in, imapd_saslconn);
prot_setsasl(imapd_out, imapd_saslconn);
imapd_logfd = telemetry_log(imapd_userid, imapd_in, imapd_out, 0);
if ((r = mboxname_init_namespace(&imapd_namespace,
imapd_userisadmin || imapd_userisproxyadmin)) != 0) {
syslog(LOG_ERR, error_message(r));
fatal(error_message(r), EC_CONFIG);
}
mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid,
config_virtdomains ?
strcspn(imapd_userid, "@") : 0);
imapd_authstate = auth_newstate(imapd_userid);
imapd_userisadmin = global_authisa( imapd_authstate, IMAPOPT_ADMINS );
if ( !imapd_userisadmin )
{
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, "INBOX", imapd_userid, mailboxname);
if ( !r )
{
char *partition = NULL;
struct od_user_opts useropts;
odGetUserOpts( imapd_userid, &useropts );
if ( useropts.fAltDataLoc[ 0 ] != '\0' )
{
partition = useropts.fAltDataLoc;
}
r = mboxlist_createmailbox( mailboxname, MAILBOX_FORMAT_NORMAL, partition, 1, imapd_userid, imapd_authstate, 0, 0, 0 );
if ( r && (r !=IMAP_MAILBOX_EXISTS) )
{
syslog( LOG_DEBUG, "mboxlist_createmailbox error(%d) for mailbox(%s)", r, mailboxname);
}
}
if ( r == IMAP_MAILBOX_EXISTS )
{
if ( useropts.fDiskQuota == 0 )
{
mboxlist_setquota( mailboxname, INT_MAX, 0 );
syslog( LOG_DEBUG, "quota set to \"unlimited\" for mailbox %s ", mailboxname);
}
else
{
mboxlist_setquota( mailboxname, useropts.fDiskQuota * 1024, 0 );
syslog( LOG_DEBUG, "quota set to %lu for mailbox %s ", useropts.fDiskQuota * 1024, mailboxname);
}
}
}
return;
}
void
cmd_noop(char *tag, char *cmd __attribute__((unused)))
{
if (imapd_mailbox) {
index_check(imapd_mailbox, 0, 1);
}
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
void cmd_id(char *tag)
{
static int did_id = 0;
static int failed_id = 0;
static int logged_id = 0;
int error = 0;
int c = EOF, npair = 0;
static struct buf arg, field;
struct attvaluelist *params = 0;
if (!imapd_userid && did_id) {
prot_printf(imapd_out,
"%s NO Only one Id allowed in non-authenticated state\r\n",
tag);
eatline(imapd_in, c);
return;
}
if (failed_id >= MAXIDFAILED) {
prot_printf(imapd_out, "%s NO Too many (%u) invalid Id commands\r\n",
tag, failed_id);
eatline(imapd_in, c);
return;
}
c = getword(imapd_in, &arg);
if (strcasecmp(arg.s, "NIL") && c != '(') {
prot_printf(imapd_out, "%s BAD Invalid parameter list in Id\r\n", tag);
eatline(imapd_in, c);
failed_id++;
return;
}
if (c == '(') {
for (;;) {
if (c == ')') {
break;
}
c = getstring(imapd_in, imapd_out, &field);
if (c != ' ') {
prot_printf(imapd_out,
"%s BAD Invalid/missing field name in Id\r\n",
tag);
error = 1;
break;
}
c = getnstring(imapd_in, imapd_out, &arg);
if (c != ' ' && c != ')') {
prot_printf(imapd_out,
"%s BAD Invalid/missing value in Id\r\n",
tag);
error = 1;
break;
}
if (strlen(field.s) > MAXIDFIELDLEN) {
prot_printf(imapd_out,
"%s BAD field longer than %u octets in Id\r\n",
tag, MAXIDFIELDLEN);
error = 1;
break;
}
if (strlen(arg.s) > MAXIDVALUELEN) {
prot_printf(imapd_out,
"%s BAD value longer than %u octets in Id\r\n",
tag, MAXIDVALUELEN);
error = 1;
break;
}
if (++npair > MAXIDPAIRS) {
prot_printf(imapd_out,
"%s BAD too many (%u) field-value pairs in ID\r\n",
tag, MAXIDPAIRS);
error = 1;
break;
}
appendattvalue(¶ms, field.s, arg.s);
}
if (error || c != ')') {
eatline(imapd_in, c);
freeattvalues(params);
failed_id++;
return;
}
c = prot_getc(imapd_in);
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') {
prot_printf(imapd_out,
"%s BAD Unexpected extra arguments to Id\r\n", tag);
eatline(imapd_in, c);
freeattvalues(params);
failed_id++;
return;
}
if (npair && logged_id < MAXIDLOG) {
char logbuf[MAXIDLOGLEN + 1] = "";
struct attvaluelist *pptr;
for (pptr = params; pptr; pptr = pptr->next) {
snprintf(logbuf + strlen(logbuf), MAXIDLOGLEN - strlen(logbuf),
" \"%s\" ", pptr->attrib);
if (!strcmp(pptr->value, "NIL"))
snprintf(logbuf + strlen(logbuf), MAXIDLOGLEN - strlen(logbuf),
"NIL");
else
snprintf(logbuf + strlen(logbuf), MAXIDLOGLEN - strlen(logbuf),
"\"%s\"", pptr->value);
}
syslog(LOG_INFO, "client id:%s", logbuf);
logged_id++;
}
freeattvalues(params);
if (config_getswitch(IMAPOPT_IMAPIDRESPONSE)) {
id_response(imapd_out);
prot_printf(imapd_out, ")\r\n");
}
else
prot_printf(imapd_out, "* ID NIL\r\n");
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
failed_id = 0;
did_id = 1;
}
void cmd_idle(char *tag)
{
int c;
static struct buf arg;
if (!idle_init(imapd_mailbox, idle_update)) {
prot_printf(imapd_out,
"%s NO cannot start idling\r\n", tag);
return;
}
prot_printf(imapd_out, "+ idling\r\n");
prot_flush(imapd_out);
c = getword(imapd_in, &arg);
if (c != EOF) {
if (!strcasecmp(arg.s, "Done") &&
(c = (c == '\r') ? prot_getc(imapd_in) : c) == '\n') {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
else {
prot_printf(imapd_out,
"%s BAD Invalid Idle continuation\r\n", tag);
eatline(imapd_in, c);
}
}
idle_done(imapd_mailbox);
return;
}
void idle_update(idle_flags_t flags)
{
if ((flags & IDLE_MAILBOX) && imapd_mailbox)
index_check(imapd_mailbox, 0, 1);
if (flags & IDLE_ALERT) {
char shut[1024];
if (! imapd_userisadmin && shutdown_file(shut, sizeof(shut))) {
char *p;
for (p = shut; *p == '['; p++);
prot_printf(imapd_out, "* BYE [ALERT] %s\r\n", p);
shut_down(0);
}
}
prot_flush(imapd_out);
}
void cmd_capability(char *tag)
{
const char *sasllist;
unsigned mechcount;
if (imapd_mailbox) {
index_check(imapd_mailbox, 0, 0);
}
prot_printf(imapd_out, "* CAPABILITY " CAPABILITY_STRING);
if (idle_enabled()) {
prot_printf(imapd_out, " IDLE");
}
if (tls_enabled() && !imapd_starttls_done && !imapd_authstate) {
prot_printf(imapd_out, " STARTTLS");
}
if (imapd_authstate ||
(!imapd_starttls_done && !config_getswitch(IMAPOPT_ALLOWPLAINTEXT))) {
prot_printf(imapd_out, " LOGINDISABLED");
}
if(config_mupdate_server) {
prot_printf(imapd_out, " MUPDATE=mupdate://%s/", config_mupdate_server);
}
if ( config_getswitch( IMAPOPT_APPLE_AUTH ) == 0 )
{
if (!imapd_authstate &&
sasl_listmech(imapd_saslconn, NULL,
"AUTH=", " AUTH=", " SASL-IR",
&sasllist,
NULL, &mechcount) == SASL_OK && mechcount > 0) {
prot_printf(imapd_out, " %s", sasllist);
} else {
}
}
else
{
if ( config_getswitch( IMAPOPT_IMAP_AUTH_PLAIN ) )
{
prot_printf(imapd_out, " AUTH=PLAIN");
}
if ( config_getswitch( IMAPOPT_IMAP_AUTH_LOGIN ) )
{
prot_printf(imapd_out, " AUTH=LOGIN");
}
if ( config_getswitch( IMAPOPT_IMAP_AUTH_CRAM_MD5 ) )
{
prot_printf(imapd_out, " AUTH=CRAM-MD5");
}
if ( config_getswitch( IMAPOPT_IMAP_AUTH_GSSAPI ) )
{
prot_printf(imapd_out, " AUTH=GSSAPI");
}
}
#ifdef ENABLE_LISTEXT
prot_printf(imapd_out, " LISTEXT LIST-SUBSCRIBED");
#endif
#ifdef ENABLE_X_NETSCAPE_HACK
prot_printf(imapd_out, " X-NETSCAPE");
#endif
prot_printf(imapd_out, "\r\n%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
static int isokflag(char *s)
{
if (s[0] == '\\') {
lcase(s);
if (!strcmp(s, "\\seen")) return 1;
if (!strcmp(s, "\\answered")) return 1;
if (!strcmp(s, "\\flagged")) return 1;
if (!strcmp(s, "\\draft")) return 1;
if (!strcmp(s, "\\deleted")) return 1;
return 0;
} else {
return imparse_isatom(s);
}
}
#define FLAGGROW 10
void cmd_append(char *tag, char *name)
{
int c;
static struct buf arg;
char *p;
time_t now = time(NULL);
unsigned size, totalsize = 0;
int sawdigit = 0;
int isnowait = 0;
int r, i;
char mailboxname[MAX_MAILBOX_NAME+1];
struct appendstate mailbox;
unsigned long uidvalidity;
unsigned long firstuid, num;
const char *parseerr = NULL;
FILE *f;
int numalloc = 5;
struct appendstage *curstage;
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
if (!r) {
r = append_check(mailboxname, MAILBOX_FORMAT_NORMAL,
imapd_authstate, ACL_INSERT, totalsize);
}
if (r) {
eatline(imapd_in, ' ');
prot_printf(imapd_out, "%s NO %s%s\r\n",
tag,
(r == IMAP_MAILBOX_NONEXISTENT &&
mboxlist_createmailboxcheck(mailboxname, 0, 0,
imapd_userisadmin,
imapd_userid, imapd_authstate,
(char **)0, (char **)0) == 0)
? "[TRYCREATE] " : "", error_message(r));
return;
}
stage = xmalloc(numalloc * sizeof(struct appendstage *));
c = ' ';
while (!r && c == ' ') {
if (numstage == numalloc) {
if (numalloc > INT_MAX/(2*sizeof(struct appendstage *)))
goto done;
numalloc *= 2;
stage = xrealloc(stage, numalloc * sizeof(struct appendstage *));
}
curstage = stage[numstage] = xzmalloc(sizeof(struct appendstage));
numstage++;
c = getword(imapd_in, &arg);
if (c == '(' && !arg.s[0]) {
curstage->nflags = 0;
do {
c = getword(imapd_in, &arg);
if (!curstage->nflags && !arg.s[0] && c == ')') break;
if (!isokflag(arg.s)) {
parseerr = "Invalid flag in Append command";
r = IMAP_PROTOCOL_ERROR;
goto done;
}
if (curstage->nflags == curstage->flagalloc) {
curstage->flagalloc += FLAGGROW;
curstage->flag =
(char **) xrealloc((char *) curstage->flag,
curstage->flagalloc * sizeof(char *));
}
curstage->flag[curstage->nflags] = xstrdup(arg.s);
curstage->nflags++;
} while (c == ' ');
if (c != ')') {
parseerr =
"Missing space or ) after flag name in Append command";
r = IMAP_PROTOCOL_ERROR;
goto done;
}
c = prot_getc(imapd_in);
if (c != ' ') {
parseerr = "Missing space after flag list in Append command";
r = IMAP_PROTOCOL_ERROR;
goto done;
}
c = getword(imapd_in, &arg);
}
if (c == '\"' && !arg.s[0]) {
prot_ungetc(c, imapd_in);
c = getdatetime(&(curstage->internaldate));
if (c != ' ') {
parseerr = "Invalid date-time in Append command";
r = IMAP_PROTOCOL_ERROR;
goto done;
}
c = getword(imapd_in, &arg);
} else {
curstage->internaldate = now;
}
p = arg.s;
if (*p == '~') {
p++;
r = IMAP_NO_UNKNOWN_CTE;
goto done;
}
if (*p != '{') {
parseerr = "Missing required argument to Append command";
r = IMAP_PROTOCOL_ERROR;
goto done;
}
isnowait = 0;
size = 0;
for (++p; *p && isdigit((int) *p); p++) {
sawdigit++;
size = size*10 + *p - '0';
#if 0
if (size < 0) {
lose();
}
#endif
}
if (*p == '+') {
isnowait++;
p++;
}
if (c == '\r') {
c = prot_getc(imapd_in);
}
else {
prot_ungetc(c, imapd_in);
c = ' ';
}
if (*p != '}' || p[1] || c != '\n' || !sawdigit) {
parseerr = "Invalid literal in Append command";
r = IMAP_PROTOCOL_ERROR;
goto done;
}
if (!isnowait) {
prot_printf(imapd_out, "+ go ahead\r\n");
prot_flush(imapd_out);
}
f = append_newstage(mailboxname, now, numstage, &(curstage->stage));
if (f) {
totalsize += size;
r = message_copy_strict(imapd_in, f, size);
fclose(f);
} else {
r = IMAP_IOERROR;
}
c = prot_getc(imapd_in);
}
done:
if (r) {
eatline(imapd_in, c);
} else {
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') {
parseerr = "junk after literal";
r = IMAP_PROTOCOL_ERROR;
eatline(imapd_in, c);
}
}
if (!r) {
r = append_setup(&mailbox, mailboxname, MAILBOX_FORMAT_NORMAL,
imapd_userid, imapd_authstate, ACL_INSERT, totalsize);
}
if (!r) {
for (i = 0; !r && i < numstage; i++) {
r = append_fromstage(&mailbox, stage[i]->stage, stage[i]->internaldate,
(const char **) stage[i]->flag, stage[i]->nflags, 0);
}
if (!r) {
r = append_commit(&mailbox, totalsize, &uidvalidity, &firstuid, &num);
} else {
append_abort(&mailbox);
}
}
while (numstage) {
curstage = stage[--numstage];
append_removestage(curstage->stage);
while (curstage->nflags--) {
free(curstage->flag[curstage->nflags]);
}
if (curstage->flag) free((char *) curstage->flag);
free(curstage);
}
if (stage) free(stage);
stage = NULL;
if (imapd_mailbox) {
index_check(imapd_mailbox, 0, 0);
}
if (r == IMAP_PROTOCOL_ERROR && parseerr) {
prot_printf(imapd_out, "%s BAD %s\r\n", tag, parseerr);
} else if (r) {
prot_printf(imapd_out, "%s NO %s%s\r\n",
tag,
(r == IMAP_MAILBOX_NONEXISTENT &&
mboxlist_createmailboxcheck(mailboxname, 0, 0,
imapd_userisadmin,
imapd_userid, imapd_authstate,
(char **)0, (char **)0) == 0)
? "[TRYCREATE] " : "", error_message(r));
} else {
prot_printf(imapd_out, "%s OK [APPENDUID %lu", tag, uidvalidity);
if (num == 1) {
prot_printf(imapd_out, " %lu", firstuid);
} else {
prot_printf(imapd_out, " %lu:%lu", firstuid, firstuid + num - 1);
}
prot_printf(imapd_out, "] %s\r\n", error_message(IMAP_OK_COMPLETED));
}
}
void cmd_select(char *tag, char *cmd, char *name)
{
struct mailbox mailbox;
char mailboxname[MAX_MAILBOX_NAME+1];
int r = 0;
double usage;
int doclose = 0;
if (imapd_mailbox) {
index_closemailbox(imapd_mailbox);
mailbox_close(imapd_mailbox);
imapd_mailbox = 0;
}
if (cmd[0] == 'B') {
r = IMAP_MAILBOX_NONEXISTENT;
}
else {
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
}
if (!r) {
r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, NULL, NULL);
}
if (r == IMAP_MAILBOX_MOVED) return;
if (!r) {
r = mailbox_open_header(mailboxname, imapd_authstate, &mailbox);
}
if (!r) {
doclose = 1;
r = mailbox_open_index(&mailbox);
}
if (!r && !(mailbox.myrights & ACL_READ)) {
r = (imapd_userisadmin || (mailbox.myrights & ACL_LOOKUP)) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
}
if (!r && chdir(mailbox.path)) {
syslog(LOG_ERR, "IOERROR: changing directory to %s: %m", mailbox.path);
r = IMAP_IOERROR;
}
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
if (doclose) mailbox_close(&mailbox);
return;
}
mboxstruct = mailbox;
imapd_mailbox = &mboxstruct;
index_newmailbox(imapd_mailbox, cmd[0] == 'E');
if (cmd[0] == 'E') {
imapd_mailbox->myrights &= ~(ACL_SEEN|ACL_WRITE|ACL_REMOVE);
}
if (imapd_mailbox->myrights & ACL_REMOVE) {
r = quota_read(&imapd_mailbox->quota, NULL, 0);
if (!r && imapd_mailbox->quota.limit > 0) {
int warnsize = config_getint(IMAPOPT_QUOTAWARNKB);
if (warnsize <= 0 || warnsize >= imapd_mailbox->quota.limit ||
(int)((imapd_mailbox->quota.limit * QUOTA_UNITS) -
imapd_mailbox->quota.used) < (warnsize * QUOTA_UNITS)) {
usage = ((double) imapd_mailbox->quota.used * 100.0) / (double)
(imapd_mailbox->quota.limit * QUOTA_UNITS);
if (usage >= 100.0) {
prot_printf(imapd_out, "* NO [ALERT] %s\r\n",
error_message(IMAP_NO_OVERQUOTA));
}
else if (usage > config_getint(IMAPOPT_QUOTAWARN)) {
int usageint = (int) usage;
prot_printf(imapd_out, "* NO [ALERT] ");
prot_printf(imapd_out, error_message(IMAP_NO_CLOSEQUOTA),
usageint);
prot_printf(imapd_out, "\r\n");
}
}
}
}
prot_printf(imapd_out, "%s OK [READ-%s] %s\r\n", tag,
(imapd_mailbox->myrights & (ACL_WRITE|ACL_REMOVE)) ?
"WRITE" : "ONLY", error_message(IMAP_OK_COMPLETED));
proc_register("imapd", imapd_clienthost, imapd_userid, mailboxname);
syslog(LOG_DEBUG, "open: user %s opened %s", imapd_userid, name);
}
void
cmd_close(tag)
char *tag;
{
int r;
if (!(imapd_mailbox->myrights & ACL_REMOVE)) r = 0;
else {
r = mailbox_expunge(imapd_mailbox, 1, (int (*)())0, (char *)0);
}
index_closemailbox(imapd_mailbox);
mailbox_close(imapd_mailbox);
imapd_mailbox = 0;
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
}
else {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
}
void
cmd_unselect(tag)
char* tag;
{
index_closemailbox(imapd_mailbox);
mailbox_close(imapd_mailbox);
imapd_mailbox = 0;
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
#define PARSE_PARTIAL(start_octet, octet_count) \
(start_octet) = (octet_count) = 0; \
if (*p == '<' && isdigit((int) p[1])) { \
(start_octet) = p[1] - '0'; \
p += 2; \
while (isdigit((int) *p)) { \
(start_octet) = \
(start_octet) * 10 + *p++ - '0'; \
} \
\
if (*p == '.' && p[1] >= '1' && p[1] <= '9') { \
(octet_count) = p[1] - '0'; \
p[0] = '>'; p[1] = '\0'; \
p += 2; \
while (isdigit((int) *p)) { \
(octet_count) = \
(octet_count) * 10 + *p++ - '0'; \
} \
} \
else p--; \
\
if (*p != '>') { \
prot_printf(imapd_out, \
"%s BAD Invalid body partial\r\n", tag); \
eatline(imapd_in, c); \
goto freeargs; \
} \
p++; \
}
void cmd_fetch(char *tag, char *sequence, int usinguid)
{
char *cmd = usinguid ? "UID Fetch" : "Fetch";
static struct buf fetchatt, fieldname;
int c;
int inlist = 0;
int fetchitems = 0;
struct fetchargs fetchargs;
struct octetinfo oi;
struct strlist *newfields = 0;
char *p, *section;
int fetchedsomething, r;
clock_t start = clock();
char mytime[100];
memset(&fetchargs, 0, sizeof(struct fetchargs));
c = getword(imapd_in, &fetchatt);
if (c == '(' && !fetchatt.s[0]) {
inlist = 1;
c = getword(imapd_in, &fetchatt);
}
for (;;) {
ucase(fetchatt.s);
switch (fetchatt.s[0]) {
case 'A':
if (!inlist && !strcmp(fetchatt.s, "ALL")) {
fetchitems |= FETCH_ALL;
}
else goto badatt;
break;
case 'B':
if (!strncmp(fetchatt.s, "BINARY[", 7) ||
!strncmp(fetchatt.s, "BINARY.PEEK[", 12) ||
!strncmp(fetchatt.s, "BINARY.SIZE[", 12)) {
int binsize = 0;
p = section = fetchatt.s + 7;
if (!strncmp(p, "PEEK[", 5)) {
p = section += 5;
}
else if (!strncmp(p, "SIZE[", 5)) {
p = section += 5;
binsize = 1;
}
else {
fetchitems |= FETCH_SETSEEN;
}
while ((*p >= '1' && *p <= '9') || *p == '.') {
if (*p == '.' && !isdigit((int) p[-1])) break;
p++;
}
if (*p != ']') {
prot_printf(imapd_out, "%s BAD Invalid body section\r\n", tag);
eatline(imapd_in, c);
goto freeargs;
}
p++;
if (!binsize) PARSE_PARTIAL(oi.start_octet, oi.octet_count);
if (*p) {
prot_printf(imapd_out, "%s BAD Junk after body section\r\n", tag);
eatline(imapd_in, c);
goto freeargs;
}
if (binsize)
appendstrlist_withdata(&fetchargs.sizesections, section, &oi, sizeof(oi));
else
appendstrlist_withdata(&fetchargs.binsections, section, &oi, sizeof(oi));
}
else if (!strcmp(fetchatt.s, "BODY")) {
fetchitems |= FETCH_BODY;
}
else if (!strcmp(fetchatt.s, "BODYSTRUCTURE")) {
fetchitems |= FETCH_BODYSTRUCTURE;
}
else if (!strncmp(fetchatt.s, "BODY[", 5) ||
!strncmp(fetchatt.s, "BODY.PEEK[", 10)) {
p = section = fetchatt.s + 5;
if (!strncmp(p, "PEEK[", 5)) {
p = section += 5;
}
else {
fetchitems |= FETCH_SETSEEN;
}
while (isdigit((int) *p) || *p == '.') {
if (*p == '.' && !isdigit((int) p[-1])) break;
if (*p == '0' && !isdigit((int) p[-1]) && p[1] != ']') break;
p++;
}
if (*p == 'H' && !strncmp(p, "HEADER.FIELDS", 13) &&
(p == section || p[-1] == '.') &&
(p[13] == '\0' || !strcmp(p+13, ".NOT"))) {
if (p != section || p[13] != '\0') {
fetchargs.cache_atleast = BIT32_MAX;
}
if (c != ' ') {
prot_printf(imapd_out,
"%s BAD Missing required argument to %s %s\r\n",
tag, cmd, fetchatt.s);
eatline(imapd_in, c);
goto freeargs;
}
c = prot_getc(imapd_in);
if (c != '(') {
prot_printf(imapd_out, "%s BAD Missing required open parenthesis in %s %s\r\n",
tag, cmd, fetchatt.s);
eatline(imapd_in, c);
goto freeargs;
}
do {
c = getastring(imapd_in, imapd_out, &fieldname);
for (p = fieldname.s; *p; p++) {
if (*p <= ' ' || *p & 0x80 || *p == ':') break;
}
if (*p || !*fieldname.s) {
prot_printf(imapd_out, "%s BAD Invalid field-name in %s %s\r\n",
tag, cmd, fetchatt.s);
eatline(imapd_in, c);
goto freeargs;
}
appendstrlist(&newfields, fieldname.s);
if (fetchargs.cache_atleast < BIT32_MAX) {
bit32 this_ver =
mailbox_cached_header(fieldname.s);
if(this_ver > fetchargs.cache_atleast)
fetchargs.cache_atleast = this_ver;
}
} while (c == ' ');
if (c != ')') {
prot_printf(imapd_out, "%s BAD Missing required close parenthesis in %s %s\r\n",
tag, cmd, fetchatt.s);
eatline(imapd_in, c);
goto freeargs;
}
c = getword(imapd_in, &fieldname);
p = fieldname.s;
if (*p++ != ']') {
prot_printf(imapd_out, "%s BAD Missing required close bracket after %s %s\r\n",
tag, cmd, fetchatt.s);
eatline(imapd_in, c);
goto freeargs;
}
PARSE_PARTIAL(oi.start_octet, oi.octet_count);
if (*p) {
prot_printf(imapd_out, "%s BAD Junk after body section\r\n", tag);
eatline(imapd_in, c);
goto freeargs;
}
appendfieldlist(&fetchargs.fsections,
section, newfields, fieldname.s,
&oi, sizeof(oi));
newfields = 0;
break;
}
switch (*p) {
case 'H':
if (p != section && p[-1] != '.') break;
if (!strncmp(p, "HEADER]", 7)) p += 6;
break;
case 'M':
if (!strncmp(p-1, ".MIME]", 6)) p += 4;
break;
case 'T':
if (p != section && p[-1] != '.') break;
if (!strncmp(p, "TEXT]", 5)) p += 4;
break;
}
if (*p != ']') {
prot_printf(imapd_out, "%s BAD Invalid body section\r\n", tag);
eatline(imapd_in, c);
goto freeargs;
}
p++;
PARSE_PARTIAL(oi.start_octet, oi.octet_count);
if (*p) {
prot_printf(imapd_out, "%s BAD Junk after body section\r\n", tag);
eatline(imapd_in, c);
goto freeargs;
}
appendstrlist_withdata(&fetchargs.bodysections, section,
&oi, sizeof(oi));
}
else goto badatt;
break;
case 'E':
if (!strcmp(fetchatt.s, "ENVELOPE")) {
fetchitems |= FETCH_ENVELOPE;
}
else goto badatt;
break;
case 'F':
if (!inlist && !strcmp(fetchatt.s, "FAST")) {
fetchitems |= FETCH_FAST;
}
else if (!inlist && !strcmp(fetchatt.s, "FULL")) {
fetchitems |= FETCH_FULL;
}
else if (!strcmp(fetchatt.s, "FLAGS")) {
fetchitems |= FETCH_FLAGS;
}
else goto badatt;
break;
case 'I':
if (!strcmp(fetchatt.s, "INTERNALDATE")) {
fetchitems |= FETCH_INTERNALDATE;
}
else goto badatt;
break;
case 'R':
if (!strcmp(fetchatt.s, "RFC822")) {
fetchitems |= FETCH_RFC822|FETCH_SETSEEN;
}
else if (!strcmp(fetchatt.s, "RFC822.HEADER")) {
fetchitems |= FETCH_HEADER;
}
else if (!strcmp(fetchatt.s, "RFC822.PEEK")) {
fetchitems |= FETCH_RFC822;
}
else if (!strcmp(fetchatt.s, "RFC822.SIZE")) {
fetchitems |= FETCH_SIZE;
}
else if (!strcmp(fetchatt.s, "RFC822.TEXT")) {
fetchitems |= FETCH_TEXT|FETCH_SETSEEN;
}
else if (!strcmp(fetchatt.s, "RFC822.TEXT.PEEK")) {
fetchitems |= FETCH_TEXT;
}
else if (!strcmp(fetchatt.s, "RFC822.HEADER.LINES") ||
!strcmp(fetchatt.s, "RFC822.HEADER.LINES.NOT")) {
if (c != ' ') {
prot_printf(imapd_out, "%s BAD Missing required argument to %s %s\r\n",
tag, cmd, fetchatt.s);
eatline(imapd_in, c);
goto freeargs;
}
c = prot_getc(imapd_in);
if (c != '(') {
prot_printf(imapd_out, "%s BAD Missing required open parenthesis in %s %s\r\n",
tag, cmd, fetchatt.s);
eatline(imapd_in, c);
goto freeargs;
}
do {
c = getastring(imapd_in, imapd_out, &fieldname);
for (p = fieldname.s; *p; p++) {
if (*p <= ' ' || *p & 0x80 || *p == ':') break;
}
if (*p || !*fieldname.s) {
prot_printf(imapd_out, "%s BAD Invalid field-name in %s %s\r\n",
tag, cmd, fetchatt.s);
eatline(imapd_in, c);
goto freeargs;
}
lcase(fieldname.s);;
appendstrlist(strlen(fetchatt.s) == 19 ?
&fetchargs.headers : &fetchargs.headers_not,
fieldname.s);
if (strlen(fetchatt.s) != 19) {
fetchargs.cache_atleast = BIT32_MAX;
}
if (fetchargs.cache_atleast < BIT32_MAX) {
bit32 this_ver =
mailbox_cached_header(fieldname.s);
if(this_ver > fetchargs.cache_atleast)
fetchargs.cache_atleast = this_ver;
}
} while (c == ' ');
if (c != ')') {
prot_printf(imapd_out, "%s BAD Missing required close parenthesis in %s %s\r\n",
tag, cmd, fetchatt.s);
eatline(imapd_in, c);
goto freeargs;
}
c = prot_getc(imapd_in);
}
else goto badatt;
break;
case 'U':
if (!strcmp(fetchatt.s, "UID")) {
fetchitems |= FETCH_UID;
}
else goto badatt;
break;
default:
badatt:
prot_printf(imapd_out, "%s BAD Invalid %s attribute %s\r\n", tag, cmd, fetchatt.s);
eatline(imapd_in, c);
goto freeargs;
}
if (inlist && c == ' ') c = getword(imapd_in, &fetchatt);
else break;
}
if (inlist && c == ')') {
inlist = 0;
c = prot_getc(imapd_in);
}
if (inlist) {
prot_printf(imapd_out, "%s BAD Missing close parenthesis in %s\r\n", tag, cmd);
eatline(imapd_in, c);
goto freeargs;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') {
prot_printf(imapd_out, "%s BAD Unexpected extra arguments to %s\r\n", tag, cmd);
eatline(imapd_in, c);
goto freeargs;
}
if (!fetchitems && !fetchargs.bodysections && !fetchargs.fsections &&
!fetchargs.binsections && !fetchargs.sizesections &&
!fetchargs.headers && !fetchargs.headers_not) {
prot_printf(imapd_out, "%s BAD Missing required argument to %s\r\n", tag, cmd);
goto freeargs;
}
if (usinguid) {
fetchitems |= FETCH_UID;
index_check(imapd_mailbox, 1, 0);
}
fetchargs.fetchitems = fetchitems;
r = index_fetch(imapd_mailbox, sequence, usinguid, &fetchargs,
&fetchedsomething);
snprintf(mytime, sizeof(mytime), "%2.3f",
(clock() - start) / (double) CLOCKS_PER_SEC);
if (r) {
prot_printf(imapd_out, "%s NO %s (%s sec)\r\n", tag,
error_message(r), mytime);
} else if (fetchedsomething || usinguid) {
prot_printf(imapd_out, "%s OK %s (%s sec)\r\n", tag,
error_message(IMAP_OK_COMPLETED), mytime);
} else {
prot_printf(imapd_out, "%s NO %s (%s sec)\r\n", tag,
error_message(IMAP_NO_NOSUCHMSG), mytime);
}
freeargs:
freestrlist(newfields);
freestrlist(fetchargs.bodysections);
freefieldlist(fetchargs.fsections);
freestrlist(fetchargs.headers);
freestrlist(fetchargs.headers_not);
}
#undef PARSE_PARTIAL
void cmd_partial(const char *tag, const char *msgno, char *data,
const char *start, const char *count)
{
const char *pc;
char *p;
struct fetchargs fetchargs;
char *section;
int prev;
int fetchedsomething;
memset(&fetchargs, 0, sizeof(struct fetchargs));
for (pc = msgno; *pc; pc++) {
if (!isdigit((int) *pc)) break;
}
if (*pc || !*msgno) {
prot_printf(imapd_out, "%s BAD Invalid message number\r\n", tag);
return;
}
lcase(data);
if (!strcmp(data, "rfc822")) {
fetchargs.fetchitems = FETCH_RFC822|FETCH_SETSEEN;
}
else if (!strcmp(data, "rfc822.header")) {
fetchargs.fetchitems = FETCH_HEADER;
}
else if (!strcmp(data, "rfc822.peek")) {
fetchargs.fetchitems = FETCH_RFC822;
}
else if (!strcmp(data, "rfc822.text")) {
fetchargs.fetchitems = FETCH_TEXT|FETCH_SETSEEN;
}
else if (!strcmp(data, "rfc822.text.peek")) {
fetchargs.fetchitems = FETCH_TEXT;
}
else if (!strncmp(data, "body[", 5) ||
!strncmp(data, "body.peek[", 10)) {
p = section = data + 5;
if (!strncmp(p, "peek[", 5)) {
p = section += 5;
}
else {
fetchargs.fetchitems = FETCH_SETSEEN;
}
while (isdigit((int) *p) || *p == '.') {
if (*p == '.' && (p == section || !isdigit((int) p[1]))) break;
p++;
}
if (p == section || *p != ']' || p[1]) {
prot_printf(imapd_out, "%s BAD Invalid body section\r\n", tag);
freestrlist(fetchargs.bodysections);
return;
}
*(p+1) = '\0';
appendstrlist(&fetchargs.bodysections, section);
}
else {
prot_printf(imapd_out, "%s BAD Invalid Partial item\r\n", tag);
freestrlist(fetchargs.bodysections);
return;
}
for (pc = start; *pc; pc++) {
if (!isdigit((int) *pc)) break;
prev = fetchargs.start_octet;
fetchargs.start_octet = fetchargs.start_octet*10 + *pc - '0';
if(fetchargs.start_octet < prev) {
fetchargs.start_octet = 0;
break;
}
}
if (*pc || !fetchargs.start_octet) {
prot_printf(imapd_out, "%s BAD Invalid starting octet\r\n", tag);
freestrlist(fetchargs.bodysections);
return;
}
fetchargs.start_octet--;
prev = fetchargs.octet_count;
for (pc = count; *pc; pc++) {
if (!isdigit((int) *pc)) break;
prev = fetchargs.octet_count;
fetchargs.octet_count = fetchargs.octet_count*10 + *pc - '0';
if(fetchargs.octet_count < prev) {
prev = -1;
break;
}
}
if (*pc || !*count || prev == -1) {
prot_printf(imapd_out, "%s BAD Invalid octet count\r\n", tag);
freestrlist(fetchargs.bodysections);
return;
}
fetchargs.fetchitems |= FETCH_IS_PARTIAL;
index_fetch(imapd_mailbox, msgno, 0, &fetchargs, &fetchedsomething);
index_check(imapd_mailbox, 0, 0);
if (fetchedsomething) {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
} else {
prot_printf(imapd_out,
"%s BAD Invalid sequence in PARTIAL command\r\n",
tag);
}
freestrlist(fetchargs.bodysections);
}
void cmd_store(char *tag, char *sequence, char *operation, int usinguid)
{
char *cmd = usinguid ? "UID Store" : "Store";
struct storeargs storeargs;
static struct buf flagname;
int len, c;
char **flag = 0;
int nflags = 0, flagalloc = 0;
int flagsparsed = 0, inlist = 0;
int r;
memset(&storeargs, 0, sizeof storeargs);
lcase(operation);
len = strlen(operation);
if (len > 7 && !strcmp(operation+len-7, ".silent")) {
storeargs.silent = 1;
operation[len-7] = '\0';
}
if (!strcmp(operation, "+flags")) {
storeargs.operation = STORE_ADD;
}
else if (!strcmp(operation, "-flags")) {
storeargs.operation = STORE_REMOVE;
}
else if (!strcmp(operation, "flags")) {
storeargs.operation = STORE_REPLACE;
}
else {
prot_printf(imapd_out, "%s BAD Invalid %s attribute\r\n", tag, cmd);
eatline(imapd_in, ' ');
return;
}
for (;;) {
c = getword(imapd_in, &flagname);
if (c == '(' && !flagname.s[0] && !flagsparsed && !inlist) {
inlist = 1;
continue;
}
if (!flagname.s[0]) break;
if (flagname.s[0] == '\\') {
lcase(flagname.s);
if (!strcmp(flagname.s, "\\seen")) {
storeargs.seen = 1;
}
else if (!strcmp(flagname.s, "\\answered")) {
storeargs.system_flags |= FLAG_ANSWERED;
}
else if (!strcmp(flagname.s, "\\flagged")) {
storeargs.system_flags |= FLAG_FLAGGED;
}
else if (!strcmp(flagname.s, "\\deleted")) {
storeargs.system_flags |= FLAG_DELETED;
}
else if (!strcmp(flagname.s, "\\draft")) {
storeargs.system_flags |= FLAG_DRAFT;
}
else {
prot_printf(imapd_out, "%s BAD Invalid system flag in %s command\r\n",
tag, cmd);
eatline(imapd_in, c);
goto freeflags;
}
}
else if (!imparse_isatom(flagname.s)) {
prot_printf(imapd_out, "%s BAD Invalid flag name %s in %s command\r\n",
tag, flagname.s, cmd);
eatline(imapd_in, c);
goto freeflags;
}
else {
if (nflags == flagalloc) {
flagalloc += FLAGGROW;
flag = (char **)xrealloc((char *)flag,
flagalloc*sizeof(char *));
}
flag[nflags] = xstrdup(flagname.s);
nflags++;
}
flagsparsed++;
if (c != ' ') break;
}
if (!inlist && !flagsparsed) {
prot_printf(imapd_out, "%s BAD Missing required argument to %s\r\n", tag, cmd);
eatline(imapd_in, c);
return;
}
if (inlist && c == ')') {
inlist = 0;
c = prot_getc(imapd_in);
}
if (inlist) {
prot_printf(imapd_out, "%s BAD Missing close parenthesis in %s\r\n", tag, cmd);
eatline(imapd_in, c);
goto freeflags;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') {
prot_printf(imapd_out, "%s BAD Unexpected extra arguments to %s\r\n", tag, cmd);
eatline(imapd_in, c);
goto freeflags;
}
r = index_store(imapd_mailbox, sequence, usinguid, &storeargs,
flag, nflags);
if (usinguid) {
index_check(imapd_mailbox, 1, 0);
}
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
}
else {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
freeflags:
while (nflags--) {
free(flag[nflags]);
}
if (flag) free((char *)flag);
}
void
cmd_search(tag, usinguid)
char *tag;
int usinguid;
{
int c;
int charset = 0;
struct searchargs *searchargs;
clock_t start = clock();
char mytime[100];
int n;
searchargs = (struct searchargs *)xzmalloc(sizeof(struct searchargs));
c = getsearchprogram(tag, searchargs, &charset, 1);
if (c == EOF) {
eatline(imapd_in, ' ');
freesearchargs(searchargs);
return;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') {
prot_printf(imapd_out, "%s BAD Unexpected extra arguments to Search\r\n", tag);
eatline(imapd_in, c);
freesearchargs(searchargs);
return;
}
if (charset == -1) {
prot_printf(imapd_out, "%s NO %s\r\n", tag,
error_message(IMAP_UNRECOGNIZED_CHARSET));
}
else {
n = index_search(imapd_mailbox, searchargs, usinguid);
snprintf(mytime, sizeof(mytime), "%2.3f",
(clock() - start) / (double) CLOCKS_PER_SEC);
prot_printf(imapd_out, "%s OK %s (%d msgs in %s secs)\r\n", tag,
error_message(IMAP_OK_COMPLETED), n, mytime);
}
freesearchargs(searchargs);
}
void
cmd_sort(tag, usinguid)
char *tag;
int usinguid;
{
int c;
struct sortcrit *sortcrit = NULL;
static struct buf arg;
int charset = 0;
struct searchargs *searchargs;
clock_t start = clock();
char mytime[100];
int n;
c = getsortcriteria(tag, &sortcrit);
if (c == EOF) {
eatline(imapd_in, ' ');
freesortcrit(sortcrit);
return;
}
if (c != ' ') {
prot_printf(imapd_out, "%s BAD Missing charset in Sort\r\n",
tag);
eatline(imapd_in, c);
freesortcrit(sortcrit);
return;
}
c = getword(imapd_in, &arg);
if (c != ' ') {
prot_printf(imapd_out, "%s BAD Missing search criteria in Sort\r\n",
tag);
eatline(imapd_in, c);
freesortcrit(sortcrit);
return;
}
lcase(arg.s);
charset = charset_lookupname(arg.s);
if (charset == -1) {
prot_printf(imapd_out, "%s NO %s\r\n", tag,
error_message(IMAP_UNRECOGNIZED_CHARSET));
eatline(imapd_in, c);
freesortcrit(sortcrit);
return;
}
searchargs = (struct searchargs *)xzmalloc(sizeof(struct searchargs));
c = getsearchprogram(tag, searchargs, &charset, 0);
if (c == EOF) {
eatline(imapd_in, ' ');
freesearchargs(searchargs);
freesortcrit(sortcrit);
return;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') {
prot_printf(imapd_out,
"%s BAD Unexpected extra arguments to Sort\r\n", tag);
eatline(imapd_in, c);
freesearchargs(searchargs);
freesortcrit(sortcrit);
return;
}
n = index_sort(imapd_mailbox, sortcrit, searchargs, usinguid);
snprintf(mytime, sizeof(mytime), "%2.3f",
(clock() - start) / (double) CLOCKS_PER_SEC);
prot_printf(imapd_out, "%s OK %s (%d msgs in %s secs)\r\n", tag,
error_message(IMAP_OK_COMPLETED), n, mytime);
freesortcrit(sortcrit);
freesearchargs(searchargs);
return;
}
void
cmd_thread(tag, usinguid)
char *tag;
int usinguid;
{
static struct buf arg;
int c;
int charset = 0;
int alg;
struct searchargs *searchargs;
clock_t start = clock();
char mytime[100];
int n;
c = getword(imapd_in, &arg);
if (c != ' ') {
prot_printf(imapd_out, "%s BAD Missing algorithm in Thread\r\n", tag);
eatline(imapd_in, c);
return;
}
if ((alg = find_thread_algorithm(arg.s)) == -1) {
prot_printf(imapd_out, "%s BAD Invalid Thread algorithm %s\r\n",
tag, arg.s);
eatline(imapd_in, c);
return;
}
c = getword(imapd_in, &arg);
if (c != ' ') {
prot_printf(imapd_out, "%s BAD Missing charset in Thread\r\n",
tag);
eatline(imapd_in, c);
return;
}
lcase(arg.s);
charset = charset_lookupname(arg.s);
if (charset == -1) {
prot_printf(imapd_out, "%s NO %s\r\n", tag,
error_message(IMAP_UNRECOGNIZED_CHARSET));
eatline(imapd_in, c);
return;
}
searchargs = (struct searchargs *)xzmalloc(sizeof(struct searchargs));
c = getsearchprogram(tag, searchargs, &charset, 0);
if (c == EOF) {
eatline(imapd_in, ' ');
freesearchargs(searchargs);
return;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') {
prot_printf(imapd_out,
"%s BAD Unexpected extra arguments to Thread\r\n", tag);
eatline(imapd_in, c);
freesearchargs(searchargs);
return;
}
n = index_thread(imapd_mailbox, alg, searchargs, usinguid);
snprintf(mytime, sizeof(mytime), "%2.3f",
(clock() - start) / (double) CLOCKS_PER_SEC);
prot_printf(imapd_out, "%s OK %s (%d msgs in %s secs)\r\n", tag,
error_message(IMAP_OK_COMPLETED), n, mytime);
freesearchargs(searchargs);
return;
}
void
cmd_copy(tag, sequence, name, usinguid)
char *tag;
char *sequence;
char *name;
int usinguid;
{
int r;
char mailboxname[MAX_MAILBOX_NAME+1];
char *copyuid;
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
if (!r) {
r = index_copy(imapd_mailbox, sequence, usinguid, mailboxname,
©uid);
}
index_check(imapd_mailbox, usinguid, 0);
if (r) {
prot_printf(imapd_out, "%s NO %s%s\r\n", tag,
(r == IMAP_MAILBOX_NONEXISTENT &&
mboxlist_createmailboxcheck(mailboxname, 0, 0,
imapd_userisadmin,
imapd_userid, imapd_authstate,
(char **)0, (char **)0) == 0)
? "[TRYCREATE] " : "", error_message(r));
}
else {
if (copyuid) {
prot_printf(imapd_out, "%s OK [COPYUID %s] %s\r\n", tag,
copyuid, error_message(IMAP_OK_COMPLETED));
free(copyuid);
}
else if (usinguid) {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
else {
prot_printf(imapd_out, "%s NO %s\r\n", tag,
error_message(IMAP_NO_NOSUCHMSG));
}
}
}
void
cmd_expunge(tag, sequence)
char *tag;
char *sequence;
{
int r;
if (!(imapd_mailbox->myrights & ACL_REMOVE)) r = IMAP_PERMISSION_DENIED;
else if (sequence) {
r = mailbox_expunge(imapd_mailbox, 1, index_expungeuidlist, sequence);
}
else {
r = mailbox_expunge(imapd_mailbox, 1, (mailbox_decideproc_t *)0,
(void *)0);
}
index_check(imapd_mailbox, 0, 0);
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
}
else {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
}
void
cmd_create(char *tag, char *name, char *partition, int localonly)
{
int r = 0;
char mailboxname[MAX_MAILBOX_NAME+1];
int autocreatequota;
char *mbox_partition = NULL;
struct od_user_opts useropts;
if (partition && !imapd_userisadmin) {
r = IMAP_PERMISSION_DENIED;
}
if ( partition && imapd_userisadmin )
{
mbox_partition = partition;
}
if ( !mbox_partition )
{
odGetUserOpts( imapd_userid, &useropts );
if ( useropts.fAltDataLoc[ 0 ] != '\0' )
{
mbox_partition = useropts.fAltDataLoc;
}
}
if (name[0] && name[strlen(name)-1] == imapd_namespace.hier_sep) {
name[strlen(name)-1] = '\0';
}
if (!r) {
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
}
if (!r) {
r = mboxlist_createmailbox(mailboxname, 0, mbox_partition,
imapd_userisadmin,
imapd_userid, imapd_authstate,
localonly, localonly, 0);
if (r == IMAP_PERMISSION_DENIED && !strcasecmp(name, "INBOX") &&
(autocreatequota = config_getint(IMAPOPT_AUTOCREATEQUOTA))) {
r = mboxlist_createmailbox(mailboxname, 0,
mbox_partition, 1, imapd_userid,
imapd_authstate, 0, 0, 0);
if (!r && autocreatequota > 0) {
(void) mboxlist_setquota(mailboxname, autocreatequota, 0);
}
}
}
if (imapd_mailbox) {
index_check(imapd_mailbox, 0, 0);
}
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
}
else {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
}
static int delmbox(char *name,
int matchlen __attribute__((unused)),
int maycreate __attribute__((unused)),
void *rock __attribute__((unused)))
{
int r;
r = mboxlist_deletemailbox(name, imapd_userisadmin,
imapd_userid, imapd_authstate,
0, 0, 0);
if(r) {
prot_printf(imapd_out, "* NO delete %s: %s\r\n",
name, error_message(r));
}
return 0;
}
void cmd_delete(char *tag, char *name, int localonly)
{
int r;
char mailboxname[MAX_MAILBOX_NAME+1];
char *p;
int domainlen = 0;
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
if (!r) {
if (config_virtdomains && (p = strchr(mailboxname, '!')))
domainlen = p - mailboxname + 1;
r = mboxlist_deletemailbox(mailboxname, imapd_userisadmin,
imapd_userid, imapd_authstate, 1,
localonly, 0);
}
if (!r && !localonly &&
!strncmp(mailboxname+domainlen, "user.", 5) &&
!strchr(mailboxname+domainlen+5, '.')) {
int mailboxname_len = strlen(mailboxname);
p = mailboxname + mailboxname_len;
if (mailboxname_len < sizeof(mailboxname) - 3) {
strcpy(p, ".*");
}
mboxlist_findall(NULL, mailboxname, imapd_userisadmin, imapd_userid,
imapd_authstate, delmbox, NULL);
*p = '\0';
if ((!domainlen) ||
(domainlen+1 < (sizeof(mailboxname) - mailboxname_len))) {
if (domainlen) {
snprintf(p, (sizeof(mailboxname) - mailboxname_len), "@%.*s",
domainlen-1, mailboxname);
}
user_deletedata(mailboxname+domainlen+5, imapd_userid,
imapd_authstate, 1);
}
}
if (imapd_mailbox) {
index_check(imapd_mailbox, 0, 0);
}
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
}
else {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
}
struct renrock
{
int ol;
int nl;
int rename_user;
char *olduser, *newuser;
char *acl_olduser, *acl_newuser;
char *newmailboxname;
char *partition;
};
static int renmbox(char *name,
int matchlen __attribute__((unused)),
int maycreate __attribute__((unused)),
void *rock)
{
char oldextname[MAX_MAILBOX_NAME+1];
char newextname[MAX_MAILBOX_NAME+1];
struct renrock *text = (struct renrock *)rock;
int r;
if((text->nl + strlen(name + text->ol)) > MAX_MAILBOX_NAME)
return 0;
strcpy(text->newmailboxname + text->nl, name + text->ol);
r = mboxlist_renamemailbox(name, text->newmailboxname,
text->partition,
1, imapd_userid, imapd_authstate);
(*imapd_namespace.mboxname_toexternal)(&imapd_namespace,
name,
imapd_userid, oldextname);
(*imapd_namespace.mboxname_toexternal)(&imapd_namespace,
text->newmailboxname,
imapd_userid, newextname);
if(r) {
prot_printf(imapd_out, "* NO rename %s %s: %s\r\n",
oldextname, newextname, error_message(r));
if (RENAME_STOP_ON_ERROR) return r;
} else {
if (text->rename_user) {
user_copyquotaroot(name, text->newmailboxname);
user_renameacl(text->newmailboxname,
text->acl_olduser, text->acl_newuser);
}
annotatemore_rename(name, text->newmailboxname,
text->rename_user ? text->olduser : NULL,
text->newuser);
prot_printf(imapd_out, "* OK rename %s %s\r\n",
oldextname, newextname);
}
prot_flush(imapd_out);
return 0;
}
void cmd_rename(const char *tag,
char *oldname, char *newname, char *partition)
{
int r = 0;
char oldmailboxname[MAX_MAILBOX_NAME+3];
char newmailboxname[MAX_MAILBOX_NAME+2];
char oldextname[MAX_MAILBOX_NAME+1];
char newextname[MAX_MAILBOX_NAME+1];
int omlen, nmlen;
char *p;
int recursive_rename = 1;
int rename_user = 0;
char olduser[128], newuser[128];
char acl_olduser[128], acl_newuser[128];
if (partition && !imapd_userisadmin) {
r = IMAP_PERMISSION_DENIED;
}
if (!r)
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, oldname,
imapd_userid, oldmailboxname);
if (!r)
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, newname,
imapd_userid, newmailboxname);
if (!strcasecmp(oldname, "inbox")) {
recursive_rename = 0;
}
else if (config_getswitch(IMAPOPT_ALLOWUSERMOVES) &&
mboxname_isusermailbox(oldmailboxname, 1) &&
mboxname_isusermailbox(newmailboxname, 1) &&
strcmp(oldmailboxname, newmailboxname) &&
imapd_userisadmin) {
rename_user = 1;
}
omlen = strlen(oldmailboxname);
nmlen = strlen(newmailboxname);
if (omlen < nmlen) {
if (!strncmp(oldmailboxname, newmailboxname, omlen) &&
newmailboxname[omlen] == '.') {
recursive_rename = 0;
}
} else {
if (!strncmp(oldmailboxname, newmailboxname, nmlen) &&
oldmailboxname[nmlen] == '.') {
recursive_rename = 0;
}
}
if (!strcasecmp(oldname, newname)) {
r = IMAP_MAILBOX_CASE_CHANGE_NOT_SUPPORTED;
}
for (p = oldmailboxname; !r && *p; p++) {
if (*p == '*' || *p == '%') r = IMAP_MAILBOX_BADNAME;
}
if (!r) {
r = mboxlist_renamemailbox(oldmailboxname, newmailboxname, partition,
imapd_userisadmin,
imapd_userid, imapd_authstate);
}
if (!r && rename_user) {
char *domain;
domain = strchr(oldmailboxname, '!');
strcpy(olduser, domain ? domain+6 : oldmailboxname+5);
if (domain)
sprintf(olduser+strlen(olduser), "@%.*s",
domain - oldmailboxname, oldmailboxname);
strcpy(acl_olduser, olduser);
mboxname_hiersep_toexternal(&imapd_namespace, acl_olduser,
config_virtdomains ?
strcspn(acl_olduser, "@") : 0);
domain = strchr(newmailboxname, '!');
strcpy(newuser, domain ? domain+6 : newmailboxname+5);
if (domain)
sprintf(newuser+strlen(newuser), "@%.*s",
domain - newmailboxname, newmailboxname);
strcpy(acl_newuser, newuser);
mboxname_hiersep_toexternal(&imapd_namespace, acl_newuser,
config_virtdomains ?
strcspn(acl_newuser, "@") : 0);
user_copyquotaroot(oldmailboxname, newmailboxname);
user_renameacl(newmailboxname, acl_olduser, acl_newuser);
user_renamedata(olduser, newuser, imapd_userid, imapd_authstate);
}
if (!r) {
annotatemore_rename(oldmailboxname, newmailboxname,
rename_user ? olduser : NULL,
newuser);
}
if (!r && recursive_rename) {
struct renrock rock;
int ol = omlen + 1;
int nl = nmlen + 1;
(*imapd_namespace.mboxname_toexternal)(&imapd_namespace,
oldmailboxname,
imapd_userid, oldextname);
(*imapd_namespace.mboxname_toexternal)(&imapd_namespace,
newmailboxname,
imapd_userid, newextname);
prot_printf(imapd_out, "* OK rename %s %s\r\n",
oldextname, newextname);
prot_flush(imapd_out);
strcat(oldmailboxname, ".*");
strcat(newmailboxname, ".");
rock.newmailboxname = newmailboxname;
rock.ol = ol;
rock.nl = nl;
rock.olduser = olduser;
rock.newuser = newuser;
rock.acl_olduser = acl_olduser;
rock.acl_newuser = acl_newuser;
rock.partition = partition;
rock.rename_user = rename_user;
r = mboxlist_findall(NULL, oldmailboxname, 1, imapd_userid,
imapd_authstate, renmbox, &rock);
}
if (!r && rename_user)
user_deletedata(olduser, imapd_userid, imapd_authstate, 1);
if (imapd_mailbox) {
index_check(imapd_mailbox, 0, 0);
}
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
} else {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
}
void
cmd_reconstruct(const char *tag, const char *name, int recursive)
{
int r = 0;
char mailboxname[MAX_MAILBOX_NAME+1];
char quotaroot[MAX_MAILBOX_NAME+1];
struct mailbox mailbox;
if (!imapd_userisadmin) {
r = IMAP_PERMISSION_DENIED;
}
if (!r) {
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
}
if (!r) {
r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, NULL, NULL);
}
if (r == IMAP_MAILBOX_MOVED) return;
if (!r) {
int pid;
pid = fork();
if(pid == -1) {
r = IMAP_SYS_ERROR;
} else if(pid == 0) {
char buf[4096];
int ret;
syslog(LOG_NOTICE, "Reconstructing '%s' (%s) for user '%s'",
mailboxname, recursive ? "recursive" : "not recursive",
imapd_userid);
fclose(stdin);
fclose(stdout);
fclose(stderr);
ret = snprintf(buf, sizeof(buf), "%s/reconstruct", SERVICE_PATH);
if(ret < 0 || ret >= sizeof(buf)) {
fatal("reconstruct buffer not sufficiently big", EC_CONFIG);
}
if(recursive) {
execl(buf, buf, "-C", config_filename, "-r", "-f",
mailboxname, NULL);
} else {
execl(buf, buf, "-C", config_filename, mailboxname, NULL);
}
exit(-1);
} else {
int status;
if(waitpid(pid, &status, 0) < 0) r = IMAP_SYS_ERROR;
if(WEXITSTATUS(status) != 0) r = IMAP_SYS_ERROR;
}
}
if (!r) {
r = mailbox_open_header(mailboxname, imapd_authstate, &mailbox);
}
if(!r) {
if(mailbox.quota.root) {
strcpy(quotaroot, mailbox.quota.root);
} else {
strcpy(quotaroot, mailboxname);
}
mailbox_close(&mailbox);
}
if (!r) {
int pid;
pid = fork();
if(pid == -1) {
r = IMAP_SYS_ERROR;
} else if(pid == 0) {
char buf[4096];
int ret;
syslog(LOG_NOTICE,
"Regenerating quota roots starting with '%s' for user '%s'",
mailboxname, imapd_userid);
fclose(stdin);
fclose(stdout);
fclose(stderr);
ret = snprintf(buf, sizeof(buf), "%s/quota", SERVICE_PATH);
if(ret < 0 || ret >= sizeof(buf)) {
fatal("quota buffer not sufficiently big", EC_CONFIG);
}
execl(buf, buf, "-C", config_filename, "-f", quotaroot, NULL);
exit(-1);
} else {
int status;
if(waitpid(pid, &status, 0) < 0) r = IMAP_SYS_ERROR;
if(WEXITSTATUS(status) != 0) r = IMAP_SYS_ERROR;
}
}
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
} else {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
}
void
cmd_find(tag, namespace, pattern)
char *tag;
char *namespace;
char *pattern;
{
char *p;
lcase(namespace);
for (p = pattern; *p; p++) {
if (*p == '%') *p = '?';
}
mboxname_hiersep_tointernal(&imapd_namespace, pattern,
config_virtdomains ?
strcspn(pattern, "@") : 0);
if (!strcasecmp(namespace, "mailboxes")) {
int force = config_getswitch(IMAPOPT_ALLOWALLSUBSCRIBE);
(*imapd_namespace.mboxlist_findsub)(&imapd_namespace, pattern,
imapd_userisadmin, imapd_userid,
imapd_authstate, mailboxdata,
NULL, force);
}
else if (!strcasecmp(namespace, "all.mailboxes")) {
(*imapd_namespace.mboxlist_findall)(&imapd_namespace, pattern,
imapd_userisadmin, imapd_userid,
imapd_authstate, mailboxdata, NULL);
}
else if (!strcasecmp(namespace, "bboards")
|| !strcasecmp(namespace, "all.bboards")) {
;
}
else {
prot_printf(imapd_out, "%s BAD Invalid FIND subcommand\r\n", tag);
return;
}
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
static int mstringdatacalls;
void cmd_list(char *tag, int listopts, char *reference, char *pattern)
{
char *buf = NULL;
int patlen = 0;
int reflen = 0;
static int ignorereference = 0;
clock_t start = clock();
char mytime[100];
int (*findall)(struct namespace *namespace,
const char *pattern, int isadmin, char *userid,
struct auth_state *auth_state, int (*proc)(),
void *rock);
int (*findsub)(struct namespace *namespace,
const char *pattern, int isadmin, char *userid,
struct auth_state *auth_state, int (*proc)(),
void *rock, int force);
if (ignorereference == 0) {
ignorereference = config_getswitch(IMAPOPT_IGNOREREFERENCE);
}
mstringdata(NULL, NULL, 0, 0, 0);
if (!pattern[0] && !(listopts & LIST_LSUB)) {
prot_printf(imapd_out, "* LIST (\\Noselect) \"%c\" \"\"\r\n",
imapd_namespace.hier_sep);
} else {
if (!ignorereference || pattern[0] == imapd_namespace.hier_sep) {
patlen = strlen(pattern);
reflen = strlen(reference);
buf = xmalloc(patlen + reflen + 1);
buf[0] = '\0';
if (*reference) {
if (reference[reflen-1] == imapd_namespace.hier_sep &&
pattern[0] == imapd_namespace.hier_sep) {
reference[--reflen] = '\0';
}
strcpy(buf, reference);
}
strcat(buf, pattern);
pattern = buf;
}
mboxname_hiersep_tointernal(&imapd_namespace, pattern,
config_virtdomains ?
strcspn(pattern, "@") : 0);
if (!strcmp(pattern, "*")
&& config_getswitch(IMAPOPT_FOOLSTUPIDCLIENTS)) {
if (buf) free(buf);
buf = xstrdup("INBOX*");
pattern = buf;
findsub = mboxlist_findsub;
findall = mboxlist_findall;
}
else {
findsub = imapd_namespace.mboxlist_findsub;
findall = imapd_namespace.mboxlist_findall;
}
if (listopts & (LIST_LSUB | LIST_SUBSCRIBED)) {
int force = config_getswitch(IMAPOPT_ALLOWALLSUBSCRIBE);
(*findsub)(&imapd_namespace, pattern,
imapd_userisadmin, imapd_userid, imapd_authstate,
listdata, &listopts, force);
}
else {
(*findall)(&imapd_namespace, pattern,
imapd_userisadmin, imapd_userid, imapd_authstate,
listdata, &listopts);
}
listdata((char *)0, 0, 0, &listopts);
if (buf) free(buf);
}
snprintf(mytime, sizeof(mytime), "%2.3f",
(clock() - start) / (double) CLOCKS_PER_SEC);
prot_printf(imapd_out, "%s OK %s (%s secs %d calls)\r\n", tag,
error_message(IMAP_OK_COMPLETED), mytime, mstringdatacalls);
}
void cmd_changesub(char *tag, char *namespace,
char *name, int add)
{
int r;
char mailboxname[MAX_MAILBOX_NAME+1];
int force = config_getswitch(IMAPOPT_ALLOWALLSUBSCRIBE);
if (namespace) lcase(namespace);
if (!namespace || !strcmp(namespace, "mailbox")) {
int len = strlen(name);
if (force && imapd_namespace.isalt &&
(((len == strlen(imapd_namespace.prefix[NAMESPACE_USER]) - 1) &&
!strncmp(name, imapd_namespace.prefix[NAMESPACE_USER], len)) ||
((len == strlen(imapd_namespace.prefix[NAMESPACE_SHARED]) - 1) &&
!strncmp(name, imapd_namespace.prefix[NAMESPACE_SHARED], len)))) {
r = 0;
}
else {
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
if (!r) {
r = mboxlist_changesub(mailboxname, imapd_userid,
imapd_authstate, add, force);
}
}
}
else if (!strcmp(namespace, "bboard")) {
r = add ? IMAP_MAILBOX_NONEXISTENT : 0;
}
else {
prot_printf(imapd_out, "%s BAD Invalid %s subcommand\r\n", tag,
add ? "Subscribe" : "Unsubscribe");
return;
}
if (r) {
prot_printf(imapd_out, "%s NO %s: %s\r\n", tag,
add ? "Subscribe" : "Unsubscribe", error_message(r));
}
else {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
}
void cmd_getacl(const char *tag, const char *name)
{
char mailboxname[MAX_MAILBOX_NAME+1];
int r, access;
char *acl;
char *rights, *nextid;
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
if (!r) {
r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, &acl, NULL);
}
if (r == IMAP_MAILBOX_MOVED) return;
if (!r) {
access = cyrus_acl_myrights(imapd_authstate, acl);
if (!(access & (ACL_READ|ACL_ADMIN)) &&
!imapd_userisadmin &&
!mboxname_userownsmailbox(imapd_userid, mailboxname)) {
r = (access&ACL_LOOKUP) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
}
}
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
return;
}
prot_printf(imapd_out, "* ACL ");
printastring(name);
while (acl) {
rights = strchr(acl, '\t');
if (!rights) break;
*rights++ = '\0';
nextid = strchr(rights, '\t');
if (!nextid) break;
*nextid++ = '\0';
prot_printf(imapd_out, " ");
printastring(acl);
prot_printf(imapd_out, " ");
printastring(rights);
acl = nextid;
}
prot_printf(imapd_out, "\r\n");
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
void
cmd_listrights(tag, name, identifier)
char *tag;
char *name;
char *identifier;
{
char mailboxname[MAX_MAILBOX_NAME+1];
int r, rights;
char *acl;
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
if (!r) {
r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, &acl, NULL);
}
if (r == IMAP_MAILBOX_MOVED) return;
if (!r) {
rights = cyrus_acl_myrights(imapd_authstate, acl);
if (!rights && !imapd_userisadmin &&
!mboxname_userownsmailbox(imapd_userid, mailboxname)) {
r = IMAP_MAILBOX_NONEXISTENT;
}
}
if (!r) {
struct auth_state *authstate = auth_newstate(identifier);
char *canon_identifier;
int canonidlen = 0;
int implicit;
char rightsdesc[100], optional[33];
if (global_authisa(authstate, IMAPOPT_ADMINS))
canon_identifier = identifier;
else
canon_identifier = canonify_userid(identifier, imapd_userid, NULL);
auth_freestate(authstate);
if (canon_identifier) canonidlen = strlen(canon_identifier);
if (!canon_identifier) {
implicit = 0;
}
else if (mboxname_userownsmailbox(canon_identifier, mailboxname)) {
implicit = config_implicitrights;
}
else if (mboxname_isusermailbox(mailboxname, 1)) {
implicit = ACL_POST;
}
else {
implicit = 0;
}
cyrus_acl_masktostr(implicit ^ (canon_identifier ? ACL_FULL : 0),
optional);
if (implicit) {
cyrus_acl_masktostr(implicit, rightsdesc);
}
else {
strcpy(rightsdesc, "\"\"");
}
if (*optional) {
int i, n = strlen(optional);
char *p = rightsdesc + strlen(rightsdesc);
for (i = 0; i < n; i++) {
*p++ = ' ';
*p++ = optional[i];
}
*p = '\0';
}
prot_printf(imapd_out, "* LISTRIGHTS ");
printastring(name);
prot_putc(' ', imapd_out);
printastring(identifier);
prot_printf(imapd_out, " %s", rightsdesc);
prot_printf(imapd_out, "\r\n%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
return;
}
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
}
void cmd_myrights(const char *tag, const char *name)
{
char mailboxname[MAX_MAILBOX_NAME+1];
int r, rights = 0;
char *acl;
char str[ACL_MAXSTR];
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
if (!r) {
r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, &acl, NULL);
}
if (r == IMAP_MAILBOX_MOVED) return;
if (!r) {
rights = cyrus_acl_myrights(imapd_authstate, acl);
if (imapd_userisadmin) {
rights |= ACL_LOOKUP|ACL_ADMIN;
}
else if (mboxname_userownsmailbox(imapd_userid, mailboxname)) {
rights |= config_implicitrights;
}
if (!rights) {
r = IMAP_MAILBOX_NONEXISTENT;
}
}
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
return;
}
prot_printf(imapd_out, "* MYRIGHTS ");
printastring(name);
prot_printf(imapd_out, " ");
printastring(cyrus_acl_masktostr(rights, str));
prot_printf(imapd_out, "\r\n%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
void cmd_setacl(const char *tag, const char *name,
const char *identifier, const char *rights)
{
int r;
char mailboxname[MAX_MAILBOX_NAME+1];
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
if (!r) {
r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, NULL, NULL);
}
if (r == IMAP_MAILBOX_MOVED) return;
if (!r) {
r = mboxlist_setacl(mailboxname, identifier, rights,
imapd_userisadmin, imapd_userid, imapd_authstate);
}
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
return;
}
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
void
cmd_getquota(const char *tag, const char *name)
{
int r;
struct quota quota;
char mailboxname[MAX_MAILBOX_NAME+1];
if (!imapd_userisadmin) r = IMAP_PERMISSION_DENIED;
else {
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
if (!r) {
quota.root = mailboxname;
r = quota_read("a, NULL, 0);
}
}
if (!r) {
prot_printf(imapd_out, "* QUOTA ");
printastring(name);
prot_printf(imapd_out, " (");
if (quota.limit >= 0) {
prot_printf(imapd_out, "STORAGE %lu %d",
quota.used/QUOTA_UNITS, quota.limit);
}
prot_printf(imapd_out, ")\r\n");
}
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
return;
}
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
void
cmd_getquotaroot(const char *tag, const char *name)
{
char mailboxname[MAX_MAILBOX_NAME+1];
struct mailbox mailbox;
int r;
int doclose = 0;
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
if (!r) {
r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, NULL, NULL);
}
if (r == IMAP_MAILBOX_MOVED) return;
if (!r) {
r = mailbox_open_header(mailboxname, imapd_authstate, &mailbox);
}
if (!r) {
doclose = 1;
if (!imapd_userisadmin && !(mailbox.myrights & ACL_READ)) {
r = (mailbox.myrights & ACL_LOOKUP) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
}
}
if (!r) {
prot_printf(imapd_out, "* QUOTAROOT ");
printastring(name);
if (mailbox.quota.root) {
(*imapd_namespace.mboxname_toexternal)(&imapd_namespace,
mailbox.quota.root,
imapd_userid, mailboxname);
prot_printf(imapd_out, " ");
printastring(mailboxname);
r = quota_read(&mailbox.quota, NULL, 0);
if (!r) {
prot_printf(imapd_out, "\r\n* QUOTA ");
printastring(mailboxname);
prot_printf(imapd_out, " (");
if (mailbox.quota.limit >= 0) {
prot_printf(imapd_out, "STORAGE %lu %d",
mailbox.quota.used/QUOTA_UNITS,
mailbox.quota.limit);
}
prot_putc(')', imapd_out);
}
}
prot_printf(imapd_out, "\r\n");
}
if (doclose) mailbox_close(&mailbox);
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
return;
}
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
void
cmd_setquota(const char *tag, const char *quotaroot)
{
int newquota = -1;
int badresource = 0;
int c;
int force = 0;
static struct buf arg;
char *p;
int r;
char mailboxname[MAX_MAILBOX_NAME+1];
c = prot_getc(imapd_in);
if (c != '(') goto badlist;
c = getword(imapd_in, &arg);
if (c != ')' || arg.s[0] != '\0') {
for (;;) {
if (c != ' ') goto badlist;
if (strcasecmp(arg.s, "storage") != 0) badresource = 1;
c = getword(imapd_in, &arg);
if (c != ' ' && c != ')') goto badlist;
if (arg.s[0] == '\0') goto badlist;
newquota = 0;
for (p = arg.s; *p; p++) {
if (!isdigit((int) *p)) goto badlist;
newquota = newquota * 10 + *p - '0';
if (newquota < 0) goto badlist;
}
if (c == ')') break;
}
}
c = prot_getc(imapd_in);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') {
prot_printf(imapd_out, "%s BAD Unexpected extra arguments to SETQUOTA\r\n", tag);
eatline(imapd_in, c);
return;
}
if (badresource) r = IMAP_UNSUPPORTED_QUOTA;
else if (!imapd_userisadmin && !imapd_userisproxyadmin) {
r = IMAP_PERMISSION_DENIED;
} else {
if(quotaroot[0] == '+') {
force = 1;
quotaroot++;
}
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, quotaroot,
imapd_userid, mailboxname);
if (!r) {
r = mboxlist_setquota(mailboxname, newquota, force);
}
}
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
return;
}
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
return;
badlist:
prot_printf(imapd_out, "%s BAD Invalid quota list in Setquota\r\n", tag);
eatline(imapd_in, c);
}
#ifdef HAVE_SSL
void cmd_starttls(char *tag, int imaps)
{
int result;
int *layerp;
char *auth_id;
sasl_ssf_t ssf;
layerp = (int *) &ssf;
if (imapd_starttls_done == 1)
{
prot_printf(imapd_out, "%s NO TLS already active\r\n", tag);
return;
}
result=tls_init_serverengine("imap",
5,
!imaps,
!imaps);
if (result == -1) {
syslog(LOG_ERR, "error initializing TLS");
if (imaps == 0) {
prot_printf(imapd_out, "%s NO Error initializing TLS\r\n", tag);
} else {
fatal("tls_init() failed", EC_CONFIG);
}
return;
}
if (imaps == 0)
{
prot_printf(imapd_out, "%s OK Begin TLS negotiation now\r\n", tag);
prot_flush(imapd_out);
}
result=tls_start_servertls(0,
1,
layerp,
&auth_id,
&tls_conn);
if (result==-1) {
if (imaps == 0) {
prot_printf(imapd_out, "%s NO Starttls negotiation failed\r\n",
tag);
syslog(LOG_NOTICE, "STARTTLS negotiation failed: %s",
imapd_clienthost);
return;
} else {
syslog(LOG_NOTICE, "imaps TLS negotiation failed: %s",
imapd_clienthost);
fatal("tls_start_servertls() failed", EC_TEMPFAIL);
return;
}
}
result = sasl_setprop(imapd_saslconn, SASL_SSF_EXTERNAL, &ssf);
if (result != SASL_OK) {
fatal("sasl_setprop() failed: cmd_starttls()", EC_TEMPFAIL);
}
saslprops.ssf = ssf;
result = sasl_setprop(imapd_saslconn, SASL_AUTH_EXTERNAL, auth_id);
if (result != SASL_OK) {
fatal("sasl_setprop() failed: cmd_starttls()", EC_TEMPFAIL);
}
if(saslprops.authid) {
free(saslprops.authid);
saslprops.authid = NULL;
}
if(auth_id)
saslprops.authid = xstrdup(auth_id);
prot_settls(imapd_in, tls_conn);
prot_settls(imapd_out, tls_conn);
imapd_starttls_done = 1;
}
#else
void cmd_starttls(char *tag, int imaps)
{
fatal("cmd_starttls() executed, but starttls isn't implemented!",
EC_SOFTWARE);
}
#endif
void
cmd_status(tag, name)
char *tag;
char *name;
{
int c;
int statusitems = 0;
static struct buf arg;
struct mailbox mailbox;
char mailboxname[MAX_MAILBOX_NAME+1];
int r = 0;
int doclose = 0;
c = prot_getc(imapd_in);
if (c != '(') goto badlist;
c = getword(imapd_in, &arg);
if (arg.s[0] == '\0') goto badlist;
for (;;) {
lcase(arg.s);
if (!strcmp(arg.s, "messages")) {
statusitems |= STATUS_MESSAGES;
}
else if (!strcmp(arg.s, "recent")) {
statusitems |= STATUS_RECENT;
}
else if (!strcmp(arg.s, "uidnext")) {
statusitems |= STATUS_UIDNEXT;
}
else if (!strcmp(arg.s, "uidvalidity")) {
statusitems |= STATUS_UIDVALIDITY;
}
else if (!strcmp(arg.s, "unseen")) {
statusitems |= STATUS_UNSEEN;
}
else {
prot_printf(imapd_out, "%s BAD Invalid Status attribute %s\r\n",
tag, arg.s);
eatline(imapd_in, c);
return;
}
if (c == ' ') c = getword(imapd_in, &arg);
else break;
}
if (c != ')') {
prot_printf(imapd_out,
"%s BAD Missing close parenthesis in Status\r\n", tag);
eatline(imapd_in, c);
return;
}
c = prot_getc(imapd_in);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') {
prot_printf(imapd_out,
"%s BAD Unexpected extra arguments to Status\r\n", tag);
eatline(imapd_in, c);
return;
}
if (imapd_mailbox) {
index_check(imapd_mailbox, 0, 1);
}
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
if (!r) {
r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, NULL, NULL);
}
if (r == IMAP_MAILBOX_MOVED) return;
if (!r) {
r = mailbox_open_header(mailboxname, imapd_authstate, &mailbox);
}
if (!r) {
doclose = 1;
r = mailbox_open_index(&mailbox);
}
if (!r && !(mailbox.myrights & ACL_READ)) {
r = (imapd_userisadmin || (mailbox.myrights & ACL_LOOKUP)) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
}
if (!r) {
r = index_status(&mailbox, name, statusitems);
}
if (doclose) mailbox_close(&mailbox);
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
return;
}
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
return;
badlist:
prot_printf(imapd_out, "%s BAD Invalid status list in Status\r\n", tag);
eatline(imapd_in, c);
}
#ifdef ENABLE_X_NETSCAPE_HACK
void cmd_netscrape(char *tag)
{
const char *url;
url = config_getstring(IMAPOPT_NETSCAPEURL);
prot_printf(imapd_out,
"* OK [NETSCAPE] Carnegie Mellon Cyrus IMAP\r\n"
"* VERSION %s\r\n",
CYRUS_VERSION);
if (url) prot_printf(imapd_out, "* ACCOUNT-URL %s\r\n", url);
prot_printf(imapd_out, "%s OK %s\r\n",
tag, error_message(IMAP_OK_COMPLETED));
}
#endif
static int namespacedata(char *name,
int matchlen __attribute__((unused)),
int maycreate __attribute__((unused)),
void *rock)
{
int* sawone = (int*) rock;
if (!name) {
return 0;
}
if (!(strncmp(name, "INBOX.", 6))) {
sawone[NAMESPACE_INBOX] = 1;
} else if (mboxname_isusermailbox(name, 0)) {
sawone[NAMESPACE_USER] = 1;
} else {
sawone[NAMESPACE_SHARED] = 1;
}
return 0;
}
void cmd_namespace(tag)
char* tag;
{
int sawone[3] = {0, 0, 0};
char* pattern;
if (SLEEZY_NAMESPACE) {
char inboxname[MAX_MAILBOX_NAME+1];
if (strlen(imapd_userid) + 5 > MAX_MAILBOX_NAME)
sawone[NAMESPACE_INBOX] = 0;
else {
(*imapd_namespace.mboxname_tointernal)(&imapd_namespace, "INBOX",
imapd_userid, inboxname);
sawone[NAMESPACE_INBOX] =
!mboxlist_lookup(inboxname, NULL, NULL, NULL);
}
sawone[NAMESPACE_USER] = 1;
sawone[NAMESPACE_SHARED] = 1;
} else {
pattern = xstrdup("%");
mboxlist_findall(NULL, pattern, imapd_userisadmin, imapd_userid,
imapd_authstate, namespacedata, (void*) sawone);
free(pattern);
}
prot_printf(imapd_out, "* NAMESPACE");
if (sawone[NAMESPACE_INBOX]) {
prot_printf(imapd_out, " ((\"%s\" \"%c\"))",
imapd_namespace.prefix[NAMESPACE_INBOX],
imapd_namespace.hier_sep);
} else {
prot_printf(imapd_out, " NIL");
}
if (sawone[NAMESPACE_USER]) {
prot_printf(imapd_out, " ((\"%s\" \"%c\"))",
imapd_namespace.prefix[NAMESPACE_USER],
imapd_namespace.hier_sep);
} else {
prot_printf(imapd_out, " NIL");
}
if (sawone[NAMESPACE_SHARED]) {
prot_printf(imapd_out, " ((\"%s\" \"%c\"))",
imapd_namespace.prefix[NAMESPACE_SHARED],
imapd_namespace.hier_sep);
} else {
prot_printf(imapd_out, " NIL");
}
prot_printf(imapd_out, "\r\n");
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
int getannotatefetchdata(char *tag,
struct strlist **entries, struct strlist **attribs)
{
int c;
static struct buf arg;
*entries = *attribs = NULL;
c = prot_getc(imapd_in);
if (c == EOF) {
prot_printf(imapd_out,
"%s BAD Missing annotation entry\r\n", tag);
goto baddata;
}
else if (c == '(') {
do {
c = getqstring(imapd_in, imapd_out, &arg);
if (c == EOF) {
prot_printf(imapd_out,
"%s BAD Missing annotation entry\r\n", tag);
goto baddata;
}
appendstrlist(entries, arg.s);
} while (c == ' ');
if (c != ')') {
prot_printf(imapd_out,
"%s BAD Missing close paren in annotation entry list \r\n",
tag);
goto baddata;
}
c = prot_getc(imapd_in);
}
else {
prot_ungetc(c, imapd_in);
c = getqstring(imapd_in, imapd_out, &arg);
if (c == EOF) {
prot_printf(imapd_out,
"%s BAD Missing annotation entry\r\n", tag);
goto baddata;
}
appendstrlist(entries, arg.s);
}
if (c != ' ' || (c = prot_getc(imapd_in)) == EOF) {
prot_printf(imapd_out,
"%s BAD Missing annotation attribute(s)\r\n", tag);
goto baddata;
}
if (c == '(') {
do {
c = getnstring(imapd_in, imapd_out, &arg);
if (c == EOF) {
prot_printf(imapd_out,
"%s BAD Missing annotation attribute(s)\r\n", tag);
goto baddata;
}
appendstrlist(attribs, arg.s);
} while (c == ' ');
if (c != ')') {
prot_printf(imapd_out,
"%s BAD Missing close paren in "
"annotation attribute list\r\n", tag);
goto baddata;
}
c = prot_getc(imapd_in);
}
else {
prot_ungetc(c, imapd_in);
c = getqstring(imapd_in, imapd_out, &arg);
if (c == EOF) {
prot_printf(imapd_out,
"%s BAD Missing annotation attribute\r\n", tag);
goto baddata;
}
appendstrlist(attribs, arg.s);
}
return c;
baddata:
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
}
int getannotatestoredata(char *tag, struct entryattlist **entryatts)
{
int c, islist = 0;
static struct buf entry, attrib, value;
struct attvaluelist *attvalues = NULL;
*entryatts = NULL;
c = prot_getc(imapd_in);
if (c == EOF) {
prot_printf(imapd_out,
"%s BAD Missing annotation entry\r\n", tag);
goto baddata;
}
else if (c == '(') {
islist = 1;
}
else {
prot_ungetc(c, imapd_in);
}
do {
c = getqstring(imapd_in, imapd_out, &entry);
if (c == EOF) {
prot_printf(imapd_out,
"%s BAD Missing annotation entry\r\n", tag);
goto baddata;
}
if (c != ' ' || (c = prot_getc(imapd_in)) != '(') {
prot_printf(imapd_out,
"%s BAD Missing annotation attribute-values list\r\n",
tag);
goto baddata;
}
do {
c = getqstring(imapd_in, imapd_out, &attrib);
if (c == EOF) {
prot_printf(imapd_out,
"%s BAD Missing annotation attribute\r\n", tag);
goto baddata;
}
if (c != ' ' ||
(c = getnstring(imapd_in, imapd_out, &value)) == EOF) {
prot_printf(imapd_out,
"%s BAD Missing annotation value\r\n", tag);
goto baddata;
}
appendattvalue(&attvalues, attrib.s, value.s);
} while (c == ' ');
if (c != ')') {
prot_printf(imapd_out,
"%s BAD Missing close paren in annotation "
"attribute-values list\r\n", tag);
goto baddata;
}
appendentryatt(entryatts, entry.s, attvalues);
attvalues = NULL;
c = prot_getc(imapd_in);
} while (c == ' ');
if (islist) {
if (c != ')') {
prot_printf(imapd_out,
"%s BAD Missing close paren in annotation entry list \r\n",
tag);
goto baddata;
}
c = prot_getc(imapd_in);
}
return c;
baddata:
if (attvalues) freeattvalues(attvalues);
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
}
void annotate_response(struct entryattlist *l)
{
int islist;
if (!l) return;
islist = (l->next != NULL);
if (islist) prot_printf(imapd_out, "(");
while (l) {
prot_printf(imapd_out, "\"%s\"", l->entry);
if (l->attvalues) {
struct attvaluelist *av = l->attvalues;
prot_printf(imapd_out, " (");
while (av) {
prot_printf(imapd_out, "\"%s\" ", av->attrib);
if (!strcasecmp(av->value, "NIL"))
prot_printf(imapd_out, "NIL");
else
prot_printf(imapd_out, "\"%s\"", av->value);
if ((av = av->next) == NULL)
prot_printf(imapd_out, ")");
else
prot_printf(imapd_out, " ");
}
}
if ((l = l->next) != NULL)
prot_printf(imapd_out, " ");
}
if (islist) prot_printf(imapd_out, ")");
}
void cmd_getannotation(char *tag, char *mboxpat)
{
int c, r = 0;
struct strlist *entries = NULL, *attribs = NULL;
c = getannotatefetchdata(tag, &entries, &attribs);
if (c == EOF) {
eatline(imapd_in, c);
return;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') {
prot_printf(imapd_out,
"%s BAD Unexpected extra arguments to Getannotation\r\n",
tag);
eatline(imapd_in, c);
goto freeargs;
}
r = annotatemore_fetch(mboxpat, entries, attribs, &imapd_namespace,
imapd_userisadmin || imapd_userisproxyadmin,
imapd_userid, imapd_authstate, imapd_out);
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
} else {
prot_printf(imapd_out, "%s OK %s\r\n",
tag, error_message(IMAP_OK_COMPLETED));
}
freeargs:
if (entries) freestrlist(entries);
if (attribs) freestrlist(attribs);
return;
}
void cmd_setannotation(char *tag, char *mboxpat)
{
int c, r = 0;
struct entryattlist *entryatts = NULL;
c = getannotatestoredata(tag, &entryatts);
if (c == EOF) {
eatline(imapd_in, c);
return;
}
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') {
prot_printf(imapd_out,
"%s BAD Unexpected extra arguments to Setannotation\r\n",
tag);
eatline(imapd_in, c);
goto freeargs;
}
r = annotatemore_store(mboxpat,
entryatts, &imapd_namespace, imapd_userisadmin,
imapd_userid, imapd_authstate);
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
} else {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
freeargs:
if (entryatts) freeentryatts(entryatts);
return;
}
int getsearchprogram(tag, searchargs, charset, parsecharset)
char *tag;
struct searchargs *searchargs;
int *charset;
int parsecharset;
{
int c;
do {
c = getsearchcriteria(tag, searchargs, charset, parsecharset);
parsecharset = 0;
} while (c == ' ');
return c;
}
int getsearchcriteria(tag, searchargs, charset, parsecharset)
char *tag;
struct searchargs *searchargs;
int *charset;
int parsecharset;
{
static struct buf criteria, arg;
struct searchargs *sub1, *sub2;
char *p, *str;
int c, flag;
unsigned size;
time_t start, end;
c = getword(imapd_in, &criteria);
lcase(criteria.s);
switch (criteria.s[0]) {
case '\0':
if (c != '(') goto badcri;
c = getsearchprogram(tag, searchargs, charset, 0);
if (c == EOF) return EOF;
if (c != ')') {
prot_printf(imapd_out, "%s BAD Missing required close paren in Search command\r\n",
tag);
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
}
c = prot_getc(imapd_in);
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '*':
if (imparse_issequence(criteria.s)) {
appendstrlist(&searchargs->sequence, criteria.s);
}
else goto badcri;
break;
case 'a':
if (!strcmp(criteria.s, "answered")) {
searchargs->system_flags_set |= FLAG_ANSWERED;
}
else if (!strcmp(criteria.s, "all")) {
break;
}
else goto badcri;
break;
case 'b':
if (!strcmp(criteria.s, "before")) {
if (c != ' ') goto missingarg;
c = getsearchdate(&start, &end);
if (c == EOF) goto baddate;
if (!searchargs->before || searchargs->before > start) {
searchargs->before = start;
}
}
else if (!strcmp(criteria.s, "bcc")) {
if (c != ' ') goto missingarg;
c = getastring(imapd_in, imapd_out, &arg);
if (c == EOF) goto missingarg;
str = charset_convert(arg.s, *charset, NULL, 0);
if (strchr(str, EMPTY)) {
searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
}
else {
appendstrlistpat(&searchargs->bcc, str);
}
}
else if (!strcmp(criteria.s, "body")) {
if (c != ' ') goto missingarg;
c = getastring(imapd_in, imapd_out, &arg);
if (c == EOF) goto missingarg;
str = charset_convert(arg.s, *charset, NULL, 0);
if (strchr(str, EMPTY)) {
searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
}
else {
appendstrlistpat(&searchargs->body, str);
}
}
else goto badcri;
break;
case 'c':
if (!strcmp(criteria.s, "cc")) {
if (c != ' ') goto missingarg;
c = getastring(imapd_in, imapd_out, &arg);
if (c == EOF) goto missingarg;
str = charset_convert(arg.s, *charset, NULL, 0);
if (strchr(str, EMPTY)) {
searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
}
else {
appendstrlistpat(&searchargs->cc, str);
}
}
else if (parsecharset && !strcmp(criteria.s, "charset")) {
if (c != ' ') goto missingarg;
c = getastring(imapd_in, imapd_out, &arg);
if (c != ' ') goto missingarg;
lcase(arg.s);
*charset = charset_lookupname(arg.s);
}
else goto badcri;
break;
case 'd':
if (!strcmp(criteria.s, "deleted")) {
searchargs->system_flags_set |= FLAG_DELETED;
}
else if (!strcmp(criteria.s, "draft")) {
searchargs->system_flags_set |= FLAG_DRAFT;
}
else goto badcri;
break;
case 'f':
if (!strcmp(criteria.s, "flagged")) {
searchargs->system_flags_set |= FLAG_FLAGGED;
}
else if (!strcmp(criteria.s, "from")) {
if (c != ' ') goto missingarg;
c = getastring(imapd_in, imapd_out, &arg);
if (c == EOF) goto missingarg;
str = charset_convert(arg.s, *charset, NULL, 0);
if (strchr(str, EMPTY)) {
searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
}
else {
appendstrlistpat(&searchargs->from, str);
}
}
else goto badcri;
break;
case 'h':
if (!strcmp(criteria.s, "header")) {
struct strlist **patlist;
if (c != ' ') goto missingarg;
c = getastring(imapd_in, imapd_out, &arg);
if (c != ' ') goto missingarg;
lcase(arg.s);
if (!strcmp(arg.s, "bcc")) {
patlist = &searchargs->bcc;
}
else if (!strcmp(arg.s, "cc")) {
patlist = &searchargs->cc;
}
else if (!strcmp(arg.s, "to")) {
patlist = &searchargs->to;
}
else if (!strcmp(arg.s, "from")) {
patlist = &searchargs->from;
}
else if (!strcmp(arg.s, "subject")) {
patlist = &searchargs->subject;
}
else if (!strcmp(arg.s, "message-id")) {
patlist = &searchargs->messageid;
}
else {
if (searchargs->cache_atleast < BIT32_MAX) {
bit32 this_ver =
mailbox_cached_header(arg.s);
if(this_ver > searchargs->cache_atleast)
searchargs->cache_atleast = this_ver;
}
appendstrlist(&searchargs->header_name, arg.s);
patlist = &searchargs->header;
}
c = getastring(imapd_in, imapd_out, &arg);
if (c == EOF) goto missingarg;
str = charset_convert(arg.s, *charset, NULL, 0);
if (strchr(str, EMPTY)) {
searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
}
else {
appendstrlistpat(patlist, str);
}
}
else goto badcri;
break;
case 'k':
if (!strcmp(criteria.s, "keyword")) {
if (c != ' ') goto missingarg;
c = getword(imapd_in, &arg);
if (!imparse_isatom(arg.s)) goto badflag;
lcase(arg.s);
for (flag=0; flag < MAX_USER_FLAGS; flag++) {
if (imapd_mailbox->flagname[flag] &&
!strcasecmp(imapd_mailbox->flagname[flag], arg.s)) break;
}
if (flag == MAX_USER_FLAGS) {
searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
break;
}
searchargs->user_flags_set[flag/32] |= 1<<(flag&31);
}
else goto badcri;
break;
case 'l':
if (!strcmp(criteria.s, "larger")) {
if (c != ' ') goto missingarg;
c = getword(imapd_in, &arg);
size = 0;
for (p = arg.s; *p && isdigit((int) *p); p++) {
size = size * 10 + *p - '0';
}
if (!arg.s || *p) goto badnumber;
if (size > searchargs->larger) searchargs->larger = size;
}
else goto badcri;
break;
case 'n':
if (!strcmp(criteria.s, "not")) {
if (c != ' ') goto missingarg;
sub1 = (struct searchargs *)xzmalloc(sizeof(struct searchargs));
c = getsearchcriteria(tag, sub1, charset, 0);
if (c == EOF) {
freesearchargs(sub1);
return EOF;
}
appendsearchargs(searchargs, sub1, (struct searchargs *)0);
}
else if (!strcmp(criteria.s, "new")) {
searchargs->flags |= (SEARCH_SEEN_UNSET|SEARCH_RECENT_SET);
}
else goto badcri;
break;
case 'o':
if (!strcmp(criteria.s, "or")) {
if (c != ' ') goto missingarg;
sub1 = (struct searchargs *)xzmalloc(sizeof(struct searchargs));
c = getsearchcriteria(tag, sub1, charset, 0);
if (c == EOF) {
freesearchargs(sub1);
return EOF;
}
if (c != ' ') goto missingarg;
sub2 = (struct searchargs *)xzmalloc(sizeof(struct searchargs));
c = getsearchcriteria(tag, sub2, charset, 0);
if (c == EOF) {
freesearchargs(sub1);
freesearchargs(sub2);
return EOF;
}
appendsearchargs(searchargs, sub1, sub2);
}
else if (!strcmp(criteria.s, "old")) {
searchargs->flags |= SEARCH_RECENT_UNSET;
}
else if (!strcmp(criteria.s, "on")) {
if (c != ' ') goto missingarg;
c = getsearchdate(&start, &end);
if (c == EOF) goto baddate;
if (!searchargs->before || searchargs->before > end) {
searchargs->before = end;
}
if (!searchargs->after || searchargs->after < start) {
searchargs->after = start;
}
}
else goto badcri;
break;
case 'r':
if (!strcmp(criteria.s, "recent")) {
searchargs->flags |= SEARCH_RECENT_SET;
}
else goto badcri;
break;
case 's':
if (!strcmp(criteria.s, "seen")) {
searchargs->flags |= SEARCH_SEEN_SET;
}
else if (!strcmp(criteria.s, "sentbefore")) {
if (c != ' ') goto missingarg;
c = getsearchdate(&start, &end);
if (c == EOF) goto baddate;
if (!searchargs->sentbefore || searchargs->sentbefore > start) {
searchargs->sentbefore = start;
}
}
else if (!strcmp(criteria.s, "senton")) {
if (c != ' ') goto missingarg;
c = getsearchdate(&start, &end);
if (c == EOF) goto baddate;
if (!searchargs->sentbefore || searchargs->sentbefore > end) {
searchargs->sentbefore = end;
}
if (!searchargs->sentafter || searchargs->sentafter < start) {
searchargs->sentafter = start;
}
}
else if (!strcmp(criteria.s, "sentsince")) {
if (c != ' ') goto missingarg;
c = getsearchdate(&start, &end);
if (c == EOF) goto baddate;
if (!searchargs->sentafter || searchargs->sentafter < start) {
searchargs->sentafter = start;
}
}
else if (!strcmp(criteria.s, "since")) {
if (c != ' ') goto missingarg;
c = getsearchdate(&start, &end);
if (c == EOF) goto baddate;
if (!searchargs->after || searchargs->after < start) {
searchargs->after = start;
}
}
else if (!strcmp(criteria.s, "smaller")) {
if (c != ' ') goto missingarg;
c = getword(imapd_in, &arg);
size = 0;
for (p = arg.s; *p && isdigit((int) *p); p++) {
size = size * 10 + *p - '0';
}
if (!arg.s || *p) goto badnumber;
if (size == 0) size = 1;
if (!searchargs->smaller || size < searchargs->smaller)
searchargs->smaller = size;
}
else if (!strcmp(criteria.s, "subject")) {
if (c != ' ') goto missingarg;
c = getastring(imapd_in, imapd_out, &arg);
if (c == EOF) goto missingarg;
str = charset_convert(arg.s, *charset, NULL, 0);
if (strchr(str, EMPTY)) {
searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
}
else {
appendstrlistpat(&searchargs->subject, str);
}
}
else goto badcri;
break;
case 't':
if (!strcmp(criteria.s, "to")) {
if (c != ' ') goto missingarg;
c = getastring(imapd_in, imapd_out, &arg);
if (c == EOF) goto missingarg;
str = charset_convert(arg.s, *charset, NULL, 0);
if (strchr(str, EMPTY)) {
searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
}
else {
appendstrlistpat(&searchargs->to, str);
}
}
else if (!strcmp(criteria.s, "text")) {
if (c != ' ') goto missingarg;
c = getastring(imapd_in, imapd_out, &arg);
if (c == EOF) goto missingarg;
str = charset_convert(arg.s, *charset, NULL, 0);
if (strchr(str, EMPTY)) {
searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
}
else {
appendstrlistpat(&searchargs->text, str);
}
}
else goto badcri;
break;
case 'u':
if (!strcmp(criteria.s, "uid")) {
if (c != ' ') goto missingarg;
c = getword(imapd_in, &arg);
if (!imparse_issequence(arg.s)) goto badcri;
appendstrlist(&searchargs->uidsequence, arg.s);
}
else if (!strcmp(criteria.s, "unseen")) {
searchargs->flags |= SEARCH_SEEN_UNSET;
}
else if (!strcmp(criteria.s, "unanswered")) {
searchargs->system_flags_unset |= FLAG_ANSWERED;
}
else if (!strcmp(criteria.s, "undeleted")) {
searchargs->system_flags_unset |= FLAG_DELETED;
}
else if (!strcmp(criteria.s, "undraft")) {
searchargs->system_flags_unset |= FLAG_DRAFT;
}
else if (!strcmp(criteria.s, "unflagged")) {
searchargs->system_flags_unset |= FLAG_FLAGGED;
}
else if (!strcmp(criteria.s, "unkeyword")) {
if (c != ' ') goto missingarg;
c = getword(imapd_in, &arg);
if (!imparse_isatom(arg.s)) goto badflag;
lcase(arg.s);
for (flag=0; flag < MAX_USER_FLAGS; flag++) {
if (imapd_mailbox->flagname[flag] &&
!strcasecmp(imapd_mailbox->flagname[flag], arg.s)) break;
}
if (flag != MAX_USER_FLAGS) {
searchargs->user_flags_unset[flag/32] |= 1<<(flag&31);
}
}
else goto badcri;
break;
default:
badcri:
prot_printf(imapd_out, "%s BAD Invalid Search criteria\r\n", tag);
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
}
return c;
missingarg:
prot_printf(imapd_out, "%s BAD Missing required argument to Search %s\r\n",
tag, criteria.s);
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
badflag:
prot_printf(imapd_out, "%s BAD Invalid flag name %s in Search command\r\n",
tag, arg.s);
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
baddate:
prot_printf(imapd_out, "%s BAD Invalid date in Search command\r\n", tag);
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
badnumber:
prot_printf(imapd_out, "%s BAD Invalid number in Search command\r\n", tag);
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
}
void cmd_dump(char *tag, char *name, int uid_start)
{
int r = 0;
char mailboxname[MAX_MAILBOX_NAME+1];
char *path, *acl;
if (!imapd_userisadmin) {
r = IMAP_PERMISSION_DENIED;
}
if (!r) {
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
}
if (!r) {
r = mlookup(tag, name, mailboxname, NULL, &path, NULL, &acl, NULL);
}
if (r == IMAP_MAILBOX_MOVED) return;
if(!r) {
r = dump_mailbox(tag, mailboxname, path, acl, uid_start, imapd_in,
imapd_out, imapd_authstate);
}
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
} else {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
}
void cmd_undump(char *tag, char *name)
{
int r = 0;
char mailboxname[MAX_MAILBOX_NAME+1];
char *path, *acl;
if (!imapd_userisadmin) {
r = IMAP_PERMISSION_DENIED;
}
if (!r) {
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
}
if (!r) {
r = mlookup(tag, name, mailboxname, NULL, &path, NULL, &acl, NULL);
}
if (r == IMAP_MAILBOX_MOVED) return;
if(!r) {
char *safe_path = xstrdup(path);
char *safe_acl = xstrdup(acl);
r = undump_mailbox(mailboxname, safe_path, safe_acl,
imapd_in, imapd_out,
imapd_authstate);
free(safe_path);
free(safe_acl);
}
if (r) {
prot_printf(imapd_out, "%s NO %s%s\r\n",
tag,
(r == IMAP_MAILBOX_NONEXISTENT &&
mboxlist_createmailboxcheck(mailboxname, 0, 0,
imapd_userisadmin,
imapd_userid, imapd_authstate,
NULL, NULL) == 0)
? "[TRYCREATE] " : "", error_message(r));
} else {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
}
static int getresult(struct protstream *p, char *tag)
{
char buf[4096];
char *str = (char *) buf;
while(1) {
if (!prot_fgets(str, sizeof(buf), p)) {
return IMAP_SERVER_UNAVAILABLE;
}
if (!strncmp(str, tag, strlen(tag))) {
str += strlen(tag);
if(!*str) {
return IMAP_SERVER_UNAVAILABLE;
}
str++;
if (!strncasecmp(str, "OK ", 3)) { return 0; }
if (!strncasecmp(str, "NO ", 3)) { return IMAP_REMOTE_DENIED; }
return IMAP_SERVER_UNAVAILABLE;
}
}
}
static int trashacl(struct protstream *pin, struct protstream *pout,
char *mailbox)
{
int i=0, j=0;
char tagbuf[128];
int c;
struct buf tag, cmd, tmp, user;
int r = 0;
memset(&tag, 0, sizeof(struct buf));
memset(&cmd, 0, sizeof(struct buf));
memset(&tmp, 0, sizeof(struct buf));
memset(&user, 0, sizeof(struct buf));
prot_printf(pout, "ACL0 GETACL {%d+}\r\n%s\r\n",
strlen(mailbox), mailbox);
while(1) {
c = getword(pin, &tag);
if (c == EOF) {
r = IMAP_SERVER_UNAVAILABLE;
break;
}
c = getword(pin, &cmd);
if (c == EOF) {
r = IMAP_SERVER_UNAVAILABLE;
break;
}
if(c == '\r') {
c = prot_getc(pin);
if(c != '\n') {
r = IMAP_SERVER_UNAVAILABLE;
goto cleanup;
}
}
if(c == '\n') goto cleanup;
if (tag.s[0] == '*' && !strncmp(cmd.s, "ACL", 3)) {
while(c != '\n') {
c = getastring(pin, pout, &tmp);
if (c == EOF) {
r = IMAP_SERVER_UNAVAILABLE;
goto cleanup;
}
if(c == '\r') {
c = prot_getc(pin);
if(c != '\n') {
r = IMAP_SERVER_UNAVAILABLE;
goto cleanup;
}
}
if(c == '\n') goto cleanup;
c = getastring(pin, pout, &user);
if (c == EOF) {
r = IMAP_SERVER_UNAVAILABLE;
goto cleanup;
}
snprintf(tagbuf, sizeof(tagbuf), "ACL%d", ++i);
prot_printf(pout, "%s DELETEACL {%d+}\r\n%s {%d+}\r\n%s\r\n",
tagbuf, strlen(mailbox), mailbox,
strlen(user.s), user.s);
if(c == '\r') {
c = prot_getc(pin);
if(c != '\n') {
r = IMAP_SERVER_UNAVAILABLE;
goto cleanup;
}
}
}
continue;
} else if (!strncmp(tag.s, "ACL0", 4)) {
if (!strcasecmp(cmd.s, "OK")) { break; }
if (!strcasecmp(cmd.s, "NO")) { r = IMAP_REMOTE_DENIED; break; }
r = IMAP_SERVER_UNAVAILABLE;
break;
}
}
cleanup:
if(!r) {
while(j < i) {
c = getword(pin, &tag);
if (c == EOF) {
r = IMAP_SERVER_UNAVAILABLE;
break;
}
eatline(pin, c);
if(!strncmp("ACL", tag.s, 3)) {
j++;
}
}
}
if(r) eatline(pin, c);
freebuf(&user);
freebuf(&tmp);
freebuf(&cmd);
freebuf(&tag);
return r;
}
static int dumpacl(struct protstream *pin, struct protstream *pout,
char *mailbox, char *acl_in)
{
int r = 0;
int c;
char tag[128];
int tagnum = 1;
char *rights, *nextid;
int mailboxlen = strlen(mailbox);
char *acl_safe = acl_in ? xstrdup(acl_in) : NULL;
char *acl = acl_safe;
struct buf inbuf;
memset(&inbuf, 0, sizeof(struct buf));
while (acl) {
rights = strchr(acl, '\t');
if (!rights) break;
*rights++ = '\0';
nextid = strchr(rights, '\t');
if (!nextid) break;
*nextid++ = '\0';
snprintf(tag, sizeof(tag), "SACL%d", tagnum++);
prot_printf(pout, "%s SETACL {%d+}\r\n%s {%d+}\r\n%s {%d+}\r\n%s\r\n",
tag,
mailboxlen, mailbox,
strlen(acl), acl,
strlen(rights), rights);
while(1) {
c = getword(pin, &inbuf);
if (c == EOF) {
r = IMAP_SERVER_UNAVAILABLE;
break;
}
if(strncmp(tag, inbuf.s, strlen(tag))) {
eatline(pin, c);
continue;
} else {
break;
}
}
c = getword(pin, &inbuf);
if (c == EOF) {
r = IMAP_SERVER_UNAVAILABLE;
break;
}
if(strncmp("OK", inbuf.s, 2)) {
r = IMAP_REMOTE_DENIED;
break;
}
eatline(pin, c);
acl = nextid;
}
freebuf(&inbuf);
if(acl_safe) free(acl_safe);
return r;
}
static int do_xfer_single(char *toserver, char *topart,
char *name, char *mailboxname,
int mbflags,
char *path, char *part, char *acl,
int prereserved,
mupdate_handle *h_in,
struct backend *be_in)
{
int r = 0, rerr = 0;
char buf[MAX_PARTITION_LEN+HOSTNAME_SIZE+2];
struct backend *be = NULL;
mupdate_handle *mupdate_h = NULL;
int backout_mupdate = 0;
int backout_remotebox = 0;
int backout_remoteflag = 0;
if(topart && !imparse_isatom(topart)) {
return IMAP_PARTITION_UNKNOWN;
}
if(!strcmp(toserver, config_servername)) {
return IMAP_BAD_SERVER;
}
if(!r && !be_in) {
be = backend_connect(NULL, toserver, &protocol[PROTOCOL_IMAP], "", NULL);
if(!be) r = IMAP_SERVER_UNAVAILABLE;
if(r) syslog(LOG_ERR,
"Could not move mailbox: %s, Backend connect failed",
mailboxname);
} else if(!r) {
be = be_in;
}
if(h_in) {
mupdate_h = h_in;
} else if (config_mupdate_server) {
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if(r) {
syslog(LOG_ERR,
"Could not move mailbox: %s, MUPDATE connect failed",
mailboxname);
goto done;
}
}
if(!r) {
if(topart) {
prot_printf(be->out, "LC1 LOCALCREATE {%d+}\r\n%s %s\r\n",
strlen(name), name, topart);
} else {
prot_printf(be->out, "LC1 LOCALCREATE {%d+}\r\n%s\r\n",
strlen(name), name);
}
r = getresult(be->in, "LC1");
if(r) syslog(LOG_ERR, "Could not move mailbox: %s, LOCALCREATE failed",
mailboxname);
else backout_remotebox = 1;
}
if(!r) {
snprintf(buf, sizeof(buf), "%s!%s", toserver, part);
r = mboxlist_update(mailboxname, mbflags|MBTYPE_MOVING, buf, acl, 1);
if(r) syslog(LOG_ERR, "Could not move mailbox: %s, " \
"mboxlist_update failed", mailboxname);
}
if(!r && mupdate_h && !prereserved) {
backout_remoteflag = 1;
snprintf(buf, sizeof(buf), "%s!%s", config_servername, part);
r = mupdate_deactivate(mupdate_h, mailboxname, buf);
if(r) syslog(LOG_ERR,
"Could not move mailbox: %s, MUPDATE DEACTIVATE failed",
mailboxname);
}
if(!r) {
backout_mupdate = 1;
prot_printf(be->out, "D01 UNDUMP {%d+}\r\n%s ", strlen(name), name);
r = dump_mailbox(NULL, mailboxname, path, acl, 0, be->in, be->out,
imapd_authstate);
if(r)
syslog(LOG_ERR,
"Could not move mailbox: %s, dump_mailbox() failed",
mailboxname);
}
if(!r) {
r = getresult(be->in, "D01");
if(r) syslog(LOG_ERR, "Could not move mailbox: %s, UNDUMP failed",
mailboxname);
}
if(!r) {
r = trashacl(be->in, be->out, name);
if(r) syslog(LOG_ERR, "Could not clear remote acl on %s",
mailboxname);
}
if(!r) {
r = dumpacl(be->in, be->out, name, acl);
if(r) syslog(LOG_ERR, "Could not set remote acl on %s",
mailboxname);
}
if(!r && mupdate_h) {
snprintf(buf, sizeof(buf), "%s!MOVED", toserver);
r = mupdate_activate(mupdate_h, mailboxname, buf, acl);
}
if(!r) {
backout_remotebox = 0;
backout_mupdate = 0;
backout_remoteflag = 0;
if (mupdate_h) {
prot_printf(be->out, "MP1 MUPDATEPUSH {%d+}\r\n%s\r\n",
strlen(name), name);
rerr = getresult(be->in, "MP1");
if(rerr) {
syslog(LOG_ERR,
"Could not trigger remote push to mupdate server" \
"during move of %s",
mailboxname);
}
}
}
if(!r) {
r = mboxlist_deletemailbox(mailboxname,
imapd_userisadmin || imapd_userisproxyadmin,
imapd_userid, imapd_authstate, 0, 1, 0);
if(r) syslog(LOG_ERR,
"Could not delete local mailbox during move of %s",
mailboxname);
}
done:
if(r && mupdate_h && backout_mupdate) {
rerr = 0;
snprintf(buf, sizeof(buf), "%s!%s", config_servername, part);
rerr = mupdate_activate(mupdate_h, mailboxname, buf, acl);
if(rerr) {
syslog(LOG_ERR,
"Could not back out mupdate during move of %s (%s)",
mailboxname, error_message(rerr));
}
}
if(r && backout_remotebox) {
rerr = 0;
prot_printf(be->out, "LD1 LOCALDELETE {%d+}\r\n%s\r\n",
strlen(name), name);
rerr = getresult(be->in, "LD1");
if(rerr) {
syslog(LOG_ERR,
"Could not back out remote mailbox during move of %s (%s)",
name, error_message(rerr));
}
}
if(r && backout_remoteflag) {
rerr = 0;
rerr = mboxlist_update(mailboxname, mbflags, part, acl, 1);
if(rerr) syslog(LOG_ERR, "Could not unset remote flag on mailbox: %s",
mailboxname);
}
if(mupdate_h && !h_in)
mupdate_disconnect(&mupdate_h);
if(be && !be_in)
backend_disconnect(be, &protocol[PROTOCOL_IMAP]);
return r;
}
struct xfer_user_rock
{
char *toserver;
char *topart;
mupdate_handle *h;
struct backend *be;
};
static int xfer_user_cb(char *name,
int matchlen __attribute__((unused)),
int maycreate __attribute__((unused)),
void *rock)
{
mupdate_handle *mupdate_h = ((struct xfer_user_rock *)rock)->h;
char *toserver = ((struct xfer_user_rock *)rock)->toserver;
char *topart = ((struct xfer_user_rock *)rock)->topart;
struct backend *be = ((struct xfer_user_rock *)rock)->be;
char externalname[MAX_MAILBOX_NAME+1];
int mbflags;
int r = 0;
char *inpath, *inpart, *inacl;
char *path = NULL, *part = NULL, *acl = NULL;
if (!r) {
r = mboxlist_detail(name, &mbflags,
&inpath, &inpart, &inacl, NULL);
}
if (!r) {
path = xstrdup(inpath);
part = xstrdup(inpart);
acl = xstrdup(inacl);
}
if (!r) {
r = (*imapd_namespace.mboxname_toexternal)(&imapd_namespace,
name,
imapd_userid,
externalname);
}
if(!r) {
r = do_xfer_single(toserver, topart, externalname, name, mbflags,
path, part, acl, 0, mupdate_h, be);
}
if(path) free(path);
if(part) free(part);
if(acl) free(acl);
return r;
}
void cmd_xfer(char *tag, char *name, char *toserver, char *topart)
{
int r = 0;
char buf[MAX_PARTITION_LEN+HOSTNAME_SIZE+2];
char mailboxname[MAX_MAILBOX_NAME+1];
int mbflags;
int moving_user = 0;
int backout_mupdate = 0;
mupdate_handle *mupdate_h = NULL;
char *inpath, *inpart, *inacl;
char *path = NULL, *part = NULL, *acl = NULL;
char *p, *mbox = mailboxname;
if (!imapd_userisadmin && !imapd_userisproxyadmin) {
r = IMAP_PERMISSION_DENIED;
}
if (!r) {
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace,
name,
imapd_userid,
mailboxname);
}
if (config_virtdomains && (p = strchr(mailboxname, '!'))) {
mbox = p + 1;
}
if(!strncmp(mbox, "user.", 5) && !strchr(mbox+5, '.')) {
if ((strlen(mbox+5) == (strlen(imapd_userid) - (mbox - mailboxname))) &&
!strncmp(mbox+5, imapd_userid, strlen(mbox+5))) {
r = IMAP_MAILBOX_NOTSUPPORTED;
} else if (!config_getswitch(IMAPOPT_ALLOWUSERMOVES)) {
r = IMAP_MAILBOX_NOTSUPPORTED;
} else {
moving_user = 1;
}
}
if (!r) {
r = mlookup(tag, name, mailboxname, &mbflags,
&inpath, &inpart, &inacl, NULL);
}
if (r == IMAP_MAILBOX_MOVED) return;
if (!r) {
path = xstrdup(inpath);
part = xstrdup(inpart);
acl = xstrdup(inacl);
}
if(!r && !moving_user) {
r = do_xfer_single(toserver, topart, name, mailboxname, mbflags,
path, part, acl, 0, NULL, NULL);
} else if (!r) {
struct backend *be = NULL;
if(!r && config_mupdate_server) {
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if(r) {
syslog(LOG_ERR,
"Could not move mailbox: %s, MUPDATE connect failed",
mailboxname);
goto done;
}
}
be = backend_connect(NULL, toserver, &protocol[PROTOCOL_IMAP], "", NULL);
if(!be) {
r = IMAP_SERVER_UNAVAILABLE;
syslog(LOG_ERR,
"Could not move mailbox: %s, " \
"Initial backend connect failed",
mailboxname);
}
if(!r && mupdate_h) {
snprintf(buf, sizeof(buf), "%s!%s", config_servername, part);
r = mupdate_deactivate(mupdate_h, mailboxname, buf);
if(r) syslog(LOG_ERR,
"Could deactivate mailbox: %s, during move",
mailboxname);
else backout_mupdate = 1;
}
if(!r) {
struct quota quota;
quota.root = mailboxname;
r = quota_read("a, NULL, 0);
if(!r) {
prot_printf(be->out, "Q01 SETQUOTA {%d+}\r\n" \
"+%s (STORAGE %d)\r\n",
strlen(name)+1, name, quota.limit);
r = getresult(be->in, "Q01");
if(r) syslog(LOG_ERR,
"Could not move mailbox: %s, " \
"failed setting initial quota root\r\n",
mailboxname);
}
else if (r == IMAP_QUOTAROOT_NONEXISTENT) r = 0;
}
if(!r) {
struct xfer_user_rock rock;
rock.toserver = toserver;
rock.topart = topart;
rock.h = mupdate_h;
rock.be = be;
snprintf(buf, sizeof(buf), "%s.*", mailboxname);
r = mboxlist_findall(NULL, buf, 1, imapd_userid,
imapd_authstate, xfer_user_cb,
&rock);
}
if(!r) {
r = do_xfer_single(toserver, topart, name, mailboxname, mbflags,
path, part, acl, 1, mupdate_h, be);
}
if(be) {
backend_disconnect(be, &protocol[PROTOCOL_IMAP]);
free(be);
}
if(r && mupdate_h && backout_mupdate) {
int rerr = 0;
snprintf(buf, sizeof(buf), "%s!%s", config_servername, part);
rerr = mupdate_activate(mupdate_h, mailboxname, buf, acl);
if(rerr) {
syslog(LOG_ERR,
"Could not back out mupdate during move of %s (%s)",
mailboxname, error_message(rerr));
}
} else if(!r) {
user_deletedata(mailboxname+5, imapd_userid, imapd_authstate, 0);
}
if(!r && mupdate_h) {
mupdate_disconnect(&mupdate_h);
}
}
done:
if(part) free(part);
if(path) free(path);
if(acl) free(acl);
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n",
tag,
error_message(r));
} else {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
return;
}
int getsearchdate(start, end)
time_t *start, *end;
{
int c;
struct tm tm;
int quoted = 0;
char month[4];
memset(&tm, 0, sizeof tm);
c = prot_getc(imapd_in);
if (c == '\"') {
quoted++;
c = prot_getc(imapd_in);
}
if (!isdigit(c)) goto baddate;
tm.tm_mday = c - '0';
c = prot_getc(imapd_in);
if (isdigit(c)) {
tm.tm_mday = tm.tm_mday * 10 + c - '0';
c = prot_getc(imapd_in);
}
if (c != '-') goto baddate;
c = prot_getc(imapd_in);
if (!isalpha(c)) goto baddate;
month[0] = c;
c = prot_getc(imapd_in);
if (!isalpha(c)) goto baddate;
month[1] = c;
c = prot_getc(imapd_in);
if (!isalpha(c)) goto baddate;
month[2] = c;
c = prot_getc(imapd_in);
month[3] = '\0';
lcase(month);
for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) {
if (!strcmp(month, monthname[tm.tm_mon])) break;
}
if (tm.tm_mon == 12) goto baddate;
if (c != '-') goto baddate;
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
tm.tm_year = c - '0';
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
tm.tm_year = tm.tm_year * 10 + c - '0';
c = prot_getc(imapd_in);
if (isdigit(c)) {
if (tm.tm_year < 19) goto baddate;
tm.tm_year -= 19;
tm.tm_year = tm.tm_year * 10 + c - '0';
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
tm.tm_year = tm.tm_year * 10 + c - '0';
c = prot_getc(imapd_in);
}
if (quoted) {
if (c != '\"') goto baddate;
c = prot_getc(imapd_in);
}
tm.tm_isdst = -1;
*start = mktime(&tm);
tm.tm_sec = tm.tm_min = 59;
tm.tm_hour = 23;
tm.tm_isdst = -1;
*end = mktime(&tm);
return c;
baddate:
prot_ungetc(c, imapd_in);
return EOF;
}
#define SORTGROWSIZE 10
int getsortcriteria(char *tag, struct sortcrit **sortcrit)
{
int c;
static struct buf criteria;
int nsort, n;
*sortcrit = NULL;
c = prot_getc(imapd_in);
if (c != '(') goto missingcrit;
c = getword(imapd_in, &criteria);
if (criteria.s[0] == '\0') goto missingcrit;
nsort = 0;
n = 0;
for (;;) {
if (n >= nsort - 1) {
nsort += SORTGROWSIZE;
*sortcrit =
(struct sortcrit *) xrealloc(*sortcrit,
nsort * sizeof(struct sortcrit));
memset((*sortcrit)+n, 0, SORTGROWSIZE * sizeof(struct sortcrit));
}
lcase(criteria.s);
if (!strcmp(criteria.s, "reverse")) {
(*sortcrit)[n].flags |= SORT_REVERSE;
goto nextcrit;
}
else if (!strcmp(criteria.s, "arrival"))
(*sortcrit)[n].key = SORT_ARRIVAL;
else if (!strcmp(criteria.s, "cc"))
(*sortcrit)[n].key = SORT_CC;
else if (!strcmp(criteria.s, "date"))
(*sortcrit)[n].key = SORT_DATE;
else if (!strcmp(criteria.s, "from"))
(*sortcrit)[n].key = SORT_FROM;
else if (!strcmp(criteria.s, "size"))
(*sortcrit)[n].key = SORT_SIZE;
else if (!strcmp(criteria.s, "subject"))
(*sortcrit)[n].key = SORT_SUBJECT;
else if (!strcmp(criteria.s, "to"))
(*sortcrit)[n].key = SORT_TO;
#if 0
else if (!strcmp(criteria.s, "annotation")) {
(*sortcrit)[n].key = SORT_ANNOTATION;
if (c != ' ') goto missingarg;
c = getstring(imapd_in, &arg);
if (c != ' ') goto missingarg;
(*sortcrit)[n].args.annot.entry = xstrdup(arg.s);
c = getstring(imapd_in, &arg);
if (c == EOF) goto missingarg;
(*sortcrit)[n].args.annot.attrib = xstrdup(arg.s);
}
#endif
else {
prot_printf(imapd_out, "%s BAD Invalid Sort criterion %s\r\n",
tag, criteria.s);
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
}
n++;
nextcrit:
if (c == ' ') c = getword(imapd_in, &criteria);
else break;
}
if ((*sortcrit)[n].flags & SORT_REVERSE && !(*sortcrit)[n].key) {
prot_printf(imapd_out,
"%s BAD Missing Sort criterion to reverse\r\n", tag);
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
}
if (c != ')') {
prot_printf(imapd_out,
"%s BAD Missing close parenthesis in Sort\r\n", tag);
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
}
(*sortcrit)[n++].key = SORT_SEQUENCE;
c = prot_getc(imapd_in);
return c;
missingcrit:
prot_printf(imapd_out, "%s BAD Missing Sort criteria\r\n", tag);
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
#if 0
missingarg:
prot_printf(imapd_out, "%s BAD Missing argument to Sort criterion %s\r\n",
tag, criteria.s);
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
#endif
}
#ifdef ENABLE_LISTEXT
int getlistopts(char *tag, int *listopts)
{
int c;
static struct buf arg;
*listopts = LIST_EXT;
for (;;) {
c = getword(imapd_in, &arg);
if (!arg.s[0]) break;
lcase(arg.s);
if (!strcmp(arg.s, "subscribed")) {
*listopts |= LIST_SUBSCRIBED;
}
else if (!strcmp(arg.s, "children")) {
*listopts |= LIST_CHILDREN;
}
else if (!strcmp(arg.s, "remote")) {
*listopts |= LIST_REMOTE;
}
else {
prot_printf(imapd_out, "%s BAD Invalid List option %s\r\n",
tag, arg.s);
return EOF;
}
if (c != ' ') break;
}
if (c != ')') {
prot_printf(imapd_out,
"%s BAD Missing close parenthesis in List\r\n", tag);
return EOF;
}
c = prot_getc(imapd_in);
return c;
}
#endif
int getdatetime(date)
time_t *date;
{
int c;
struct tm tm;
int old_format = 0;
char month[4], zone[4], *p;
time_t tmp_gmtime;
int zone_off;
memset(&tm, 0, sizeof tm);
c = prot_getc(imapd_in);
if (c != '\"') goto baddate;
c = prot_getc(imapd_in);
if (c == ' ') c = '0';
if (!isdigit(c)) goto baddate;
tm.tm_mday = c - '0';
c = prot_getc(imapd_in);
if (isdigit(c)) {
tm.tm_mday = tm.tm_mday * 10 + c - '0';
c = prot_getc(imapd_in);
if(tm.tm_mday <= 0 || tm.tm_mday > 31)
goto baddate;
}
if (c != '-') goto baddate;
c = prot_getc(imapd_in);
if (!isalpha(c)) goto baddate;
month[0] = c;
c = prot_getc(imapd_in);
if (!isalpha(c)) goto baddate;
month[1] = c;
c = prot_getc(imapd_in);
if (!isalpha(c)) goto baddate;
month[2] = c;
c = prot_getc(imapd_in);
month[3] = '\0';
lcase(month);
for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) {
if (!strcmp(month, monthname[tm.tm_mon])) break;
}
if (tm.tm_mon == 12) goto baddate;
if (tm.tm_mday > max_monthdays[tm.tm_mon]) goto baddate;
if (c != '-') goto baddate;
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
tm.tm_year = c - '0';
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
tm.tm_year = tm.tm_year * 10 + c - '0';
c = prot_getc(imapd_in);
if (isdigit(c)) {
if (tm.tm_year < 19) goto baddate;
tm.tm_year -= 19;
tm.tm_year = tm.tm_year * 10 + c - '0';
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
tm.tm_year = tm.tm_year * 10 + c - '0';
c = prot_getc(imapd_in);
}
else old_format++;
if (c != ' ') goto baddate;
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
tm.tm_hour = c - '0';
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
tm.tm_hour = tm.tm_hour * 10 + c - '0';
c = prot_getc(imapd_in);
if (tm.tm_hour > 23) goto baddate;
if (c != ':') goto baddate;
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
tm.tm_min = c - '0';
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
tm.tm_min = tm.tm_min * 10 + c - '0';
c = prot_getc(imapd_in);
if (tm.tm_min > 59) goto baddate;
if (c != ':') goto baddate;
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
tm.tm_sec = c - '0';
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
tm.tm_sec = tm.tm_sec * 10 + c - '0';
c = prot_getc(imapd_in);
if (tm.tm_min > 60) goto baddate;
if (old_format) {
if (c != '-') goto baddate;
c = prot_getc(imapd_in);
if (!isalpha(c)) goto baddate;
zone[0] = c;
c = prot_getc(imapd_in);
if (c == '\"') {
zone[1] = '\0';
lcase(zone);
if (zone[0] <= 'm') {
zone_off = (zone[0] - 'a' + 1)*60;
}
else if (zone[0] < 'z') {
zone_off = ('m' - zone[0])*60;
}
else zone_off = 0;
}
else {
zone[1] = c;
c = prot_getc(imapd_in);
if (c == '\"') {
zone[2] = '\0';
lcase(zone);
if (!strcmp(zone, "ut")) goto baddate;
zone_off = 0;
}
else {
zone[2] = c;
c = prot_getc(imapd_in);
if (c != '\"') goto baddate;
zone[3] = '\0';
lcase(zone);
p = strchr("aecmpyhb", zone[0]);
if (c != '\"' || zone[2] != 't' || !p) goto baddate;
zone_off = (strlen(p) - 12)*60;
if (zone[1] == 'd') zone_off -= 60;
else if (zone[1] != 's') goto baddate;
}
}
}
else {
if (c != ' ') goto baddate;
c = prot_getc(imapd_in);
if (c != '+' && c != '-') goto baddate;
zone[0] = c;
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
zone_off = c - '0';
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
zone_off = zone_off * 10 + c - '0';
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
zone_off = zone_off * 6 + c - '0';
c = prot_getc(imapd_in);
if (!isdigit(c)) goto baddate;
zone_off = zone_off * 10 + c - '0';
if (zone[0] == '-') zone_off = -zone_off;
c = prot_getc(imapd_in);
if (c != '\"') goto baddate;
}
c = prot_getc(imapd_in);
tm.tm_isdst = -1;
tmp_gmtime = mkgmtime(&tm);
if(tmp_gmtime == -1) goto baddate;
*date = tmp_gmtime - zone_off*60;
return c;
baddate:
prot_ungetc(c, imapd_in);
return EOF;
}
void
printstring(s)
const char *s;
{
const char *p;
int len = 0;
for (p = s; *p && len < 1024; p++) {
len++;
if (*p & 0x80 || *p == '\r' || *p == '\n'
|| *p == '\"' || *p == '%' || *p == '\\') break;
}
if (*p || len >= 1024) {
prot_printf(imapd_out, "{%u}\r\n%s", strlen(s), s);
} else {
prot_printf(imapd_out, "\"%s\"", s);
}
}
void
printastring(s)
const char *s;
{
const char *p;
int len = 0;
if (imparse_isatom(s)) {
prot_printf(imapd_out, "%s", s);
return;
}
for (p = s; *p && len < 1024; p++) {
len++;
if (*p & 0x80 || *p == '\r' || *p == '\n'
|| *p == '\"' || *p == '%' || *p == '\\') break;
}
if (*p || len >= 1024) {
prot_printf(imapd_out, "{%u}\r\n%s", strlen(s), s);
} else {
prot_printf(imapd_out, "\"%s\"", s);
}
}
void
appendfieldlist(struct fieldlist **l, char *section,
struct strlist *fields, char *trail,
void *d, size_t size)
{
struct fieldlist **tail = l;
while (*tail) tail = &(*tail)->next;
*tail = (struct fieldlist *)xmalloc(sizeof(struct fieldlist));
(*tail)->section = xstrdup(section);
(*tail)->fields = fields;
(*tail)->trail = xstrdup(trail);
if(d && size) {
(*tail)->rock = xmalloc(size);
memcpy((*tail)->rock, d, size);
} else {
(*tail)->rock = NULL;
}
(*tail)->next = 0;
}
void freefieldlist(struct fieldlist *l)
{
struct fieldlist *n;
while (l) {
n = l->next;
free(l->section);
freestrlist(l->fields);
free(l->trail);
if (l->rock) free(l->rock);
free((char *)l);
l = n;
}
}
void
appendsearchargs(s, s1, s2)
struct searchargs *s, *s1, *s2;
{
struct searchsub **tail = &s->sublist;
while (*tail) tail = &(*tail)->next;
*tail = (struct searchsub *)xmalloc(sizeof(struct searchsub));
(*tail)->sub1 = s1;
(*tail)->sub2 = s2;
(*tail)->next = 0;
}
void
freesearchargs(s)
struct searchargs *s;
{
struct searchsub *sub, *n;
if (!s) return;
freestrlist(s->sequence);
freestrlist(s->uidsequence);
freestrlist(s->from);
freestrlist(s->to);
freestrlist(s->cc);
freestrlist(s->bcc);
freestrlist(s->subject);
freestrlist(s->body);
freestrlist(s->text);
freestrlist(s->header_name);
freestrlist(s->header);
for (sub = s->sublist; sub; sub = n) {
n = sub->next;
freesearchargs(sub->sub1);
freesearchargs(sub->sub2);
free(sub);
}
free(s);
}
static void freesortcrit(struct sortcrit *s)
{
int i = 0;
if (!s) return;
do {
switch (s[i].key) {
case SORT_ANNOTATION:
free(s[i].args.annot.entry);
free(s[i].args.annot.attrib);
break;
}
i++;
} while (s[i].key != SORT_SEQUENCE);
free(s);
}
static int mailboxdata(char *name,
int matchlen __attribute__((unused)),
int maycreate __attribute__((unused)),
void *rock __attribute__((unused)))
{
char mboxname[MAX_MAILBOX_PATH+1];
(*imapd_namespace.mboxname_toexternal)(&imapd_namespace, name,
imapd_userid, mboxname);
prot_printf(imapd_out, "* MAILBOX %s\r\n", mboxname);
return 0;
}
static void mstringdata(char *cmd, char *name, int matchlen, int maycreate,
int listopts)
{
static char lastname[MAX_MAILBOX_PATH+1];
static int lastnamedelayed = 0;
static int lastnamenoinferiors = 0;
static int nonexistent = 0;
static int sawuser = 0;
int lastnamehassub = 0;
int c, mbtype;
char mboxname[MAX_MAILBOX_PATH+1];
if (cmd == NULL) {
sawuser = 0;
mstringdatacalls = 0;
return;
}
mstringdatacalls++;
if (lastnamedelayed) {
if (name && strncmp(lastname, name, strlen(lastname)) == 0 &&
name[strlen(lastname)] == '.') {
lastnamehassub = 1;
}
prot_printf(imapd_out, "* %s (", cmd);
if (nonexistent == IMAP_MAILBOX_RESERVED) {
if (listopts & LIST_EXT)
prot_printf(imapd_out, "\\PlaceHolder");
else
prot_printf(imapd_out, "\\Noselect");
} else if (nonexistent) {
prot_printf(imapd_out, "\\NonExistent");
}
if (lastnamenoinferiors) {
prot_printf(imapd_out, "%s\\Noinferiors", nonexistent ? " " : "");
}
else if ((listopts & LIST_CHILDREN) &&
(lastnamehassub ||
!(listopts & (LIST_LSUB | LIST_SUBSCRIBED)))) {
prot_printf(imapd_out, "%s%s", nonexistent ? " " : "",
lastnamehassub ? "\\HasChildren" : "\\HasNoChildren");
}
prot_printf(imapd_out, ") \"%c\" ", imapd_namespace.hier_sep);
(*imapd_namespace.mboxname_toexternal)(&imapd_namespace, lastname,
imapd_userid, mboxname);
printstring(mboxname);
prot_printf(imapd_out, "\r\n");
lastnamedelayed = lastnamenoinferiors = nonexistent = 0;
}
if (!name) {
lastname[0] = '\0';
return;
}
if ((name[matchlen]
&& strncmp(lastname, name, matchlen) == 0
&& (lastname[matchlen] == '\0' || lastname[matchlen] == '.'))) {
return;
}
if (matchlen == 4 && strncasecmp(name, "user", 4) == 0) {
if (sawuser) return;
sawuser = 1;
}
strlcpy(lastname, name, sizeof(lastname));
lastname[matchlen] = '\0';
nonexistent = 0;
if (!strncasecmp(lastname, "inbox", 5)) {
(*imapd_namespace.mboxname_tointernal)(&imapd_namespace, "INBOX",
imapd_userid, mboxname);
strlcat(mboxname, lastname+5, sizeof(mboxname));
}
else
strlcpy(mboxname, lastname, sizeof(mboxname));
nonexistent = mboxlist_detail(mboxname, &mbtype,
NULL, NULL, NULL, NULL);
if(!nonexistent && (mbtype & MBTYPE_RESERVE))
nonexistent = IMAP_MAILBOX_RESERVED;
if (!name[matchlen]) {
lastnamedelayed = 1;
if (!maycreate) lastnamenoinferiors = 1;
return;
}
c = name[matchlen];
if (c) name[matchlen] = '\0';
prot_printf(imapd_out, "* %s (", cmd);
if (c) {
if (!strcmp(name, "user") ||
!strcmp(name, imapd_namespace.prefix[NAMESPACE_SHARED])) {
prot_printf(imapd_out, "\\Noselect");
if (listopts & LIST_EXT)
prot_printf(imapd_out, " \\PlaceHolder");
}
else {
if (nonexistent)
prot_printf(imapd_out, "\\NonExistent");
if (listopts & LIST_EXT)
prot_printf(imapd_out, "%s\\PlaceHolder", nonexistent ? " " : "");
else
prot_printf(imapd_out, "%s\\Noselect", nonexistent ? " " : "");
}
if (listopts & LIST_CHILDREN)
prot_printf(imapd_out, " \\HasChildren");
}
prot_printf(imapd_out, ") \"%c\" ", imapd_namespace.hier_sep);
(*imapd_namespace.mboxname_toexternal)(&imapd_namespace, name,
imapd_userid, mboxname);
printstring(mboxname);
prot_printf(imapd_out, "\r\n");
if (c) name[matchlen] = c;
return;
}
static int listdata(char *name, int matchlen, int maycreate, void *rock)
{
int listopts = *((int *)rock);
mstringdata(((listopts & LIST_LSUB) ? "LSUB" : "LIST"),
name, matchlen, maycreate, listopts);
return 0;
}
static int reset_saslconn(sasl_conn_t **conn)
{
int ret;
sasl_security_properties_t *secprops = NULL;
sasl_dispose(conn);
ret = sasl_server_new("imap", config_servername,
NULL, NULL, NULL,
NULL, 0, conn);
if(ret != SASL_OK) return ret;
if(saslprops.ipremoteport)
ret = sasl_setprop(*conn, SASL_IPREMOTEPORT,
saslprops.ipremoteport);
if(ret != SASL_OK) return ret;
if(saslprops.iplocalport)
ret = sasl_setprop(*conn, SASL_IPLOCALPORT,
saslprops.iplocalport);
if(ret != SASL_OK) return ret;
secprops = mysasl_secprops(SASL_SEC_NOPLAINTEXT);
ret = sasl_setprop(*conn, SASL_SEC_PROPS, secprops);
if(ret != SASL_OK) return ret;
if(saslprops.ssf) {
ret = sasl_setprop(*conn, SASL_SSF_EXTERNAL, &saslprops.ssf);
} else {
ret = sasl_setprop(*conn, SASL_SSF_EXTERNAL, &extprops_ssf);
}
if(ret != SASL_OK) return ret;
if(saslprops.authid) {
ret = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, saslprops.authid);
if(ret != SASL_OK) return ret;
}
return SASL_OK;
}
void cmd_mupdatepush(char *tag, char *name)
{
int r = 0;
char mailboxname[MAX_MAILBOX_NAME+1];
char *part, *acl;
mupdate_handle *mupdate_h = NULL;
char buf[MAX_PARTITION_LEN + HOSTNAME_SIZE + 2];
if (!imapd_userisadmin) {
r = IMAP_PERMISSION_DENIED;
}
if (!config_mupdate_server) {
r = IMAP_SERVER_UNAVAILABLE;
}
if (!r) {
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
imapd_userid, mailboxname);
}
if (!r) {
r = mlookup(tag, name, mailboxname, NULL, NULL, &part, &acl, NULL);
}
if (r == IMAP_MAILBOX_MOVED) return;
if (!r) {
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
}
if (!r) {
snprintf(buf, sizeof(buf), "%s!%s", config_servername, part);
r = mupdate_activate(mupdate_h, mailboxname, buf, acl);
}
if(mupdate_h) {
mupdate_disconnect(&mupdate_h);
}
if (r) {
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
}
else {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
}