Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117756629
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
110 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/imap/imapd.c b/imap/imapd.c
index 121db4d6f..fbc21c1f4 100644
--- a/imap/imapd.c
+++ b/imap/imapd.c
@@ -1,4474 +1,4474 @@
/* imapd.c -- IMAP server protocol parsing
*
* Copyright 1998 Carnegie Mellon University
*
* No warranties, either expressed or implied, are made regarding the
* operation, use, or results of the software.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted for non-commercial purposes only
* provided that this copyright notice appears in all copies and in
* supporting documentation.
*
* Permission is also granted to Internet Service Providers and others
* entities to use the software for internal purposes.
*
* The distribution, modification or sale of a product which uses or is
* based on the software, in whole or in part, for commercial purposes or
* benefits requires specific, additional permission from:
*
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*/
-/* $Id: imapd.c,v 1.163 1999/03/01 21:11:57 tjs Exp $ */
+/* $Id: imapd.c,v 1.164 1999/03/02 01:14:25 tjs Exp $ */
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <syslog.h>
#include <com_err.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "acl.h"
#include "util.h"
#include "auth.h"
#include "sasl.h"
#include "map.h"
#include "config.h"
#include "version.h"
#include "charset.h"
#include "imparse.h"
#include "mkgmtime.h"
#include "sysexits.h"
#include "imap_err.h"
#include "mailbox.h"
#include "imapd.h"
#include "xmalloc.h"
extern int optind;
extern char *optarg;
extern int errno;
extern char *login_capabilities();
struct buf {
char *s;
int alloc;
};
char *imapd_userid;
struct auth_state *imapd_authstate = 0;
int imapd_userisadmin;
struct mailbox *imapd_mailbox;
int imapd_exists;
struct sockaddr_in imapd_localaddr, imapd_remoteaddr;
int imapd_haveaddr = 0;
char imapd_clienthost[250] = "[local]";
struct protstream *imapd_out, *imapd_in;
time_t imapd_logtime;
static struct mailbox mboxstruct;
static struct fetchargs zerofetchargs;
static char *monthname[] = {
"jan", "feb", "mar", "apr", "may", "jun",
"jul", "aug", "sep", "oct", "nov", "dec"
};
void usage P((void));
void shutdown_file P((int fd));
void motd_file P((int fd));
void shut_down P((int code));
void fatal P((const char *s, int code));
void cmdloop P((void));
void cmd_login P((char *tag, char *user, char *passwd));
void cmd_authenticate P((char *tag, char *authtype));
void cmd_noop P((char *tag, char *cmd));
void cmd_capability P((char *tag));
void cmd_append P((char *tag, char *name));
void cmd_select P((char *tag, char *cmd, char *name));
void cmd_close P((char *tag));
void cmd_fetch P((char *tag, char *sequence, int usinguid));
void cmd_partial P((char *tag, char *msgno, char *data,
char *start, char *count));
void cmd_store P((char *tag, char *sequence, char *operation, int usinguid));
void cmd_search P((char *tag, int usinguid));
void cmd_copy P((char *tag, char *sequence, char *name, int usinguid));
void cmd_expunge P((char *tag, char *sequence));
void cmd_create P((char *tag, char *name, char *partition));
void cmd_delete P((char *tag, char *name));
void cmd_rename P((char *tag, char *oldname, char *newname, char *partition));
void cmd_find P((char *tag, char *namespace, char *pattern));
void cmd_list P((char *tag, int subscribed, char *reference, char *pattern));
void cmd_changesub P((char *tag, char *namespace, char *name, int add));
void cmd_getacl P((char *tag, char *name, int oldform));
void cmd_listrights P((char *tag, char *name, char *identifier));
void cmd_myrights P((char *tag, char *name, int oldform));
void cmd_setacl P((char *tag, char *name, char *identifier, char *rights));
void cmd_getquota P((char *tag, char *name));
void cmd_getquotaroot P((char *tag, char *name));
void cmd_setquota P((char *tag, char *quotaroot));
void cmd_status P((char *tag, char *name));
void cmd_getuids P((char *tag, char *startuid));
void cmd_unselect P((char* tag));
void cmd_namespace P((char* tag));
#ifdef ENABLE_X_NETSCAPE_HACK
void cmd_netscrape P((char* tag));
#endif
int getword P((struct buf *buf));
int getastring P((struct buf *buf));
int getbase64string P((struct buf *buf));
int getsearchprogram P((char *tag, struct searchargs *searchargs,
int *charset, int parsecharset));
int getsearchcriteria P((char *tag, struct searchargs *searchargs,
int *charset, int parsecharset));
int getsearchdate P((time_t *start, time_t *end));
int getdatetime P((time_t *date));
void eatline P((int c));
void printstring P((const char *s));
void printastring P((const char *s));
void appendfieldlist P((struct fieldlist **l, char *section,
struct strlist *fields, char *trail));
void appendstrlist P((struct strlist **l, char *s));
void appendstrlistpat P((struct strlist **l, char *s));
void freefieldlist P((struct fieldlist *l));
void freestrlist P((struct strlist *l));
void appendsearchargs P((struct searchargs *s, struct searchargs *s1,
struct searchargs *s2));
void freesearchargs P((struct searchargs *s));
void printauthready P((int len, unsigned char *data));
/* XXX fix when proto-izing mboxlist.c */
static int mailboxdata(), listdata(), lsubdata();
static void mstringdata P((char *cmd, char *name, int matchlen, int maycreate));
void mboxlist_close P((void));
main(argc, argv, envp)
int argc;
char **argv;
char **envp;
{
int salen;
struct hostent *hp;
int timeout;
imapd_in = prot_new(0, 0);
imapd_out = prot_new(1, 1);
setproctitle_init(argc, argv, envp);
config_init("imapd");
signal(SIGPIPE, SIG_IGN);
if (geteuid() == 0) fatal("must run as the Cyrus user", EX_USAGE);
/* Find out name of client host */
salen = sizeof(imapd_remoteaddr);
if (getpeername(0, (struct sockaddr *)&imapd_remoteaddr, &salen) == 0 &&
imapd_remoteaddr.sin_family == AF_INET) {
if (hp = gethostbyaddr((char *)&imapd_remoteaddr.sin_addr,
sizeof(imapd_remoteaddr.sin_addr), AF_INET)) {
strncpy(imapd_clienthost, hp->h_name, sizeof(imapd_clienthost)-30);
imapd_clienthost[sizeof(imapd_clienthost)-30] = '\0';
}
else {
imapd_clienthost[0] = '\0';
}
strcat(imapd_clienthost, "[");
strcat(imapd_clienthost, inet_ntoa(imapd_remoteaddr.sin_addr));
strcat(imapd_clienthost, "]");
salen = sizeof(imapd_localaddr);
if (getsockname(0, (struct sockaddr *)&imapd_localaddr, &salen) == 0) {
imapd_haveaddr = 1;
}
}
proc_register("imapd", imapd_clienthost, (char *)0, (char *)0);
/* Set inactivity timer */
timeout = config_getint("timeout", 30);
if (timeout < 30) timeout = 30;
prot_settimeout(imapd_in, timeout*60);
prot_setflushonread(imapd_in, imapd_out);
cmdloop();
}
void
usage()
{
prot_printf(imapd_out, "* BYE usage: imapd\r\n");
prot_flush(imapd_out);
exit(EX_USAGE);
}
/*
* found a motd file; spit out message and return
*/
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')) *p = 0;
if (p = strchr(buf, '\n')) *p = 0;
for(p = buf; *p == '['; p++); /* can't have [ be first char, sigh */
prot_printf(imapd_out, "* OK [ALERT] %s\r\n", p);
}
/*
* Found a shutdown file: Spit out an untagged BYE and shut down
*/
void shutdown_file(fd)
int fd;
{
struct protstream *shutdown_in;
char buf[1024];
char *p;
shutdown_in = prot_new(fd, 0);
prot_fgets(buf, sizeof(buf), shutdown_in);
if (p = strchr(buf, '\r')) *p = 0;
if (p = strchr(buf, '\n')) *p = 0;
for(p = buf; *p == '['; p++); /* can't have [ be first char, sigh */
prot_printf(imapd_out, "* BYE [ALERT] %s\r\n", p);
shut_down(0);
}
/*
* Cleanly shut down and exit
*/
void
shut_down(code)
int code;
{
proc_cleanup();
if (imapd_mailbox) {
index_closemailbox(imapd_mailbox);
mailbox_close(imapd_mailbox);
}
prot_flush(imapd_out);
exit(code);
}
void
fatal(s, code)
const char *s;
int code;
{
static int recurse_code = 0;
if (recurse_code) {
/* We were called recursively. Just give up */
proc_cleanup();
exit(recurse_code);
}
recurse_code = code;
prot_printf(imapd_out, "* BYE Fatal error: %s\r\n", s);
prot_flush(imapd_out);
shut_down(code);
}
/*
* Top-level command loop parsing
*/
void
cmdloop()
{
int fd;
char shutdownfilename[1024];
char motdfilename[1024];
char hostname[MAXHOSTNAMELEN+1];
int c;
int usinguid, havepartition, havenamespace, oldform;
static struct buf tag, cmd, arg1, arg2, arg3, arg4;
char *p;
const char *err;
sprintf(shutdownfilename, "%s/msg/shutdown", config_dir);
#if 0
if ((fd = open(shutdownfilename, O_RDONLY, 0)) != -1) {
shutdown_file(fd);
}
#endif
gethostname(hostname, sizeof(hostname));
prot_printf(imapd_out,
"* OK %s Cyrus IMAP4 %s server ready\r\n", hostname,
CYRUS_VERSION);
sprintf(motdfilename, "%s/msg/motd", config_dir);
if ((fd = open(motdfilename, O_RDONLY, 0)) != -1) {
motd_file(fd);
close(fd);
}
for (;;) {
if (! imapd_userisadmin &&
(fd = open(shutdownfilename, O_RDONLY, 0)) != -1) {
shutdown_file(fd);
}
/* Parse tag */
c = getword(&tag);
if (c == EOF) {
if (err = prot_error(imapd_in)) {
syslog(LOG_WARNING, "PROTERR: %s", err);
prot_printf(imapd_out, "* BYE %s\r\n", err);
}
shut_down(0);
}
if (c != ' ' || !imparse_isatom(tag.s) || (tag.s[0] == '*' && !tag.s[1])) {
prot_printf(imapd_out, "* BAD Invalid tag\r\n");
eatline(c);
continue;
}
/* Parse command name */
c = getword(&cmd);
if (!cmd.s[0]) {
prot_printf(imapd_out, "%s BAD Null command\r\n", tag.s);
eatline(c);
continue;
}
if (islower(cmd.s[0])) cmd.s[0] = toupper(cmd.s[0]);
for (p = &cmd.s[1]; *p; p++) {
if (isupper(*p)) *p = tolower(*p);
}
/* Only Authenticate/Login/Logout/Noop allowed when not logged in */
if (!imapd_userid && !strchr("ALNC", cmd.s[0])) goto nologin;
/* note that about half the commands (the common ones that don't
hit the mailboxes file) now close the mailboxes file just in
case it was open. */
switch (cmd.s[0]) {
case 'A':
if (!strcmp(cmd.s, "Authenticate")) {
if (c != ' ') goto missingargs;
c = getword(&arg1);
if (!imparse_isatom(arg1.s)) {
prot_printf(imapd_out, "%s BAD Invalid authenticate mechanism\r\n", tag.s);
eatline(c);
continue;
}
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);
}
else if (!imapd_userid) goto nologin;
else if (!strcmp(cmd.s, "Append")) {
if (c != ' ') goto missingargs;
c = getastring(&arg1);
if (c != ' ') goto missingargs;
cmd_append(tag.s, arg1.s);
}
else goto badcmd;
break;
case 'B':
if (!strcmp(cmd.s, "Bboard")) {
if (c != ' ') goto missingargs;
c = getastring(&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);
}
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);
}
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;
mboxlist_close();
cmd_noop(tag.s, cmd.s);
}
else if (!strcmp(cmd.s, "Copy")) {
if (!imapd_mailbox) goto nomailbox;
usinguid = 0;
if (c != ' ') goto missingargs;
copy:
c = getword(&arg1);
if (c != ' ' || !imparse_issequence(arg1.s)) goto badsequence;
c = getastring(&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);
}
else if (!strcmp(cmd.s, "Create")) {
havepartition = 0;
if (c != ' ') goto missingargs;
c = getastring(&arg1);
if (c == EOF) goto missingargs;
if (c == ' ') {
havepartition = 1;
c = getword(&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);
}
else if (!strcmp(cmd.s, "Close")) {
if (!imapd_mailbox) goto nomailbox;
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
mboxlist_close();
cmd_close(tag.s);
}
else goto badcmd;
break;
case 'D':
if (!strcmp(cmd.s, "Delete")) {
if (c != ' ') goto missingargs;
c = getastring(&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);
}
else if (!strcmp(cmd.s, "Deleteacl")) {
if (c != ' ') goto missingargs;
c = getastring(&arg1);
if (!strcasecmp(arg1.s, "mailbox")) {
if (c != ' ') goto missingargs;
c = getastring(&arg1);
}
if (c != ' ') goto missingargs;
c = getastring(&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, (char *)0);
}
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;
mboxlist_close();
cmd_expunge(tag.s, 0);
}
else if (!strcmp(cmd.s, "Examine")) {
if (c != ' ') goto missingargs;
c = getastring(&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);
}
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(&arg1);
if (c != ' ' || !imparse_issequence(arg1.s)) goto badsequence;
mboxlist_close();
cmd_fetch(tag.s, arg1.s, usinguid);
}
else if (!strcmp(cmd.s, "Find")) {
c = getword(&arg1);
if (c != ' ') goto missingargs;
c = getastring(&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);
}
else goto badcmd;
break;
case 'G':
if (!strcmp(cmd.s, "Getacl")) {
oldform = 0;
if (c != ' ') goto missingargs;
c = getastring(&arg1);
if (!strcasecmp(arg1.s, "mailbox")) {
oldform = 1;
if (c != ' ') goto missingargs;
c = getastring(&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, oldform);
}
else if (!strcmp(cmd.s, "Getquota")) {
if (c != ' ') goto missingargs;
c = getastring(&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);
}
else if (!strcmp(cmd.s, "Getquotaroot")) {
if (c != ' ') goto missingargs;
c = getastring(&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);
}
#ifdef ENABLE_EXPERIMENT_OPTIMIZE_1
/* This command is disabled because it was removed from the
OPTIMIZE-1 extension, now known as UIDPLUS. */
else if (!strcmp(cmd.s, "Getuids")) {
if (!imapd_mailbox) goto nomailbox;
if (c != ' ') goto missingargs;
c = getword(&arg1);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_getuids(tag.s, arg1.s);
}
#endif /* ENABLE_EXPERIMENT_OPTIMIZE_1 */
else goto badcmd;
break;
case 'L':
if (!strcmp(cmd.s, "Login")) {
if (c != ' ' || (c = getastring(&arg1)) != ' ') {
goto missingargs;
}
c = getastring(&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 logged in\r\n", tag.s);
continue;
}
cmd_login(tag.s, arg1.s, arg2.s);
}
else if (!strcmp(cmd.s, "Logout")) {
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
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));
shut_down(0);
}
else if (!imapd_userid) goto nologin;
else if (!strcmp(cmd.s, "List")) {
c = getastring(&arg1);
if (c != ' ') goto missingargs;
c = getastring(&arg2);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_list(tag.s, 0, arg1.s, arg2.s);
}
else if (!strcmp(cmd.s, "Lsub")) {
c = getastring(&arg1);
if (c != ' ') goto missingargs;
c = getastring(&arg2);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_list(tag.s, 1, arg1.s, arg2.s);
}
else if (!strcmp(cmd.s, "Listrights")) {
c = getastring(&arg1);
if (c != ' ') goto missingargs;
c = getastring(&arg2);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
cmd_listrights(tag.s, arg1.s, arg2.s);
}
else goto badcmd;
break;
case 'M':
if (!strcmp(cmd.s, "Myrights")) {
oldform = 0;
if (c != ' ') goto missingargs;
c = getastring(&arg1);
if (!strcasecmp(arg1.s, "mailbox")) {
oldform = 1;
if (c != ' ') goto missingargs;
c = getastring(&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, oldform);
}
else goto badcmd;
break;
case 'N':
if (!strcmp(cmd.s, "Noop")) {
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
mboxlist_close();
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(&arg1);
if (c != ' ') goto missingargs;
c = getword(&arg2);
if (c != ' ') goto missingargs;
c = getword(&arg3);
if (c != ' ') goto missingargs;
c = getword(&arg4);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') goto extraargs;
mboxlist_close();
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(&arg1);
if (c != ' ') goto missingargs;
c = getastring(&arg2);
if (c == EOF) goto missingargs;
if (c == ' ') {
havepartition = 1;
c = getword(&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 goto badcmd;
break;
case 'S':
if (!strcmp(cmd.s, "Store")) {
if (!imapd_mailbox) goto nomailbox;
usinguid = 0;
if (c != ' ') goto missingargs;
store:
c = getword(&arg1);
if (c != ' ' || !imparse_issequence(arg1.s)) goto badsequence;
c = getword(&arg2);
if (c != ' ') goto badsequence;
mboxlist_close();
cmd_store(tag.s, arg1.s, arg2.s, usinguid);
}
else if (!strcmp(cmd.s, "Select")) {
if (c != ' ') goto missingargs;
c = getastring(&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);
}
else if (!strcmp(cmd.s, "Search")) {
if (!imapd_mailbox) goto nomailbox;
usinguid = 0;
if (c != ' ') goto missingargs;
search:
mboxlist_close();
cmd_search(tag.s, usinguid);
}
else if (!strcmp(cmd.s, "Subscribe")) {
if (c != ' ') goto missingargs;
havenamespace = 0;
c = getastring(&arg1);
if (c == ' ') {
havenamespace = 1;
c = getastring(&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);
}
}
else if (!strcmp(cmd.s, "Setacl")) {
if (c != ' ') goto missingargs;
c = getastring(&arg1);
if (!strcasecmp(arg1.s, "mailbox")) {
if (c != ' ') goto missingargs;
c = getastring(&arg1);
}
if (c != ' ') goto missingargs;
c = getastring(&arg2);
if (c != ' ') goto missingargs;
c = getastring(&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);
}
else if (!strcmp(cmd.s, "Setquota")) {
if (c != ' ') goto missingargs;
c = getastring(&arg1);
if (c != ' ') goto missingargs;
cmd_setquota(tag.s, arg1.s);
}
else if (!strcmp(cmd.s, "Status")) {
if (c != ' ') goto missingargs;
c = getastring(&arg1);
if (c != ' ') goto missingargs;
cmd_status(tag.s, arg1.s);
}
else goto badcmd;
break;
case 'U':
if (!strcmp(cmd.s, "Uid")) {
if (!imapd_mailbox) goto nomailbox;
usinguid = 1;
if (c != ' ') goto missingargs;
c = getword(&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, "copy")) {
goto copy;
}
else if (!strcmp(arg1.s, "expunge")) {
c = getword(&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);
}
else {
prot_printf(imapd_out, "%s BAD Unrecognized UID subcommand\r\n", tag.s);
eatline(c);
}
}
else if (!strcmp(cmd.s, "Unsubscribe")) {
if (c != ' ') goto missingargs;
havenamespace = 0;
c = getastring(&arg1);
if (c == ' ') {
havenamespace = 1;
c = getastring(&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);
}
}
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_close(tag.s);
}
else goto badcmd;
break;
default:
badcmd:
prot_printf(imapd_out, "%s BAD Unrecognized command\r\n", tag.s);
eatline(c);
}
continue;
nologin:
prot_printf(imapd_out, "%s BAD Please login first\r\n", tag.s);
eatline(c);
continue;
nomailbox:
prot_printf(imapd_out, "%s BAD Please select a mailbox first\r\n", tag.s);
eatline(c);
continue;
missingargs:
prot_printf(imapd_out, "%s BAD Missing required argument to %s\r\n", tag.s, cmd.s);
eatline(c);
continue;
extraargs:
prot_printf(imapd_out, "%s BAD Unexpected extra arguments to %s\r\n", tag.s, cmd.s);
eatline(c);
continue;
badsequence:
prot_printf(imapd_out, "%s BAD Invalid sequence in %s\r\n", tag.s, cmd.s);
eatline(c);
continue;
badpartition:
prot_printf(imapd_out, "%s BAD Invalid partition name in %s\r\n",
tag.s, cmd.s);
eatline(c);
continue;
}
}
/*
* Perform a LOGIN command
*/
void
cmd_login(tag, user, passwd)
char *tag;
char *user;
char *passwd;
{
char *canon_user;
char *reply = 0;
const char *val;
char buf[MAX_MAILBOX_PATH];
char *p;
FILE *logfile;
int plaintextloginpause;
canon_user = auth_canonifyid(user);
if (!canon_user) {
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 (!strcmp(canon_user, "anonymous")) {
if (config_getswitch("allowanonymouslogin", 0)) {
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";
}
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));
return;
}
}
else if (login_plaintext(canon_user, passwd, &reply) != 0) {
if (reply) {
syslog(LOG_NOTICE, "badlogin: %s plaintext %s %s",
imapd_clienthost, canon_user, reply);
}
sleep(3);
prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(IMAP_INVALID_LOGIN));
return;
}
else {
syslog(LOG_NOTICE, "login: %s %s plaintext %s", imapd_clienthost,
canon_user, reply ? reply : "");
if (plaintextloginpause = config_getint("plaintextloginpause", 0)) {
sleep(plaintextloginpause);
}
}
imapd_authstate = auth_newstate(canon_user, (char *)0);
imapd_userid = xstrdup(canon_user);
proc_register("imapd", imapd_clienthost, imapd_userid, (char *)0);
val = config_getstring("admins", "");
while (*val) {
for (p = (char *)val; *p && !isspace(*p); p++);
strncpy(buf, val, p - val);
buf[p-val] = 0;
if (auth_memberof(imapd_authstate, buf)) {
imapd_userisadmin = 1;
break;
}
val = p;
while (*val && isspace(*val)) val++;
}
if (!reply) reply = "User logged in";
/* Create telemetry log */
sprintf(buf, "%s%s%s/%u", config_dir, FNAME_LOGDIR, imapd_userid,
getpid());
logfile = fopen(buf, "w");
if (logfile) {
prot_setlog(imapd_in, fileno(logfile));
prot_setlog(imapd_out, fileno(logfile));
if (config_getswitch("logtimestamps", 0)) {
prot_setlogtime(imapd_in, &imapd_logtime);
prot_setlogtime(imapd_out, &imapd_logtime);
}
}
prot_printf(imapd_out, "%s OK %s\r\n", tag, reply);
return;
}
/*
* Perform an AUTHENTICATE command
*/
void
cmd_authenticate(tag, authtype)
char *tag;
char *authtype;
{
char *canon_user;
int r;
struct sasl_server *mech;
int (*authproc)();
int outputlen;
char *output;
int inputlen;
static struct buf input;
void *state;
const char *reply = 0;
int protlevel;
char *user;
sasl_encodefunc_t *encodefunc;
sasl_decodefunc_t *decodefunc;
int maxplain;
const char *val;
char buf[MAX_MAILBOX_PATH];
char *p;
FILE *logfile;
lcase(authtype);
r = login_authenticate(authtype, &mech, &authproc, &reply);
if (!r) {
r = mech->start(mech->rock, "imap", authproc,
SASL_PROT_ANY, PROT_BUFSIZE,
imapd_haveaddr ? (struct sockaddr *)&imapd_localaddr : 0,
imapd_haveaddr ? (struct sockaddr *)&imapd_remoteaddr : 0,
&outputlen, &output, &state, &reply);
}
if (r && r != SASL_DONE) {
if (reply) {
syslog(LOG_NOTICE, "badlogin: %s %s %s",
imapd_clienthost, authtype, reply);
}
prot_printf(imapd_out, "%s NO %s\r\n", tag,
error_message(IMAP_INVALID_LOGIN));
return;
}
while (r == 0) {
printauthready(outputlen, (unsigned char *)output);
inputlen = getbase64string(&input);
if (inputlen == -1) {
prot_printf(imapd_out, "%s BAD Invalid base64 string\r\n", tag);
mech->free_state(state);
return;
}
r = mech->auth(state, inputlen, input.s, &outputlen, &output, &reply);
}
if (r != SASL_DONE) {
mech->free_state(state);
if (reply) {
syslog(LOG_NOTICE, "badlogin: %s %s %s",
imapd_clienthost, authtype, reply);
}
sleep(3);
prot_printf(imapd_out, "%s NO %s\r\n", tag,
error_message(IMAP_INVALID_LOGIN));
return;
}
mech->query_state(state, &user, &protlevel, &encodefunc, &decodefunc,
&maxplain);
canon_user = auth_canonifyid(user);
if (!canon_user) {
syslog(LOG_NOTICE, "badlogin: %s %s %s bad userid",
imapd_clienthost, authtype, beautify_string(user));
mech->free_state(state);
prot_printf(imapd_out, "%s NO %s\r\n", tag,
error_message(IMAP_INVALID_USER));
return;
}
imapd_authstate = auth_newstate(canon_user, mech->get_cacheid(state));
imapd_userid = xstrdup(canon_user);
proc_register("imapd", imapd_clienthost, imapd_userid, (char *)0);
val = config_getstring("admins", "");
while (*val) {
for (p = (char *)val; *p && !isspace(*p); p++);
strncpy(buf, val, p - val);
buf[p-val] = 0;
if (auth_memberof(imapd_authstate, buf)) {
imapd_userisadmin = 1;
break;
}
val = p;
while (*val && isspace(*val)) val++;
}
if (!reply) reply = "User logged in";
syslog(LOG_NOTICE, "login: %s %s %s %s", imapd_clienthost, canon_user,
authtype, reply ? reply : "");
prot_printf(imapd_out, "%s OK %s (%s)\r\n", tag, reply,
sasl_prottostring(protlevel));
if (encodefunc || decodefunc) {
prot_setfunc(imapd_in, decodefunc, state, 0);
prot_setfunc(imapd_out, encodefunc, state, maxplain);
}
else {
mech->free_state(state);
}
/* Create telemetry log */
sprintf(buf, "%s%s%s/%u", config_dir, FNAME_LOGDIR, imapd_userid,
getpid());
logfile = fopen(buf, "w");
if (logfile) {
prot_setlog(imapd_in, fileno(logfile));
prot_setlog(imapd_out, fileno(logfile));
if (config_getswitch("logtimestamps", 0)) {
prot_setlogtime(imapd_in, &imapd_logtime);
prot_setlogtime(imapd_out, &imapd_logtime);
}
}
return;
}
/*
* Perform a NOOP command
*/
void
cmd_noop(tag, cmd)
char *tag;
char *cmd;
{
if (imapd_mailbox) {
index_check(imapd_mailbox, 0, 1);
}
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
/*
* Perform a CAPABILITY command
*/
void
cmd_capability(tag)
char *tag;
{
if (imapd_mailbox) {
index_check(imapd_mailbox, 0, 0);
}
prot_printf(imapd_out,
"* CAPABILITY IMAP4 IMAP4rev1 ACL QUOTA LITERAL+ NAMESPACE UIDPLUS");
/* XXX */
prot_printf(imapd_out,
" X-NON-HIERARCHICAL-RENAME NO_ATOMIC_RENAME");
prot_printf(imapd_out, "%s", login_capabilities());
prot_printf(imapd_out, " UNSELECT");
#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));
}
/*
* Parse and perform an APPEND command.
* The command has been parsed up to and including
* the mailbox name.
*/
#define FLAGGROW 10
void
cmd_append(tag, name)
char *tag;
char *name;
{
int c;
char **flag = 0;
int nflags = 0, flagalloc = 0;
static struct buf arg;
char *p;
time_t internaldate = time(0);
unsigned size = 0;
int sawdigit = 0;
int isnowait = 0;
int r;
char mailboxname[MAX_MAILBOX_NAME+1];
struct mailbox mailbox;
unsigned long uidvalidity, newuid;
/* Parse flags */
c = getword(&arg);
if (c == '(' && !arg.s[0]) {
do {
c = getword(&arg);
if (arg.s[0] == '\\') {
lcase(arg.s);
if (strcmp(arg.s, "\\seen") && strcmp(arg.s, "\\answered") &&
strcmp(arg.s, "\\flagged") && strcmp(arg.s, "\\draft") &&
strcmp(arg.s, "\\deleted")) {
prot_printf(imapd_out, "%s BAD Invalid system flag in Append command\r\n",tag);
eatline(c);
goto freeflags;
}
}
else if (!imparse_isatom(arg.s)) {
if (!nflags && !arg.s[0] && c == ')') break; /* empty list */
prot_printf(imapd_out, "%s BAD Invalid flag name %s in Append command\r\n",
tag, arg.s);
eatline(c);
goto freeflags;
}
if (nflags == flagalloc) {
flagalloc += FLAGGROW;
flag = (char **)xrealloc((char *)flag, flagalloc*sizeof(char *));
}
flag[nflags++] = xstrdup(arg.s);
} while (c == ' ');
if (c != ')') {
prot_printf(imapd_out,
"%s BAD Missing space or ) after flag name in Append command\r\n",
tag);
eatline(c);
goto freeflags;
}
c = prot_getc(imapd_in);
if (c != ' ') {
prot_printf(imapd_out,
"%s BAD Missing space after flag list in Append command\r\n",
tag);
eatline(c);
goto freeflags;
}
c = getword(&arg);
}
/* Parse internaldate */
if (c == '\"' && !arg.s[0]) {
prot_ungetc(c, imapd_in);
c = getdatetime(&internaldate);
if (c != ' ') {
prot_printf(imapd_out, "%s BAD Invalid date-time in Append command\r\n", tag);
eatline(c);
goto freeflags;
}
c = getword(&arg);
}
if (arg.s[0] != '{') {
prot_printf(imapd_out, "%s BAD Missing required argument to Append command\r\n",
tag);
eatline(c);
goto freeflags;
}
/* Read size from literal */
for (p = arg.s + 1; *p && isdigit(*p); p++) {
sawdigit++;
size = size*10 + *p - '0';
}
if (*p == '+') {
isnowait++;
p++;
}
if (c == '\r') {
c = prot_getc(imapd_in);
}
else {
prot_ungetc(c, imapd_in);
c = ' '; /* Force a syntax error */
}
if (*p != '}' || p[1] || c != '\n' || !sawdigit) {
prot_printf(imapd_out, "%s BAD Invalid literal in Append command\r\n", tag);
eatline(c);
goto freeflags;
}
if (size < 2) {
prot_printf(imapd_out, "%s NO %s\r\n", tag,
error_message(IMAP_MESSAGE_NOBLANKLINE));
eatline(c);
goto freeflags;
}
/* Set up the append */
r = mboxname_tointernal(name, imapd_userid, mailboxname);
if (!r) {
r = append_setup(&mailbox, mailboxname, MAILBOX_FORMAT_NORMAL,
imapd_authstate, ACL_INSERT, size);
}
if (r) {
if (isnowait) {
/* Eat message and trailing newline */
while (size--) c = prot_getc(imapd_in);
eatline(' ');
}
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));
goto freeflags;
}
if (!isnowait) {
/* Tell client to send the message */
prot_printf(imapd_out, "+ go ahead\r\n");
prot_flush(imapd_out);
}
/* Perform the rest of the append */
r = append_fromstream(&mailbox, imapd_in, size, internaldate, flag, nflags,
imapd_userid);
uidvalidity = mailbox.uidvalidity;
newuid = mailbox.last_uid;
mailbox_close(&mailbox);
/* Parse newline terminating command */
c = prot_getc(imapd_in);
if (c == '\r') c = prot_getc(imapd_in);
if (c != '\n') {
if (c == EOF) return;
prot_printf(imapd_out, "* BAD Junk after literal in APPEND command\r\n");
eatline(c);
}
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 [APPENDUID %u %u] %s\r\n", tag,
uidvalidity, newuid,
error_message(IMAP_OK_COMPLETED));
}
freeflags:
while (nflags--) {
free(flag[nflags]);
}
if (flag) free((char *)flag);
}
/*
* Perform a SELECT/EXAMINE/BBOARD command
*/
void
cmd_select(tag, cmd, name)
char *tag;
char *cmd;
char *name;
{
struct mailbox mailbox;
char mailboxname[MAX_MAILBOX_NAME+1];
int r = 0;
int usage;
int doclose = 0;
if (imapd_mailbox) {
index_closemailbox(imapd_mailbox);
mailbox_close(imapd_mailbox);
imapd_mailbox = 0;
}
if (cmd[0] == 'B') {
/* BBoard namespace is empty */
r = IMAP_MAILBOX_NONEXISTENT;
}
else {
r = mboxname_tointernal(name, imapd_userid, mailboxname);
}
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;
}
if (mailbox.format == MAILBOX_FORMAT_NETNEWS) {
(void) mailbox_expungenews(&mailbox);
}
mboxstruct = mailbox;
imapd_mailbox = &mboxstruct;
index_newmailbox(imapd_mailbox, cmd[0] == 'E');
/* Examine command puts mailbox in read-only mode */
if (cmd[0] == 'E') {
imapd_mailbox->myrights &= ~(ACL_SEEN|ACL_WRITE|ACL_DELETE);
}
if (imapd_mailbox->myrights & ACL_DELETE) {
/* Warn if mailbox is close to or over quota */
mailbox_read_quota(&imapd_mailbox->quota);
if (imapd_mailbox->quota.limit > 0) {
usage = imapd_mailbox->quota.used * 100 /
(imapd_mailbox->quota.limit * QUOTA_UNITS);
if (usage >= 100) {
prot_printf(imapd_out, "* NO [ALERT] %s\r\n",
error_message(IMAP_NO_OVERQUOTA));
}
else if (usage > config_getint("quotawarn", 90)) {
prot_printf(imapd_out, "* NO [ALERT] ");
prot_printf(imapd_out, error_message(IMAP_NO_CLOSEQUOTA),
usage);
prot_printf(imapd_out, "\r\n");
}
}
}
prot_printf(imapd_out, "%s OK [READ-%s] %s\r\n", tag,
(imapd_mailbox->myrights & (ACL_WRITE|ACL_DELETE)) ?
"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);
}
/*
* Perform a CLOSE command
*/
void
cmd_close(tag)
char *tag;
{
int r;
if (!(imapd_mailbox->myrights & ACL_DELETE)) 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));
}
}
#ifdef ENABLE_EXPERIMENT
/*
* Perform an UNSELECT command -- for some support of IMAP proxy.
* Just like close except no expunge.
*/
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));
}
#endif
/*
* Parse and perform a FETCH/UID FETCH command
* The command has been parsed up to and including
* the sequence
*/
void
cmd_fetch(tag, sequence, usinguid)
char *tag;
char *sequence;
int usinguid;
{
char *cmd = usinguid ? "UID Fetch" : "Fetch";
static struct buf fetchatt, fieldname;
int c, i;
int inlist = 0;
int fetchitems = 0;
struct fetchargs fetchargs;
struct strlist *newfields = 0;
char *p, *section;
fetchargs = zerofetchargs;
c = getword(&fetchatt);
if (c == '(' && !fetchatt.s[0]) {
inlist = 1;
c = getword(&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 (!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 (*p == 'P') {
p = section += 5;
}
else {
fetchitems |= FETCH_SETSEEN;
}
while (isdigit(*p) || *p == '.') {
if (*p == '.' && !isdigit(p[-1])) break;
/* Obsolete section 0 can only occur before close brace */
if (*p == '0' && !isdigit(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 not top-level or a HEADER.FIELDS.NOT, can't pull
* the headers out of the cache.
*/
if (p != section || p[13] != '\0') {
fetchitems |= FETCH_UNCACHEDHEADER;
}
if (c != ' ') {
prot_printf(imapd_out,
"%s BAD Missing required argument to %s %s\r\n",
tag, cmd, fetchatt.s);
eatline(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(c);
goto freeargs;
}
do {
c = getastring(&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(c);
goto freeargs;
}
appendstrlist(&newfields, fieldname.s);
if (!(fetchitems & FETCH_UNCACHEDHEADER)) {
for (i=0; i<mailbox_num_cache_header; i++) {
if (!strcasecmp(mailbox_cache_header_name[i],
fieldname.s)) break;
}
if (i == mailbox_num_cache_header) {
fetchitems |= FETCH_UNCACHEDHEADER;
}
}
} while (c == ' ');
if (c != ')') {
prot_printf(imapd_out, "%s BAD Missing required close parenthesis in %s %s\r\n",
tag, cmd, fetchatt.s);
eatline(c);
goto freeargs;
}
/* Grab/parse the ]<x.y> part */
c = getword(&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(c);
goto freeargs;
}
if (*p == '<' && isdigit(p[1])) {
p += 2;
while (isdigit(*p)) p++;
if (*p == '.' && p[1] >= '1' && p[1] <= '9') {
p += 2;
while (isdigit(*p)) p++;
}
else p--;
if (*p != '>') {
prot_printf(imapd_out, "%s BAD Invalid body partial\r\n", tag);
eatline(c);
goto freeargs;
}
p++;
}
if (*p) {
prot_printf(imapd_out, "%s BAD Junk after body section\r\n", tag);
eatline(c);
goto freeargs;
}
appendfieldlist(&fetchargs.fsections,
section, newfields, fieldname.s);
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(c);
goto freeargs;
}
p++;
if (*p == '<' && isdigit(p[1])) {
p += 2;
while (isdigit(*p)) p++;
if (*p == '.' && p[1] >= '1' && p[1] <= '9') {
p += 2;
while (isdigit(*p)) p++;
}
else p--;
if (*p != '>') {
prot_printf(imapd_out, "%s BAD Invalid body partial\r\n", tag);
eatline(c);
goto freeargs;
}
p++;
}
if (*p) {
prot_printf(imapd_out, "%s BAD Junk after body section\r\n", tag);
eatline(c);
goto freeargs;
}
appendstrlist(&fetchargs.bodysections, section);
}
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(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(c);
goto freeargs;
}
do {
c = getastring(&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(c);
goto freeargs;
}
lcase(fieldname.s);;
appendstrlist(strlen(fetchatt.s) == 19 ?
&fetchargs.headers : &fetchargs.headers_not,
fieldname.s);
if (strlen(fetchatt.s) != 19) {
fetchitems |= FETCH_UNCACHEDHEADER;
}
if (!(fetchitems & FETCH_UNCACHEDHEADER)) {
for (i=0; i<mailbox_num_cache_header; i++) {
if (!strcmp(mailbox_cache_header_name[i],
fieldname.s)) break;
}
if (i == mailbox_num_cache_header) {
fetchitems |= FETCH_UNCACHEDHEADER;
}
}
} while (c == ' ');
if (c != ')') {
prot_printf(imapd_out, "%s BAD Missing required close parenthesis in %s %s\r\n",
tag, cmd, fetchatt.s);
eatline(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(c);
goto freeargs;
}
if (inlist && c == ' ') c = getword(&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(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(c);
goto freeargs;
}
if (!fetchitems && !fetchargs.bodysections &&
!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;
index_fetch(imapd_mailbox, sequence, usinguid, &fetchargs);
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
freeargs:
freestrlist(newfields);
freestrlist(fetchargs.bodysections);
freefieldlist(fetchargs.fsections);
freestrlist(fetchargs.headers);
freestrlist(fetchargs.headers_not);
}
/*
* Perform a PARTIAL command
*/
void
cmd_partial(tag, msgno, data, start, count)
char *tag;
char *msgno;
char *data;
char *start;
char *count;
{
char *p;
struct fetchargs fetchargs;
char *section;
fetchargs = zerofetchargs;
for (p = msgno; *p; p++) {
if (!isdigit(*p)) break;
}
if (*p || !*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 (*p == 'p') {
p = section += 5;
}
else {
fetchargs.fetchitems = FETCH_SETSEEN;
}
while (isdigit(*p) || *p == '.') {
if (*p == '.' && (p == section || !isdigit(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 = '\0';
appendstrlist(&fetchargs.bodysections, section);
}
else {
prot_printf(imapd_out, "%s BAD Invalid Partial item\r\n", tag);
freestrlist(fetchargs.bodysections);
return;
}
for (p = start; *p; p++) {
if (!isdigit(*p)) break;
fetchargs.start_octet = fetchargs.start_octet*10 + *p - '0';
}
if (*p || !fetchargs.start_octet) {
prot_printf(imapd_out, "%s BAD Invalid starting octet\r\n", tag);
freestrlist(fetchargs.bodysections);
return;
}
for (p = count; *p; p++) {
if (!isdigit(*p)) break;
fetchargs.octet_count = fetchargs.octet_count*10 + *p - '0';
}
if (*p || !*count) {
prot_printf(imapd_out, "%s BAD Invalid octet count\r\n", tag);
freestrlist(fetchargs.bodysections);
return;
}
index_fetch(imapd_mailbox, msgno, 0, &fetchargs);
index_check(imapd_mailbox, 0, 0);
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
freestrlist(fetchargs.bodysections);
}
/*
* Parse and perform a STORE/UID STORE command
* The command has been parsed up to and including
* the FLAGS/+FLAGS/-FLAGS
*/
void
cmd_store(tag, sequence, operation, usinguid)
char *tag;
char *sequence;
char *operation;
int usinguid;
{
char *cmd = usinguid ? "UID Store" : "Store";
struct storeargs storeargs;
static struct storeargs zerostoreargs;
static struct buf flagname;
int len, c;
char **flag = 0;
int nflags = 0, flagalloc = 0;
int flagsparsed = 0, inlist = 0;
int r;
storeargs = zerostoreargs;
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(' ');
return;
}
for (;;) {
c = getword(&flagname);
if (c == '(' && !flagname.s[0] && !flagsparsed && !inlist) {
inlist = 1;
continue;
}
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(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(c);
goto freeflags;
}
else {
if (nflags == flagalloc) {
flagalloc += FLAGGROW;
flag = (char **)xrealloc((char *)flag,
flagalloc*sizeof(char *));
}
flag[nflags++] = xstrdup(flagname.s);
}
flagsparsed++;
if (c != ' ') 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(c);
return;
}
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(c);
return;
}
if (!flagsparsed) {
prot_printf(imapd_out, "%s BAD Missing required argument to %s\r\n", tag, cmd);
return;
}
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;
static struct searchargs zerosearchargs;
searchargs = (struct searchargs *)xmalloc(sizeof(struct searchargs));
*searchargs = zerosearchargs;
c = getsearchprogram(tag, searchargs, &charset, 1);
if (c == EOF) {
eatline(' ');
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(c);
freesearchargs(searchargs);
return;
}
if (charset == -1) {
prot_printf(imapd_out, "%s NO %s\r\n", tag,
error_message(IMAP_UNRECOGNIZED_CHARSET));
}
else {
index_search(imapd_mailbox, searchargs, usinguid);
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
freesearchargs(searchargs);
}
/*
* Perform a COPY/UID COPY command
*/
void
cmd_copy(tag, sequence, name, usinguid)
char *tag;
char *sequence;
char *name;
int usinguid;
{
- char *cmd = usinguid ? "UID Copy" : "Copy";
int r;
char mailboxname[MAX_MAILBOX_NAME+1];
char *copyuid;
r = mboxname_tointernal(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 {
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
}
}
/*
* Perform an EXPUNGE command
*/
void
cmd_expunge(tag, sequence)
char *tag;
char *sequence;
{
int r;
if (!(imapd_mailbox->myrights & ACL_DELETE)) 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));
}
}
/*
* Perform a CREATE command
*/
void
cmd_create(tag, name, partition)
char *tag;
char *name;
char *partition;
{
int r = 0;
char mailboxname[MAX_MAILBOX_NAME+1];
int autocreatequota;
if (partition && !imapd_userisadmin) {
r = IMAP_PERMISSION_DENIED;
}
if (name[0] && name[strlen(name)-1] == '.') {
/* We don't care about trailing hierarchy delimiters. */
name[strlen(name)-1] = '\0';
}
if (!r) {
r = mboxname_tointernal(name, imapd_userid, mailboxname);
}
if (!r) {
r = mboxlist_createmailbox(mailboxname, MAILBOX_FORMAT_NORMAL, partition,
imapd_userisadmin, imapd_userid, imapd_authstate);
if (r == IMAP_PERMISSION_DENIED && !strcasecmp(name, "INBOX") &&
(autocreatequota = config_getint("autocreatequota", 0))) {
/* Auto create */
r = mboxlist_createmailbox(mailboxname, MAILBOX_FORMAT_NORMAL,
partition, 1, imapd_userid, imapd_authstate);
if (!r && autocreatequota > 0) {
(void) mboxlist_setquota(mailboxname, autocreatequota);
}
}
}
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));
}
}
/*
* Perform a DELETE command
*/
void
cmd_delete(tag, name)
char *tag;
char *name;
{
int r;
char mailboxname[MAX_MAILBOX_NAME+1];
r = mboxname_tointernal(name, imapd_userid, mailboxname);
if (!r) {
r = mboxlist_deletemailbox(mailboxname, imapd_userisadmin,
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));
}
}
/*
* Perform a RENAME command
*/
void
cmd_rename(tag, oldname, newname, partition)
char *tag;
char *oldname;
char *newname;
char *partition;
{
int r;
char oldmailboxname[MAX_MAILBOX_NAME+1];
char newmailboxname[MAX_MAILBOX_NAME+1];
if (partition && !imapd_userisadmin) {
r = IMAP_PERMISSION_DENIED;
}
else {
r = mboxname_tointernal(oldname, imapd_userid, oldmailboxname);
}
if (!r) {
r = mboxname_tointernal(newname, imapd_userid, newmailboxname);
}
if (!r) {
r = mboxlist_renamemailbox(oldmailboxname, newmailboxname, partition,
imapd_userisadmin, imapd_userid, imapd_authstate);
}
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));
}
}
/*
* Perform a FIND command
*/
void
cmd_find(tag, namespace, pattern)
char *tag;
char *namespace;
char *pattern;
{
char *p;
lcase(namespace);
for (p = pattern; *p; p++) {
if (*p == '%') *p = '?';
}
if (!strcmp(namespace, "mailboxes")) {
mboxlist_findsub(pattern, imapd_userisadmin, imapd_userid, imapd_authstate,
mailboxdata);
}
else if (!strcmp(namespace, "all.mailboxes")) {
mboxlist_findall(pattern, imapd_userisadmin, imapd_userid,
imapd_authstate, mailboxdata, NULL);
}
else if (!strcmp(namespace, "bboards")
|| !strcmp(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));
}
/*
* Perform a LIST or LSUB command
*/
void
cmd_list(tag, subscribed, reference, pattern)
char *tag;
int subscribed;
char *reference;
char *pattern;
{
char *buf = NULL;
int patlen = 0;
int reflen = 0;
static int ignorereference = 0;
/* Ignore the reference argument?
(the behavior in 1.5.10 & older) */
if (ignorereference == 0) {
ignorereference = config_getswitch("ignorereference", 0);
}
/* Reset state in mstringdata */
mstringdata(NULL, NULL, 0, 0);
if (!pattern[0] && !subscribed) {
/* Special case: query top-level hierarchy separator */
prot_printf(imapd_out, "* LIST (\\Noselect) \".\" \"\"\r\n");
} else {
/* Do we need to concatenate fields? */
if (!ignorereference || pattern[0] == '.') {
/* Either
* - name begins with dot
* - we're configured to honor the reference argument */
/* Allocate a buffer, figure out how to stick the arguments
together, do it, then do that instead of using pattern. */
patlen = strlen(pattern);
reflen = strlen(reference);
buf = xmalloc(patlen + reflen + 1);
buf[0] = '\0';
if (*reference) {
/* check for LIST A. .B, change to LIST "" A.B */
if (reference[reflen-1] == '.' && pattern[0] == '.') {
reference[--reflen] = '\0';
}
strcpy(buf, reference);
}
strcat(buf, pattern);
pattern = buf;
}
if (subscribed) {
mboxlist_findsub(pattern, imapd_userisadmin, imapd_userid,
imapd_authstate, lsubdata, NULL);
lsubdata((char *)0, 0, 0, 0);
}
else {
mboxlist_findall(pattern, imapd_userisadmin, imapd_userid,
imapd_authstate, listdata, NULL);
listdata((char *)0, 0, 0, 0);
}
if (buf) free(buf);
}
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
/*
* Perform a SUBSCRIBE (add is nonzero) or
* UNSUBSCRIBE (add is zero) command
*/
void
cmd_changesub(tag, namespace, name, add)
char *tag;
char *namespace;
char *name;
int add;
{
int r;
char mailboxname[MAX_MAILBOX_NAME+1];
if (namespace) lcase(namespace);
if (!namespace || !strcmp(namespace, "mailbox")) {
r = mboxname_tointernal(name, imapd_userid, mailboxname);
if (!r) {
r = mboxlist_changesub(mailboxname, imapd_userid, imapd_authstate, add);
}
}
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));
}
}
/*
* Perform a GETACL command
*/
void
cmd_getacl(tag, name, oldform)
char *tag;
char *name;
int oldform;
{
char mailboxname[MAX_MAILBOX_NAME+1];
int r, access;
char *acl;
char *rights, *nextid;
r = mboxname_tointernal(name, imapd_userid, mailboxname);
if (!r) {
r = mboxlist_lookup(mailboxname, (char **)0, &acl);
}
if (!r) {
access = 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;
}
if (oldform) {
while (acl) {
rights = strchr(acl, '\t');
if (!rights) break;
*rights++ = '\0';
nextid = strchr(rights, '\t');
if (!nextid) break;
*nextid++ = '\0';
prot_printf(imapd_out, "* ACL MAILBOX ");
printastring(name);
prot_printf(imapd_out, " ");
printastring(acl);
prot_printf(imapd_out, " ");
printastring(rights);
prot_printf(imapd_out, "\r\n");
acl = nextid;
}
}
else {
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));
}
/*
* Perform a LISTRIGHTS command
*/
void
cmd_listrights(tag, name, identifier)
char *tag;
char *name;
char *identifier;
{
char mailboxname[MAX_MAILBOX_NAME+1];
int r, rights;
char *canon_identifier;
int canonidlen;
char *acl;
char *rightsdesc;
r = mboxname_tointernal(name, imapd_userid, mailboxname);
if (!r) {
r = mboxlist_lookup(mailboxname, (char **)0, &acl);
}
if (!r) {
rights = acl_myrights(imapd_authstate, acl);
if (!rights && !imapd_userisadmin &&
!mboxname_userownsmailbox(imapd_userid, mailboxname)) {
r = IMAP_MAILBOX_NONEXISTENT;
}
}
if (!r) {
canon_identifier = auth_canonifyid(identifier);
if (canon_identifier) canonidlen = strlen(canon_identifier);
if (!canon_identifier) {
rightsdesc = "\"\"";
}
else if (!strncmp(mailboxname, "user.", 5) &&
!strchr(canon_identifier, '.') &&
!strncmp(mailboxname+5, canon_identifier, canonidlen) &&
(mailboxname[5+canonidlen] == '\0' ||
mailboxname[5+canonidlen] == '.')) {
rightsdesc = "lca r s w i p d 0 1 2 3 4 5 6 7 8 9";
}
else {
rightsdesc = "\"\" l r s w i p c d a 0 1 2 3 4 5 6 7 8 9";
}
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));
}
/*
* Perform a MYRIGHTS command
*/
void
cmd_myrights(tag, name, oldform)
char *tag;
char *name;
int oldform;
{
char mailboxname[MAX_MAILBOX_NAME+1];
int r, rights;
char *acl;
char str[ACL_MAXSTR];
r = mboxname_tointernal(name, imapd_userid, mailboxname);
if (!r) {
r = mboxlist_lookup(mailboxname, (char **)0, &acl);
}
if (!r) {
rights = acl_myrights(imapd_authstate, acl);
/* Add in implicit rights */
if (imapd_userisadmin ||
mboxname_userownsmailbox(imapd_userid, mailboxname)) {
rights |= ACL_LOOKUP|ACL_ADMIN;
}
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 ");
if (oldform) prot_printf(imapd_out, "MAILBOX ");
printastring(name);
prot_printf(imapd_out, " ");
printastring(acl_masktostr(rights, str));
prot_printf(imapd_out, "\r\n%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
/*
* Perform a SETACL command
*/
void
cmd_setacl(tag, name, identifier, rights)
char *tag;
char *name;
char *identifier;
char *rights;
{
int r;
- char *cmd = rights ? "Setacl" : "Deleteacl";
char mailboxname[MAX_MAILBOX_NAME+1];
r = mboxname_tointernal(name, imapd_userid, mailboxname);
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));
}
/*
* Perform a GETQUOTA command
*/
void
cmd_getquota(tag, name)
char *tag;
char *name;
{
int r;
struct quota quota;
char buf[MAX_MAILBOX_PATH];
quota.root = name;
quota.fd = -1;
if (!imapd_userisadmin) r = IMAP_PERMISSION_DENIED;
else {
sprintf(buf, "%s%s%s", config_dir, FNAME_QUOTADIR, quota.root);
quota.fd = open(buf, O_RDWR, 0);
if (quota.fd == -1) {
r = IMAP_QUOTAROOT_NONEXISTENT;
}
else {
r = mailbox_read_quota("a);
}
}
if (!r) {
prot_printf(imapd_out, "* QUOTA ");
printastring(quota.root);
prot_printf(imapd_out, " (");
if (quota.limit >= 0) {
prot_printf(imapd_out, "STORAGE %u %d",
quota.used/QUOTA_UNITS, quota.limit);
}
prot_printf(imapd_out, ")\r\n");
}
if (quota.fd != -1) {
close(quota.fd);
}
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));
}
/*
* Perform a GETQUOTAROOT command
*/
void
cmd_getquotaroot(tag, name)
char *tag;
char *name;
{
char mailboxname[MAX_MAILBOX_NAME+1];
struct mailbox mailbox;
int r;
int doclose = 0;
r = mboxname_tointernal(name, imapd_userid, mailboxname);
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) {
prot_printf(imapd_out, " ");
printastring(mailbox.quota.root);
r = mailbox_read_quota(&mailbox.quota);
if (!r) {
prot_printf(imapd_out, "\r\n* QUOTA ");
printastring(mailbox.quota.root);
prot_printf(imapd_out, " (");
if (mailbox.quota.limit >= 0) {
prot_printf(imapd_out, "STORAGE %u %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));
}
/*
* Parse and perform a SETQUOTA command
* The command has been parsed up to the resource list
*/
void
cmd_setquota(tag, quotaroot)
char *tag;
char *quotaroot;
{
int newquota = -1;
int badresource = 0;
int c;
static struct buf arg;
char *p;
int r;
c = prot_getc(imapd_in);
if (c != '(') goto badlist;
c = getword(&arg);
if (c != ')' || arg.s[0] != '\0') {
for (;;) {
if (c != ' ') goto badlist;
if (strcasecmp(arg.s, "storage") != 0) badresource = 1;
c = getword(&arg);
if (c != ' ' && c != ')') goto badlist;
if (arg.s[0] == '\0') goto badlist;
newquota = 0;
for (p = arg.s; *p; p++) {
if (!isdigit(*p)) goto badlist;
newquota = newquota * 10 + *p - '0';
}
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(c);
return;
}
if (badresource) r = IMAP_UNSUPPORTED_QUOTA;
else if (!imapd_userisadmin) r = IMAP_PERMISSION_DENIED;
else {
r = mboxlist_setquota(quotaroot, newquota);
}
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(c);
}
/*
* Parse and perform a STATUS command
* The command has been parsed up to the attribute list
*/
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(&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(c);
return;
}
if (c == ' ') c = getword(&arg);
else break;
}
if (c != ')') {
prot_printf(imapd_out,
"%s BAD Missing close parenthesis in Status\r\n", tag);
eatline(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(c);
return;
}
/*
* Perform a full checkpoint of any open mailbox, in case we're
* doing a STATUS check of the current mailbox.
*/
if (imapd_mailbox) {
index_check(imapd_mailbox, 0, 1);
}
r = mboxname_tointernal(name, imapd_userid, mailboxname);
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(c);
}
#ifdef ENABLE_EXPERIMENT_OPTIMIZE_1
/* This extension has been superceded by UIDPLUS and therefore this code
* is not used. */
/*
* Perform a GETUIDS command
*/
void
cmd_getuids(tag, startuid)
char *tag;
char *startuid;
{
char *p;
unsigned uid = 0;
for (p = startuid; *p; p++) {
if (!isdigit(*p)) break;
uid = uid * 10 + *p - '0';
}
if (*p || !uid) {
prot_printf(imapd_out, "%s BAD Invalid UID\r\n", tag);
return;
}
index_check(imapd_mailbox, 0, 0);
index_getuids(imapd_mailbox, uid);
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
}
#endif /* ENABLE_EXPERIMENT_OPTIMIZE_1 */
#ifdef ENABLE_X_NETSCAPE_HACK
/*
* Reply to Netscape's crock with a crock of my own
*/
void
cmd_netscrape(tag)
char *tag;
{
char *url;
/* so tempting, and yet ... */
/* url = "http://random.yahoo.com/ryl/"; */
url = config_getstring("netscapeurl",
"http://andrew2.andrew.cmu.edu/cyrus/imapd/netscape-admin.html");
/* I only know of three things to reply with: */
prot_printf(imapd_out,
"* OK [NETSCAPE] Carnegie Mellon Cyrus IMAP\r\n* VERSION %s\r\n",
CYRUS_VERSION);
prot_printf(imapd_out,
"* ACCOUNT-URL %s\r\n%s OK %s\r\n",
url, tag, error_message(IMAP_OK_COMPLETED));
}
#endif /* ENABLE_X_NETSCAPE_HACK */
/* Callback for cmd_namespace to be passed to mboxlist_findall.
* For each top-level mailbox found, print a bit of the response
* if it is a shared namespace. The rock is used as an integer in
* order to ensure the namespace response is correct on a server with
* no shared namespace.
*/
/* locations to set if the user can see a given namespace */
#define NAMESPACE_INBOX 0
#define NAMESPACE_USER 1
#define NAMESPACE_SHARED 2
static int namespacedata(name, matchlen, maycreate, rock)
char* name;
int matchlen;
int maycreate;
void* rock;
{
int* sawone = (int*) rock;
if (!name) {
return 0;
}
if (!(strncmp(name, "INBOX.", 6))) {
/* The user has a "personal" namespace. */
sawone[NAMESPACE_INBOX] = 1;
} else if (!(strncmp(name, "user.", 5))) {
/* The user can see the "other users" namespace. */
sawone[NAMESPACE_USER] = 1;
} else {
/* The user can see the "shared" namespace. */
sawone[NAMESPACE_SHARED] = 1;
}
return 0;
}
/*
* Print out a response to the NAMESPACE command defined by
* RFC 2342.
*/
void cmd_namespace(tag)
char* tag;
{
int sawone[3] = {0, 0, 0};
char* pattern = xstrdup("%");
/* now find all the exciting toplevel namespaces */
mboxlist_findall(pattern, imapd_userisadmin, imapd_userid,
imapd_authstate, namespacedata, (void*) sawone);
prot_printf(imapd_out, "* NAMESPACE %s %s %s\r\n",
(sawone[NAMESPACE_INBOX]) ? "((\"INBOX.\" \".\"))" : "NIL",
(sawone[NAMESPACE_USER]) ? "((\"user.\" \".\"))" : "NIL",
(sawone[NAMESPACE_SHARED]) ? "((\"\" \".\"))" : "NIL");
prot_printf(imapd_out, "%s OK %s\r\n", tag,
error_message(IMAP_OK_COMPLETED));
free(pattern);
}
/*
* Parse a word
* (token not containing whitespace, parens, or double quotes)
*/
#define BUFGROWSIZE 100
int getword(buf)
struct buf *buf;
{
int c;
int len = 0;
if (buf->alloc == 0) {
buf->alloc = BUFGROWSIZE;
buf->s = xmalloc(buf->alloc+1);
}
for (;;) {
c = prot_getc(imapd_in);
if (c == EOF || isspace(c) || c == '(' || c == ')' || c == '\"') {
buf->s[len] = '\0';
return c;
}
if (len == buf->alloc) {
buf->alloc += BUFGROWSIZE;
buf->s = xrealloc(buf->s, buf->alloc+1);
}
buf->s[len++] = c;
}
}
/*
* Parse an astring
* (atom, quoted-string, or literal)
*/
int getastring(buf)
struct buf *buf;
{
int c;
int i, len = 0;
int sawdigit = 0;
int isnowait;
if (buf->alloc == 0) {
buf->alloc = BUFGROWSIZE;
buf->s = xmalloc(buf->alloc+1);
}
c = prot_getc(imapd_in);
switch (c) {
case EOF:
case ' ':
case '(':
case ')':
case '\r':
case '\n':
/* Invalid starting character */
buf->s[0] = '\0';
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
default:
/*
* Atom -- server is liberal in accepting specials other
* than whitespace, parens, or double quotes
*/
for (;;) {
if (c == EOF || isspace(c) || c == '(' || c == ')' || c == '\"') {
buf->s[len] = '\0';
return c;
}
if (len == buf->alloc) {
buf->alloc += BUFGROWSIZE;
buf->s = xrealloc(buf->s, buf->alloc+1);
}
buf->s[len++] = c;
c = prot_getc(imapd_in);
}
case '\"':
/*
* Quoted-string. Server is liberal in accepting qspecials
* other than double-quote, CR, and LF.
*/
for (;;) {
c = prot_getc(imapd_in);
if (c == '\\') {
c = prot_getc(imapd_in);
}
else if (c == '\"') {
buf->s[len] = '\0';
return prot_getc(imapd_in);
}
else if (c == EOF || c == '\r' || c == '\n') {
buf->s[len] = '\0';
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
}
if (len == buf->alloc) {
buf->alloc += BUFGROWSIZE;
buf->s = xrealloc(buf->s, buf->alloc+1);
}
buf->s[len++] = c;
}
case '{':
/* Literal */
isnowait = 0;
buf->s[0] = '\0';
while ((c = prot_getc(imapd_in)) != EOF && isdigit(c)) {
sawdigit = 1;
len = len*10 + c - '0';
}
if (c == '+') {
isnowait++;
c = prot_getc(imapd_in);
}
if (!sawdigit || c != '}') {
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
}
c = prot_getc(imapd_in);
if (c != '\r') {
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
}
c = prot_getc(imapd_in);
if (c != '\n') {
if (c != EOF) prot_ungetc(c, imapd_in);
return EOF;
}
if (len >= buf->alloc) {
buf->alloc = len+1;
buf->s = xrealloc(buf->s, buf->alloc+1);
}
if (!isnowait) {
prot_printf(imapd_out, "+ go ahead\r\n");
prot_flush(imapd_out);
}
for (i = 0; i < len; i++) {
c = prot_getc(imapd_in);
if (c == EOF) {
buf->s[len] = '\0';
return EOF;
}
buf->s[i] = c;
}
buf->s[len] = '\0';
if (strlen(buf->s) != len) return EOF; /* Disallow imbedded NUL */
return prot_getc(imapd_in);
}
}
#define XX 127
/*
* Table for decoding base64
*/
static const char index_64[256] = {
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
};
#define CHAR64(c) (index_64[(unsigned char)(c)])
/*
* Parse a base64_string
*/
int getbase64string(buf)
struct buf *buf;
{
int c1, c2, c3, c4;
int len = 0;
if (buf->alloc == 0) {
buf->alloc = BUFGROWSIZE;
buf->s = xmalloc(buf->alloc+1);
}
for (;;) {
c1 = prot_getc(imapd_in);
if (c1 == '\r') {
c1 = prot_getc(imapd_in);
if (c1 != '\n') {
eatline(c1);
return -1;
}
return len;
}
else if (c1 == '\n') return len;
if (CHAR64(c1) == XX) {
eatline(c1);
return -1;
}
c2 = prot_getc(imapd_in);
if (CHAR64(c2) == XX) {
eatline(c2);
return -1;
}
c3 = prot_getc(imapd_in);
if (c3 != '=' && CHAR64(c3) == XX) {
eatline(c3);
return -1;
}
c4 = prot_getc(imapd_in);
if (c4 != '=' && CHAR64(c4) == XX) {
eatline(c4);
return -1;
}
if (len+3 >= buf->alloc) {
buf->alloc = len+BUFGROWSIZE;
buf->s = xrealloc(buf->s, buf->alloc+1);
}
buf->s[len++] = ((CHAR64(c1)<<2) | ((CHAR64(c2)&0x30)>>4));
if (c3 == '=') {
c1 = prot_getc(imapd_in);
if (c1 == '\r') c1 = prot_getc(imapd_in);
if (c1 != '\n') {
eatline(c1);
return -1;
}
if (c4 != '=') return -1;
return len;
}
buf->s[len++] = (((CHAR64(c2)&0xf)<<4) | ((CHAR64(c3)&0x3c)>>2));
if (c4 == '=') {
c1 = prot_getc(imapd_in);
if (c1 == '\r') c1 = prot_getc(imapd_in);
if (c1 != '\n') {
eatline(c1);
return -1;
}
return len;
}
buf->s[len++] = (((CHAR64(c3)&0x3)<<6) | CHAR64(c4));
}
}
/*
* Parse a search program
*/
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;
}
/*
* Parse a search criteria
*/
int getsearchcriteria(tag, searchargs, charset, parsecharset)
char *tag;
struct searchargs *searchargs;
int *charset;
int parsecharset;
{
static struct buf criteria, arg;
static struct searchargs zerosearchargs;
struct searchargs *sub1, *sub2;
char *p, *str;
int i, c, flag, size;
time_t start, end;
c = getword(&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(&arg);
if (c == EOF) goto missingarg;
- str = charset_convert(arg.s, *charset);
+ charset_convert(arg.s, *charset, str, 0);
if (strchr(str, EMPTY)) {
/* Force failure */
searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
}
else {
appendstrlistpat(&searchargs->bcc, str);
}
+ free(str);
}
else if (!strcmp(criteria.s, "body")) {
if (c != ' ') goto missingarg;
c = getastring(&arg);
if (c == EOF) goto missingarg;
- str = charset_convert(arg.s, *charset);
+ charset_convert(arg.s, *charset, str, 0);
if (strchr(str, EMPTY)) {
/* Force failure */
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(&arg);
if (c == EOF) goto missingarg;
- str = charset_convert(arg.s, *charset);
+ charset_convert(arg.s, *charset, str, 0);
if (strchr(str, EMPTY)) {
/* Force failure */
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(&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(&arg);
if (c == EOF) goto missingarg;
- str = charset_convert(arg.s, *charset);
+ charset_convert(arg.s, *charset, str, 0);
if (strchr(str, EMPTY)) {
/* Force failure */
searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
}
else {
appendstrlistpat(&searchargs->from, str);
}
}
else goto badcri;
break;
case 'h':
if (!strcmp(criteria.s, "header")) {
if (c != ' ') goto missingarg;
c = getastring(&arg);
if (c != ' ') goto missingarg;
lcase(arg.s);
if (!(searchargs->flags & SEARCH_UNCACHEDHEADER)) {
for (i=0; i<mailbox_num_cache_header; i++) {
if (!strcmp(mailbox_cache_header_name[i], arg.s)) break;
}
if (i == mailbox_num_cache_header) {
searchargs->flags |= SEARCH_UNCACHEDHEADER;
}
}
appendstrlist(&searchargs->header_name, arg.s);
c = getastring(&arg);
if (c == EOF) goto missingarg;
- str = charset_convert(arg.s, *charset);
+ charset_convert(arg.s, *charset, str, 0);
if (strchr(str, EMPTY)) {
/* Force failure */
searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
}
else {
appendstrlistpat(&searchargs->header, str);
}
}
else goto badcri;
break;
case 'k':
if (!strcmp(criteria.s, "keyword")) {
if (c != ' ') goto missingarg;
c = getword(&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) {
/* Force failure */
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(&arg);
size = 0;
for (p = arg.s; *p && isdigit(*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 *)xmalloc(sizeof(struct searchargs));
*sub1 = zerosearchargs;
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 *)xmalloc(sizeof(struct searchargs));
*sub1 = zerosearchargs;
c = getsearchcriteria(tag, sub1, charset, 0);
if (c == EOF) {
freesearchargs(sub1);
return EOF;
}
if (c != ' ') goto missingarg;
sub2 = (struct searchargs *)xmalloc(sizeof(struct searchargs));
*sub2 = zerosearchargs;
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(&arg);
size = 0;
for (p = arg.s; *p && isdigit(*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(&arg);
if (c == EOF) goto missingarg;
- str = charset_convert(arg.s, *charset);
+ charset_convert(arg.s, *charset, str, 0);
if (strchr(str, EMPTY)) {
/* Force failure */
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(&arg);
if (c == EOF) goto missingarg;
- str = charset_convert(arg.s, *charset);
+ charset_convert(arg.s, *charset, str, 0);
if (strchr(str, EMPTY)) {
/* Force failure */
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(&arg);
if (c == EOF) goto missingarg;
- str = charset_convert(arg.s, *charset);
+ charset_convert(arg.s, *charset, str, 0);
if (strchr(str, EMPTY)) {
/* Force failure */
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(&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(&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;
}
/*
* Parse a "date", for SEARCH criteria
* The time_t's pointed to by 'start' and 'end' are set to the
* times of the start and end of the parsed date.
*/
int getsearchdate(start, end)
time_t *start, *end;
{
int c;
struct tm tm;
static struct tm zerotm;
int quoted = 0;
char month[4];
tm = zerotm;
c = prot_getc(imapd_in);
if (c == '\"') {
quoted++;
c = prot_getc(imapd_in);
}
/* Day of month */
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);
/* Month name */
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);
/* Year */
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;
}
/*
* Parse a date_time, for the APPEND command
*/
int getdatetime(date)
time_t *date;
{
int c;
- struct tm tm, *ltm;
+ struct tm tm;
int old_format = 0;
static struct tm zerotm;
char month[4], zone[4], *p;
int zone_off;
tm = zerotm;
c = prot_getc(imapd_in);
if (c != '\"') goto baddate;
/* Day of month */
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 (c != '-') goto baddate;
c = prot_getc(imapd_in);
/* Month name */
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);
/* Year */
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++;
/* Hour */
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;
/* Minute */
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;
/* Second */
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;
/* Time zone */
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 == '\"') {
/* Military (single-char) zones */
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 {
/* UT (universal time) */
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 {
/* 3-char time zone */
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;
*date = mkgmtime(&tm) - zone_off*60;
return c;
baddate:
prot_ungetc(c, imapd_in);
return EOF;
}
/*
* Eat characters up to and including the next newline
* Also look for and eat non-synchronizing literals.
*/
void
eatline(c)
int c;
{
int state = 0;
char *statediagram = " {+}\r";
- int size;
+ int size = -1;
for (;;) {
if (c == '\n') return;
if (c == statediagram[state+1]) {
state++;
if (state == 1) size = 0;
else if (c == '\r') {
/* Got a non-synchronizing literal */
c = prot_getc(imapd_in);/* Eat newline */
while (size) {
c = prot_getc(imapd_in); /* Eat contents */
}
state = 0; /* Go back to scanning for eol */
}
}
else if (state == 1 && isdigit(c)) {
size = size * 10 + c - '0';
}
else state = 0;
c = prot_getc(imapd_in);
if (c == EOF) return;
}
}
/*
* Print 's' as a quoted-string or literal (but not an atom)
*/
void
printstring(s)
const char *s;
{
const char *p;
int len = 0;
/* Look for any non-QCHAR characters */
for (p = s; *p; 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);
}
}
/*
* Print 's' as an atom, quoted-string, or literal
*/
void
printastring(s)
const char *s;
{
const char *p;
int len = 0;
if (imparse_isatom(s)) {
prot_printf(imapd_out, "%s", s);
return;
}
/* Look for any non-QCHAR characters */
for (p = s; *p; 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);
}
}
/*
* Append 'section', 'fields', 'trail' to the fieldlist 'l'.
*/
void
appendfieldlist(l, section, fields, trail)
struct fieldlist **l;
char *section;
struct strlist *fields;
char *trail;
{
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);
(*tail)->next = 0;
}
/*
* Append 's' to the strlist 'l'.
*/
void
appendstrlist(l, s)
struct strlist **l;
char *s;
{
struct strlist **tail = l;
while (*tail) tail = &(*tail)->next;
*tail = (struct strlist *)xmalloc(sizeof(struct strlist));
(*tail)->s = xstrdup(s);
(*tail)->p = 0;
(*tail)->next = 0;
}
/*
* Append 's' to the strlist 'l', compiling it as a pattern.
+ * Caller must pass in memory that is freed when the strlist is freed.
*/
void
appendstrlistpat(l, s)
struct strlist **l;
char *s;
{
struct strlist **tail = l;
while (*tail) tail = &(*tail)->next;
*tail = (struct strlist *)xmalloc(sizeof(struct strlist));
- (*tail)->s = xstrdup(s);
+ (*tail)->s = s;
(*tail)->p = charset_compilepat(s);
(*tail)->next = 0;
}
/*
* Free the fieldlist 'l'
*/
void
freefieldlist(l)
struct fieldlist *l;
{
struct fieldlist *n;
while (l) {
n = l->next;
free(l->section);
freestrlist(l->fields);
free(l->trail);
free((char *)l);
l = n;
}
}
/*
* Free the strlist 'l'
*/
void
freestrlist(l)
struct strlist *l;
{
struct strlist *n;
while (l) {
n = l->next;
free(l->s);
if (l->p) charset_freepat(l->p);
free((char *)l);
l = n;
}
}
/*
* Append the searchargs 's1' and 's2' to the sublist of 's'
*/
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;
}
/*
* Free the searchargs 's'
*/
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);
}
/*
* Print an authentication ready response
*/
static char basis_64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
void
printauthready(len, data)
int len;
unsigned char *data;
{
int c1, c2, c3;
prot_putc('+', imapd_out);
prot_putc(' ', imapd_out);
while (len) {
c1 = *data++;
len--;
prot_putc(basis_64[c1>>2], imapd_out);
if (len == 0) c2 = 0;
else c2 = *data++;
prot_putc(basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)], imapd_out);
if (len == 0) {
prot_putc('=', imapd_out);
prot_putc('=', imapd_out);
break;
}
if (--len == 0) c3 = 0;
else c3 = *data++;
prot_putc(basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)], imapd_out);
if (len == 0) {
prot_putc('=', imapd_out);
break;
}
--len;
prot_putc(basis_64[c3 & 0x3F], imapd_out);
}
prot_putc('\r', imapd_out);
prot_putc('\n', imapd_out);
prot_flush(imapd_out);
}
/*
* Issue a MAILBOX untagged response
*/
static int mailboxdata(name, matchlen, maycreate, rock)
char *name;
int matchlen;
int maycreate;
void* rock;
{
prot_printf(imapd_out, "* MAILBOX %s\r\n", name);
return 0;
}
/*
* Issue a LIST or LSUB untagged response
*/
static void mstringdata(cmd, name, matchlen, maycreate)
char *cmd;
char *name;
int matchlen;
int maycreate;
{
static char lastname[MAX_MAILBOX_PATH];
static int lastnamedelayed;
static sawuser = 0;
int lastnamehassub = 0;
int c;
/* We have to reset the sawuser flag before each list command.
* Handle it as a dirty hack.
*/
if (cmd == NULL) {
sawuser = 0;
return;
}
if (lastnamedelayed) {
if (name && strncasecmp(lastname, name, strlen(lastname)) == 0 &&
name[strlen(lastname)] == '.') {
lastnamehassub = 1;
}
prot_printf(imapd_out, "* %s (%s) \".\" ", cmd,
lastnamehassub ? "" : "\\Noinferiors");
printastring(lastname);
prot_printf(imapd_out, "\r\n");
lastnamedelayed = 0;
}
/* Special-case to flush any final state */
if (!name) {
lastname[0] = '\0';
return;
}
/* Suppress any output of a partial match */
if (name[matchlen] && strncasecmp(lastname, name, matchlen) == 0) {
return;
}
/*
* We can get a partial match for "user" multiple times with
* other matches inbetween. Handle it as a special case
*/
if (matchlen == 4 && strncasecmp(name, "user", 4) == 0) {
if (sawuser) return;
sawuser = 1;
}
strcpy(lastname, name);
lastname[matchlen] = '\0';
if (!name[matchlen] && !maycreate) {
lastnamedelayed = 1;
return;
}
c = name[matchlen];
if (c) name[matchlen] = '\0';
prot_printf(imapd_out, "* %s (%s) \".\" ", cmd, c ? "\\Noselect" : "");
printstring(name);
prot_printf(imapd_out, "\r\n");
if (c) name[matchlen] = c;
return;
}
/*
* Issue a LIST untagged response
*/
static int listdata(name, matchlen, maycreate, rock)
char *name;
int matchlen;
int maycreate;
void* rock;
{
mstringdata("LIST", name, matchlen, maycreate);
return 0;
}
/*
* Issue a LSUB untagged response
*/
static int lsubdata(name, matchlen, maycreate, rock)
char *name;
int matchlen;
int maycreate;
void* rock;
{
mstringdata("LSUB", name, matchlen, maycreate);
return 0;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Apr 4, 8:47 AM (2 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18823313
Default Alt Text
(110 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline