diff options
Diffstat (limited to 'imap/smap.c')
| -rw-r--r-- | imap/smap.c | 4557 | 
1 files changed, 4557 insertions, 0 deletions
| diff --git a/imap/smap.c b/imap/smap.c new file mode 100644 index 0000000..20a7c2c --- /dev/null +++ b/imap/smap.c @@ -0,0 +1,4557 @@ +/* +** Copyright 2003-2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if	HAVE_CONFIG_H +#include	"config.h" +#endif +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<errno.h> +#include	<ctype.h> +#include	<signal.h> +#include	<fcntl.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif +#if	HAVE_UTIME_H +#include	<utime.h> +#endif +#if TIME_WITH_SYS_TIME +#include	<sys/time.h> +#include	<time.h> +#else +#if HAVE_SYS_TIME_H +#include	<sys/time.h> +#else +#include	<time.h> +#endif +#endif +#if HAVE_LOCALE_H +#include	<locale.h> +#endif + +#include	<sys/types.h> +#include	<sys/stat.h> + +#include	"mysignal.h" +#include	"imapd.h" +#include	"fetchinfo.h" +#include	"searchinfo.h" +#include	"storeinfo.h" +#include	"mailboxlist.h" +#include	"thread.h" +#include	"outbox.h" + +#include	"imapwrite.h" +#include	"imaptoken.h" +#include	"imapscanclient.h" +#include	"searchinfo.h" +#include	"maildir/config.h" +#include	"maildir/maildircreate.h" +#include	"maildir/maildirrequota.h" +#include	"maildir/maildirgetquota.h" +#include	"maildir/maildirquota.h" +#include	"maildir/maildirmisc.h" +#include	"maildir/maildirwatch.h" +#include	"maildir/maildiraclt.h" +#include	"maildir/maildirnewshared.h" +#include	"maildir/maildirinfo.h" +#include	"unicode/unicode.h" + +#include	"rfc2045/rfc2045.h" +#include	"rfc822/rfc822.h" + +#define SMAP_BUFSIZ 8192 + +#define SHARED "shared" + +#define LIST_FOLDER 1 +#define LIST_DIRECTORY 2 + +#define FETCH_UID 1 +#define FETCH_SIZE 2 +#define FETCH_FLAGS 4 +#define FETCH_KEYWORDS 8 +#define FETCH_INTERNALDATE 16 + +extern dev_t homedir_dev; +extern ino_t homedir_ino; + +int mdcreate(const char *mailbox); +int mddelete(const char *s); + +extern int folder_rename(struct maildir_info *mi1, +			 struct maildir_info *mi2, +			 const char **errmsg); +extern int current_temp_fd; +extern const char *current_temp_fn; + +extern int snapshot_init(const char *, const char *); +extern int keywords(); + +extern unsigned long header_count, body_count; + +extern char *compute_myrights(maildir_aclt_list *l, +			      const char *l_owner); + +extern int addRemoveKeywords(int (*callback_func)(void *, void *), +			     void *callback_func_arg, +			     struct storeinfo *storeinfo_s); +extern int doAddRemoveKeywords(unsigned long n, int uid, void *vp); + + +extern void snapshot_select(int); +extern void doflags(FILE *fp, struct fetchinfo *fi, +		    struct imapscaninfo *i, unsigned long msgnum, +		    struct rfc2045 *mimep); +extern void set_time(const char *tmpname, time_t timestamp); +extern int imapenhancedidle(void); +extern void imapidle(void); + +extern void expunge(); +extern void doNoop(int); +extern int do_store(unsigned long, int, void *); +extern int reflag_filename(struct imapscanmessageinfo *mi, +			   struct imapflags *flags, int fd); + +extern void do_expunge(unsigned long expunge_start, +		       unsigned long expunge_end, +		       int force); + +extern char *current_mailbox, *current_mailbox_acl; +static int current_mailbox_shared; + +extern struct imapscaninfo current_maildir_info; +extern void get_message_flags(struct imapscanmessageinfo *, +			      char *, struct imapflags *); +extern void fetchflags(unsigned long); +extern int acl_lock(const char *homedir, +		    int (*func)(void *), +		    void *void_arg); +extern void aclminimum(const char *); + +struct rfc2045 *fetch_alloc_rfc2045(unsigned long, FILE *); +FILE *open_cached_fp(unsigned long); +void fetch_free_cache(); + +extern FILE *maildir_mkfilename(const char *mailbox, struct imapflags *flags, +				unsigned long s, char **tmpname, +				char **newname); + +/* +** Parse a word from the current SMAP command. +*/ + +static char *getword(char **ptr) +{ +	char *p= *ptr, *q, *r; + +	while (*p && isspace((int)(unsigned char)*p)) +		p++; + +	if (*p != '"') +	{ +		for (q=p; *q; q++) +		{ +			if (isspace((int)(unsigned char)*q)) +			{ +				*q++=0; +				break; +			} +		} + +		*ptr=q; +		return p; +	} + +	++p; +	r=q=p; + +	while (*r) +	{ +		if (*r == '"') +		{ +			if (r[1] == '"') +			{ +				r += 2; +				*q++='"'; +				continue; +			} +			++r; +			break; +		} + +		*q++ = *r++; +	} + +	*q=0; +	*ptr=r; +	return p; +} + +#define UC(c) if ( (c) >= 'a' && (c) <= 'z') (c) += 'A' - 'a' + +static void up(char *p) +{ +	while (*p) +	{ +		UC(*p); +		p++; +	} +} + +/* +** Write a WORD reply. +*/ +static void smapword_s(const char *w); + +void smapword(const char *w) +{ +	writes("\""); +	smapword_s(w); +	writes("\""); +} + +static void smapword_s(const char *w) +{ +	while (w && *w) +	{ +		size_t i; + +		for (i=0; w[i]; i++) +			if (w[i] == '"') +				break; +		if (i) +			writemem(w, i); + +		w += i; + +		if (*w) +		{ +			writes("\"\""); +			++w; +		} +	} +} + +struct fn_word_list { +	struct fn_word_list *next; +	char *w; +}; + +/* +** Create a folder word array by reading words from the SMAP command. +*/ + +static char **fn_fromwords(char **ptr) +{ +	struct fn_word_list *h=NULL, *n, **t=&h; +	size_t cnt=0; +	char *p; +	char **fn; + +	while (*(p=getword(ptr))) +	{ +		n=malloc(sizeof(struct fn_word_list)); + +		if (!n || !(n->w=strdup(p))) +		{ +			if (n) +				free(n); + +			while ((n=h) != NULL) +			{ +				h=n->next; +				free(n->w); +				free(n); +			} +			return NULL; +		} + +		n->next=NULL; +		*t=n; +		t= &n->next; +		cnt++; +	} + +	if (!h) +	{ +		errno=EINVAL; +		return NULL; +	} + +	fn=malloc((cnt+1)*sizeof(char *)); +	cnt=0; + +	while ((n=h) != NULL) +	{ +		h=n->next; + +		if (fn) +			fn[cnt]=n->w; +		else +			free(n->w); +		free(n); +		cnt++; +	} +	if (fn) +		fn[cnt]=0; +	return fn; +} + +/* +** LIST-related functions. +*/ + +struct list_hier { +	struct list_hier *next; +	char *hier; +	int flags; +}; + +struct list_callback_info { + +	struct list_hier *hier; /* Hierarchy being listed */ + +	struct list_hier *found; +}; + +static void list(char *folder, const char *descr, int type) +{ +	writes("* LIST "); + +	smapword(folder); + +	writes(" "); + +	smapword(descr); + +	if (type & LIST_FOLDER) +		writes(" FOLDER"); +	if (type & LIST_DIRECTORY) +		writes(" DIRECTORY"); +	writes("\n"); +} + +/* +** Callback from maildir_list.  f="INBOX.folder.name" +** +*/ + +struct list_callback_utf8 { + +	void (*callback_func)(const char *, char **, void *); +	void *callback_arg; +	const char *homedir; +	const char *owner; +}; + +static void list_callback(const char *f, void *vp) +{ +	struct list_callback_utf8 *utf8=(struct list_callback_utf8 *)vp; +	maildir_aclt_list l; + +	char **fn=maildir_smapfn_fromutf7(f); + +	if (!fn) +	{ +		perror(f); +		return; +	} + +	if (maildir_acl_read(&l, utf8->homedir, strchr(f, '.')) == 0) +	{ +		char *myrights; +		char *owner=malloc(sizeof("user=")+strlen(utf8->owner)); + +		if (!owner) +			write_error_exit(0); + +		strcat(strcpy(owner, "user="), utf8->owner); +		myrights=compute_myrights(&l, owner); +		free(owner); + +		if (myrights && strchr(myrights, ACL_LOOKUP[0]) != NULL) +			(*utf8->callback_func)(f, fn, utf8->callback_arg); +		if (myrights) +			free(myrights); + +		maildir_aclt_list_destroy(&l); +	} +	maildir_smapfn_free(fn); +} + +/* +** list_callback callback that accumulates existing folders beneath a +** certain hierarchy. +*/ + +static void list_utf8_callback(const char *n, char **f, void *vp) +{ +	struct list_callback_info *lci=(struct list_callback_info *)vp; +	struct list_hier *h=lci->hier; + +	for (;;) +	{ +		if (!*f) +			return; + +		if (h) +		{ +			if (strcmp(h->hier, *f)) +				break; + +			h=h->next; +			f++; +			continue; +		} + +		for (h=lci->found; h; h=h->next) +		{ +			if (strcmp(h->hier, *f) == 0) +				break; +		} + +		if (!h) +		{ +			if ((h=malloc(sizeof(struct list_hier))) == NULL || +			    (h->hier=strdup(*f)) == NULL) +			{ +				if (h) +					free(h); +				perror("malloc"); +				break; +			} + +			h->next=lci->found; +			lci->found=h; +			h->flags=0; +		} + +		if (f[1]) +			h->flags |= LIST_DIRECTORY; +		else +			h->flags |= LIST_FOLDER; +		break; +	} +} + +/* +** SMAP1 list command goes here.  Dirty hack: build the hierarchy list on +** the stack. +*/ + +static void do_listcmd(struct list_hier **head, +		       struct list_hier **tail, +		       char **ptr); + +static void listcmd(struct list_hier **head, +		    struct list_hier **tail, +		    char **ptr) +{ +	char *p; + +	if (*(p=getword(ptr))) +	{ +		struct list_hier node; +		node.next=NULL; +		node.hier=p; + +		*tail= &node; +		listcmd(head, &node.next, ptr); +		return; +	} +	do_listcmd(head, tail, ptr); +} + +struct smap_find_info { +	char *homedir; +	char *maildir; +}; + +static int smap_find_cb(struct maildir_newshared_enum_cb *cb); +static int smap_list_cb(struct maildir_newshared_enum_cb *cb); +static int read_acls(maildir_aclt_list *aclt_list, +		     struct maildir_info *minfo); + +static void do_listcmd(struct list_hier **head, +		       struct list_hier **tail, +		       char **ptr) +{ +	struct list_hier *p; +	size_t cnt; +	char **vecs; +	int hierlist=0; + +	if (!*head) /* No arguments to LIST */ +	{ +		list(INBOX, "New Mail", LIST_FOLDER); +		list(INBOX, "Folders", LIST_DIRECTORY); +		list(PUBLIC, "Public Folders", LIST_DIRECTORY); +	} +	else +	{ +		struct list_callback_info lci; +		struct list_callback_utf8 list_utf8_info; + +		list_utf8_info.callback_func= &list_utf8_callback; +		list_utf8_info.callback_arg= &lci; + +		lci.hier= *head; +		lci.found=NULL; + +		if (strcmp(lci.hier->hier, PUBLIC) == 0) +		{ +			struct maildir_shindex_cache *curcache; +			struct list_hier *p=lci.hier->next; +			struct smap_find_info sfi; +			int eof; +			char *d; + +			curcache=maildir_shared_cache_read(NULL, NULL, NULL); + +			while (curcache && p) +			{ +				size_t i; +				int rc; +				struct list_hier inbox; + +				for (i=0; i<curcache->nrecords; i++) +					if (strcmp(curcache->records[i].name, +						   p->hier) == 0) +						break; +				if (i >= curcache->nrecords) +				{ +					curcache=NULL; +					break; +				} + +				sfi.homedir=NULL; +				sfi.maildir=NULL; +				curcache->indexfile.startingpos= +					curcache->records[i].offset; +				rc=maildir_newshared_nextAt(&curcache +							    ->indexfile, +							    &eof, +							    smap_find_cb, +							    &sfi); + +				if (rc || eof) +				{ +					fprintf(stderr, "ERR: Internal error -" +						" maildir_newshared_nextAt: %s\n", +						strerror(errno)); +					curcache=NULL; +					break; +				} + +				if (!sfi.homedir) +				{ +					curcache= +						maildir_shared_cache_read(curcache, +									  sfi.maildir, +									  p->hier); +					p=p->next; +					free(sfi.maildir); +					continue; +				} + +				inbox.next=p->next; +				inbox.hier=INBOX; + +				d=maildir_location(sfi.homedir, sfi.maildir); +				free(sfi.homedir); +				free(sfi.maildir); + +				lci.hier= &inbox; +				list_utf8_info.homedir=d; +				list_utf8_info.owner=p->hier; + +				maildir_list(d, &list_callback, +					     &list_utf8_info); +				free(d); +				curcache=NULL; +				break; +			} + +			if (curcache) /* List a shared hierarchy */ +			{ +				int rc; + +				curcache->indexfile.startingpos=0; +				eof=0; + +				do +				{ +					rc=(curcache->indexfile.startingpos +						? maildir_newshared_next: +						maildir_newshared_nextAt) +						(&curcache->indexfile, &eof, +						 &smap_list_cb, +						 &list_utf8_info); + +					if (rc) +						fprintf(stderr, +							"ERR: Internal error -" +							" maildir_newshared_next: %s\n", +							strerror(errno)); +				} while (rc == 0 && !eof); + +				hierlist=1; +			} +		} +		else +		{ +			list_utf8_info.homedir="."; +			list_utf8_info.owner=getenv("AUTHENTICATED"); +			maildir_list(".", &list_callback, +				     &list_utf8_info); +		} + +		for (cnt=0, p= *head; p; p=p->next) +			++cnt; + +		vecs=malloc(sizeof(char *)*(cnt+2)); + +		if (!vecs) +		{ +			while (lci.found) +			{ +				struct list_hier *h=lci.found; + +				lci.found=h->next; + +				free(h->hier); +				free(h); +			} +			write_error_exit(0); +		} + + +		for (cnt=0, p= *head; p; p=p->next) +		{ +			vecs[cnt]=p->hier; +			++cnt; +		} + +		while (lci.found) +		{ +			struct list_hier *h=lci.found; +			struct maildir_info minfo; +			maildir_aclt_list aclt_list; + +			lci.found=h->next; + +			vecs[cnt]=h->hier; +			vecs[cnt+1]=0; + +			if (maildir_info_smap_find(&minfo, vecs, +						   getenv("AUTHENTICATED")) == 0) +			{ +				if (read_acls(&aclt_list, &minfo) == 0) +				{ +					char *acl; + +					acl=compute_myrights(&aclt_list, +							     minfo.owner); + +					if (acl) +					{ +						if (strchr(acl, ACL_LOOKUP[0]) +						    == NULL) +						{ +							h->flags=LIST_DIRECTORY; + +							if (hierlist) +								list(h->hier, +								     h->hier, +								     h->flags); + +						} +						else +							list(h->hier, h->hier, +							     h->flags); +						free(acl); +					} +					else +					{ +						fprintf(stderr, +							"ERR: Cannot compute" +							" my access rights" +							" for %s: %s\n", +							h->hier, +							strerror(errno)); +					} + +					maildir_aclt_list_destroy(&aclt_list); +				} +				else +				{ +					fprintf(stderr, +						"ERR: Cannot read ACLs" +						" for %s(%s): %s\n", +						minfo.homedir ? minfo.homedir +						: ".", +						minfo.maildir ? minfo.maildir +						: "unknown", +						strerror(errno)); +				} +			} +			else +			{ +				fprintf(stderr, +					"ERR: Internal error in list():" +					" cannot find folder %s: %s\n", +					h->hier, +					strerror(errno)); +			} + +			free(h->hier); +			free(h); +		} +		free(vecs); +	} +	writes("+OK LIST completed\n"); +} + +static int smap_find_cb(struct maildir_newshared_enum_cb *cb) +{ +	struct smap_find_info *ifs=(struct smap_find_info *)cb->cb_arg; + +	if (cb->homedir) +		ifs->homedir=my_strdup(cb->homedir); +	if (cb->maildir) +		ifs->maildir=my_strdup(cb->maildir); +	return 0; +} + +static int smap_list_cb(struct maildir_newshared_enum_cb *cb) +{ +	struct list_callback_utf8 *list_utf8_info= +		(struct list_callback_utf8 *)cb->cb_arg; +	struct list_callback_info *lci= +		(struct list_callback_info *)list_utf8_info->callback_arg; +	char *d; + +	struct list_hier *h; +	struct stat stat_buf; + +	if (cb->homedir == NULL) +	{ +		if ((h=malloc(sizeof(struct list_hier))) == NULL || +		    (h->hier +		     =strdup(cb->name)) == NULL) +		{ +			if (h) +				free(h); +			perror("ERR: malloc"); +			return 0; +		} + +		h->next= lci->found; +		lci->found=h; +		h->flags = LIST_DIRECTORY; +		return 0; +	} + +	d=maildir_location(cb->homedir, cb->maildir); + +	if (!d) +	{ +		perror("ERR: get_topmaildir"); +		return 0; +	} + +	if (stat(d, &stat_buf) < 0 || +	    (stat_buf.st_dev == homedir_dev && +	     stat_buf.st_ino == homedir_ino)) +	{ +		free(d); +		return 0; +	} + +	list_utf8_info->homedir=d; +	list_utf8_info->owner=cb->name; +	lci->hier=NULL; +	h=lci->found; +	lci->found=NULL; +	maildir_list(d, &list_callback, list_utf8_info); +	free(d); + +	if (!lci->found) +		lci->found=h; +	else +	{ +		char *p; + +		while (lci->found->next) /* SHOULDN'T HAPPEN!!! */ +		{ +			struct list_hier *p=lci->found->next; + +			lci->found->next=p->next; +			free(p->hier); +			free(p); +			fprintf(stderr, "ERR: Unexpected folder list" +				" in smap_list_cb()\n"); +		} +		lci->found->next=h; + +		p=my_strdup(cb->name); +		free(lci->found->hier); +		lci->found->hier=p; +	} + +	return (0); +} + +/* +** Read the name of a new folder.  Returns the pathname to the folder, suitable +** for immediate creation. +*/ + +static char *getCreateFolder_int(char **ptr, char *need_perms) +{ +	char **fn; +	char *n; +	struct maildir_info minfo; +	size_t i; +	char *save; +	maildir_aclt_list aclt_list; + +	fn=fn_fromwords(ptr); +	if (!fn) +		return NULL; + + +	if (need_perms) +	{ +		for (i=0; fn[i]; i++) +			; + +		if (i == 0) +		{ +			*need_perms=0; +			maildir_smapfn_free(fn); +			errno=EINVAL; +			return NULL; +		} + +		save=fn[--i]; +		fn[i]=NULL; +		if (maildir_info_smap_find(&minfo, fn, +					   getenv("AUTHENTICATED")) < 0) +		{ +			fn[i]=save; +			maildir_smapfn_free(fn); +			return NULL; +		} + +		fn[i]=save; + +		if (read_acls(&aclt_list, &minfo)) +		{ +			maildir_smapfn_free(fn); +			maildir_info_destroy(&minfo); +			return NULL; +		} + +		save=compute_myrights(&aclt_list, minfo.owner); +		maildir_aclt_list_destroy(&aclt_list); + +		for (i=0; need_perms[i]; i++) +			if (save == NULL || strchr(save, need_perms[i])==NULL) +			{ +				if (save) +					free(save); +				maildir_smapfn_free(fn); +				maildir_info_destroy(&minfo); +				*need_perms=0; +				errno=EPERM; +				return NULL; +			} + +		if (save) +			free(save); + +		maildir_info_destroy(&minfo); +	} + + +	if (maildir_info_smap_find(&minfo, fn, getenv("AUTHENTICATED")) < 0) +	{ +		maildir_smapfn_free(fn); +		return NULL; +	} + +	maildir_smapfn_free(fn); + +	if (minfo.homedir == NULL || minfo.maildir == NULL) +	{ +		maildir_info_destroy(&minfo); +		errno=ENOENT; +		return NULL; +	} + +	n=maildir_name2dir(minfo.homedir, minfo.maildir); + +	if (need_perms && strchr(need_perms, ACL_CREATE[0])) +	{ +		/* Initialize the ACL structures */ + +		if (read_acls(&aclt_list, &minfo) == 0) +			maildir_aclt_list_destroy(&aclt_list); +	} + +	maildir_info_destroy(&minfo); + +	return n; +} + +static char *getCreateFolder(char **ptr, char *perms) +{ +	char *p=getCreateFolder_int(ptr, perms); + +	if (p && strncmp(p, "./", 2) == 0) +	{ +		char *q=p+2; + +		while ((q[-2]=*q) != 0) +			q++; +	} +	return p; +} + + +static int read_acls(maildir_aclt_list *aclt_list, +		     struct maildir_info *minfo) +{ +	char *q; +	int rc; + +	if (minfo->homedir == NULL || minfo->maildir == NULL) +	{ +		if (minfo->mailbox_type == MAILBOXTYPE_NEWSHARED) +		{ +			/* Intermediate node in public hier */ + +			maildir_aclt_list_init(aclt_list); + +			if (maildir_aclt_list_add(aclt_list, +						  "anyone", +						  ACL_LOOKUP, +						  NULL) < 0) +			{ +				maildir_aclt_list_destroy(aclt_list); +				return -1; +			} +			return 0; +		} + +		return -1; +	} + +	q=maildir_name2dir(".", minfo->maildir); +	if (!q) +	{ +		fprintf(stderr, "ERR: Internal error" +			" in read_acls(%s)\n", minfo->maildir); +		return -1; +	} + +	rc=maildir_acl_read(aclt_list, minfo->homedir, +			    q[0] == '.' && +			    q[1] == '/' ? q+2:q); +	free(q); + +	if (current_mailbox) +	{ +		q=maildir_name2dir(minfo->homedir, minfo->maildir); + +		if (q) +		{ +			if (strcmp(q, current_mailbox) == 0) +			{ +				char *r=compute_myrights(aclt_list, +							 minfo->owner); + +				if (r && strcmp(current_mailbox_acl, r)) +				{ +					free(current_mailbox_acl); +					current_mailbox_acl=r; +					r=NULL; +				} +				if (r) free(r); +			} +			free(q); +		} +	} +	return rc; +} + +static char *getExistingFolder_int(char **ptr, +				   char *rightsWanted) +{ +	char **fn; +	char *n; +	struct maildir_info minfo; + +	fn=fn_fromwords(ptr); +	if (!fn) +		return NULL; + +	if (maildir_info_smap_find(&minfo, fn, getenv("AUTHENTICATED")) < 0) +	{ +		maildir_smapfn_free(fn); +		return NULL; +	} +	maildir_smapfn_free(fn); + +	if (minfo.homedir == NULL || minfo.maildir == NULL) +	{ +		maildir_info_destroy(&minfo); +		errno=ENOENT; +		return NULL; +	} + +	n=maildir_name2dir(minfo.homedir, minfo.maildir); + +	if (n && rightsWanted) +	{ +		maildir_aclt_list aclt_list; +		char *q, *r, *s; + +		if (read_acls(&aclt_list, &minfo) < 0) +		{ +			free(n); +			maildir_info_destroy(&minfo); +			return NULL; + +		} + +		q=compute_myrights(&aclt_list, minfo.owner); + +		maildir_aclt_list_destroy(&aclt_list); + +		if (q == NULL) +		{ +			free(n); +			maildir_info_destroy(&minfo); +			return NULL; +		} + +		for (r=s=rightsWanted; *r; r++) +			if (strchr(q, *r)) +				*s++ = *r; +		*s=0; +		free(q); +	} + +	maildir_info_destroy(&minfo); +	return n; +} + +static char *getAccessToFolder(char **ptr, char *rightsWanted) +{ +	char *p=getExistingFolder_int(ptr, rightsWanted); + +	if (p && strncmp(p, "./", 2) == 0) +	{ +		char *q=p+2; + +		while ((q[-2]=*q) != 0) +			q++; +	} + +	return p; +} + +static void smap1_noop(int real_noop) +{ +	if (current_mailbox) +		doNoop(real_noop); +	writes("+OK Folder updated\n"); +} + +/* Parse a message set.  Return the next word following the message set */ + +struct smapmsgset { +	struct smapmsgset *next; +	unsigned nranges; +	unsigned long range[2][2]; +}; + +static struct smapmsgset msgset; +static const char digit[]="0123456789"; + +static char *markmsgset(char **ptr, int *hasmsgset) +{ +	unsigned long n; +	char *w; + +	struct smapmsgset *msgsetp; + +	while ((msgsetp=msgset.next) != NULL) +	{ +		msgset.next=msgsetp->next; +		free(msgsetp); +	} + +	msgsetp= &msgset; + +	msgsetp->nranges=0; + +	*hasmsgset=0; + +	n=0; + +	while (*(w=getword(ptr))) +	{ +		unsigned long a=0, b=0; +		const char *d; + +		if (!*w || (d=strchr(digit, *w)) == NULL) +			break; + +		*hasmsgset=1; + +		while ( *w && (d=strchr(digit, *w)) != NULL) +		{ +			a=a * 10 + d-digit; +			w++; +		} + +		b=a; + +		if (*w == '-') +		{ +			++w; +			b=0; +			while ( *w && (d=strchr(digit, *w)) != NULL) +			{ +				b=b * 10 + d-digit; +				w++; +			} +		} + +		if (a <= n || b < a) +		{ +			errno=EINVAL; +			return NULL; +		} + +		n=b; + +		if (msgsetp->nranges >= +		    sizeof(msgsetp->range)/sizeof(msgsetp->range[0])) +		{ +			if ((msgsetp->next=malloc(sizeof(struct smapmsgset))) +			    == NULL) +			{ +				write_error_exit(0); +			} + +			msgsetp=msgsetp->next; +			msgsetp->next=NULL; +			msgsetp->nranges=0; +		} + +		msgsetp->range[msgsetp->nranges][0]=a; +		msgsetp->range[msgsetp->nranges][1]=b; +		++msgsetp->nranges; +	} + +	return w; +} + +static void parseflags(char *q, struct imapflags *flags) +{ +	char *p; + +	if ((q=strchr(q, '=')) == NULL) +		return; +	++q; + +	while (*q) +	{ +		p=q; + +		while (*q) +		{ +			if (*q == ',') +			{ +				*q++=0; +				break; +			} +			q++; +		} + +		if (strcmp(p, "SEEN") == 0) +			flags->seen=1; +		else if (strcmp(p, "REPLIED") == 0) +			flags->answered=1; +		else if (strcmp(p, "DRAFT") == 0) +			flags->drafts=1; +		else if (strcmp(p, "DELETED") == 0) +			flags->deleted=1; +		else if (strcmp(p, "MARKED") == 0) +			flags->flagged=1; + +	} +} + +extern int get_keyword(struct libmail_kwMessage **kwPtr, const char *kw); +extern int valid_keyword(const char *kw); + +static void parsekeywords(char *q, struct libmail_kwMessage **msgp) +{ +	char *p; + +	if ((q=strchr(q, '=')) == NULL) +		return; +	++q; + +	while (*q) +	{ +		p=q; + +		while (*q) +		{ +			if (*q == ',') +			{ +				*q++=0; +				break; +			} +			q++; +		} + +		get_keyword(msgp, p); +	} +} + +static int applymsgset( int (*callback_func)(unsigned long, void *), +			void *callback_arg) +{ +	struct smapmsgset *msgsetp= &msgset; +	unsigned long n; +	int rc; + +	while (msgsetp) +	{ +		unsigned i; + +		for (i=0; i<msgsetp->nranges; i++) +		{ +			for (n=msgsetp->range[i][0]; +			     n <= msgsetp->range[i][1]; n++) +			{ +				if (current_mailbox == NULL || +				    n > current_maildir_info.nmessages) +					break; +				rc=(*callback_func)(n-1, callback_arg); +				if (rc) +					return rc; +			} +		} + +		msgsetp=msgsetp->next; +	} +	return 0; +} + +static int do_attrfetch(unsigned long n, void *vp); + +static int applyflags(unsigned long n, void *vp) +{ +	struct storeinfo *si=(struct storeinfo *)vp; +	int attrs; +	struct libmail_kwMessage *newKw; + +	if (n >= current_maildir_info.nmessages) +		return 0; + +	attrs= si->keywords ? FETCH_KEYWORDS:FETCH_FLAGS; + +	if (!si->plusminus) +	{ +		if (si->keywords == NULL) /* STORE FLAGS= */ +			si->keywords=current_maildir_info.msgs[n].keywordMsg; +		else /* STORE KEYWORDS= */ +			get_message_flags(current_maildir_info.msgs+n, 0, +					  &si->flags); +	} + +	/* do_store may clobber si->keywords.  Punt */ + +	newKw=si->keywords; +	if (do_store(n+1, 0, si)) +	{ +		si->keywords=newKw; +		return -1; +	} +	si->keywords=newKw; + +	do_attrfetch(n, &attrs); +	return 0; +} + +struct smapAddRemoveKeywordInfo { +	struct storeinfo *si; +	void *storeVoidArg; +}; + +static int addRemoveSmapKeywordsCallback(void *myVoidArg, void *storeVoidArg); + +static int addRemoveSmapKeywords(struct storeinfo *si) +{ +	struct smapAddRemoveKeywordInfo ar; + +	ar.si=si; + +	return addRemoveKeywords(addRemoveSmapKeywordsCallback, &ar, si); +} + +static int doAddRemoveSmapKeywords(unsigned long n, void *voidArg); + +static int addRemoveSmapKeywordsCallback(void *myVoidArg, void *storeVoidArg) +{ +	struct smapAddRemoveKeywordInfo *info= +		(struct smapAddRemoveKeywordInfo *)myVoidArg; + +	info->storeVoidArg=storeVoidArg; +	return applymsgset(doAddRemoveSmapKeywords, info); +} + +static int doAddRemoveSmapKeywords(unsigned long n, void *voidArg) +{ +	struct smapAddRemoveKeywordInfo *info= +		(struct smapAddRemoveKeywordInfo *)voidArg; + +	return doAddRemoveKeywords(n+1, 0, info->storeVoidArg); +} + +static int setdate(unsigned long n, void *vp) +{ +	time_t datestamp=*(time_t *)vp; +	char	*filename=maildir_filename(current_mailbox, 0, +					   current_maildir_info.msgs[n] +					   .filename); + +	if (filename) +	{ +		set_time(filename, datestamp); +		free(filename); +	} +	return 0; +} + +static int msg_expunge(unsigned long n, void *vp) +{ +	do_expunge(n, n+1, 1); +	return 0; +} + +struct smapfetchinfo { +	int peek; +	char *entity; +	char *hdrs; +	char *mimeid; +}; + +static int hashdr(const char *hdrList, const char *hdr) +{ +	if (!hdrList || !*hdrList) +		return 1; + +	while (*hdrList) +	{ +		size_t n; +		int is_envelope=0; +		int is_mime=0; + +		if (*hdrList == ',') +		{ +			++hdrList; +			continue; +		} + +		if (strncmp(hdrList, ":ENVELOPE", 9) == 0) +		{ +			switch (hdrList[9]) { +			case 0: +			case ',': +				is_envelope=1; +				break; +			} +		} + +		if (strncmp(hdrList, ":MIME", 5) == 0) +		{ +			switch (hdrList[5]) { +			case 0: +			case ',': +				is_mime=1; +				break; +			} +		} + + +		if (is_envelope || is_mime) +		{ +			char hbuf[30]; + +			hbuf[0]=0; +			strncat(hbuf, hdr, 29); +			up(hbuf); + +			if (strcmp(hbuf, "DATE") == 0) +				return 1; +			if (strcmp(hbuf, "SUBJECT") == 0) +				return 1; +			if (strcmp(hbuf, "FROM") == 0) +				return 1; +			if (strcmp(hbuf, "SENDER") == 0) +				return 1; +			if (strcmp(hbuf, "REPLY-TO") == 0) +				return 1; +			if (strcmp(hbuf, "TO") == 0) +				return 1; +			if (strcmp(hbuf, "CC") == 0) +				return 1; +			if (strcmp(hbuf, "BCC") == 0) +				return 1; +			if (strcmp(hbuf, "IN-REPLY-TO") == 0) +				return 1; +			if (strcmp(hbuf, "MESSAGE-ID") == 0) +				return 1; +			if (strcmp(hbuf, "REFERENCES") == 0) +				return 1; + +			if (is_mime) +			{ +				if (strcmp(hbuf, "MIME-VERSION") == 0) +					return 1; + +				if (strncmp(hbuf, "CONTENT-", 8) == 0) +					return 1; +			} +		} + +		for (n=0; hdrList[n] && hdrList[n] != ',' && hdr[n]; n++) +		{ +			char a=hdrList[n]; +			char b=hdr[n]; + +			UC(b); +			if (a != b) +				break; +		} + +		if ((hdrList[n] == 0 || hdrList[n] == ',') && hdr[n] == 0) +			return 1; + +		hdrList += n; +		while (*hdrList && *hdrList != ',') +			++hdrList; +	} +	return 0; +} + +static void writemimeid(struct rfc2045 *rfcp) +{ +	if (rfcp->parent) +	{ +		writemimeid(rfcp->parent); +		writes("."); +	} +	writen(rfcp->pindex); +} + +static int dump_hdrs(int fd, unsigned long n, +		     struct rfc2045 *rfcp, const char *hdrs, +		     const char *type) +{ +	struct rfc2045src *src; +	struct rfc2045headerinfo *h; +	char *header; +	char *value; +	int rc; +        off_t start_pos, end_pos, dummy, start_body; +	off_t nbodylines; +	int get_flags=RFC2045H_NOLC; + +	rc=0; + +	if (type && strcmp(type, "RAWHEADERS") == 0) +		get_flags |= RFC2045H_KEEPNL; + +	if (!rfcp) +	{ +		struct stat stat_buf; + +		if (fstat(fd, &stat_buf)) +			end_pos=8000; /* Heh */ +		else +			end_pos=stat_buf.st_size; +		start_pos=0; +		start_body=0; +	} +	else rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body, &dummy, +			     &nbodylines); + +	writes("{."); +	writen(start_body - start_pos); +	writes("} FETCH "); +	writen(n+1); +	if (type) +	{ +		writes(" "); +		writes(type); +		writes("\n"); +	} +	else	/* MIME */ +	{ +		writes(" LINES="); +		writen(nbodylines); +		writes(" SIZE="); +		writen(end_pos-start_body); +		writes(" \"MIME.ID="); + +		if (rfcp->parent) +		{ +			writemimeid(rfcp); +			writes("\" \"MIME.PARENT="); +			if (rfcp->parent->parent) +				writemimeid(rfcp->parent); +		} +		writes("\"\n"); +	} + +	src=rfc2045src_init_fd(fd); +	h=src ? rfc2045header_start(src, rfcp):NULL; +  +	while (h && +	       (rc=rfc2045header_get(h, &header, &value, get_flags)) == 0 +	       && header) +	{ +		if (hashdr(hdrs, header)) +		{ +			if (*header == '.') +				writes("."); +			writes(header); +			writes(": "); +			writes(value); +			writes("\n"); + +			header_count += strlen(header)+strlen(value)+3; +		} +	} +	writes(".\n"); + +	if (h) +		rfc2045header_end(h); +	else +		rc= -1; +	if (src) +		rfc2045src_deinit(src); +	return rc; +} + +static int dump_body(FILE *fp, unsigned long msgNum, +		     struct rfc2045 *rfcp, int dump_all) +{ +	char buffer[SMAP_BUFSIZ]; +        off_t start_pos, end_pos, dummy, start_body; +	int i; +	int first; + +	if (!rfcp) +	{ +		struct stat stat_buf; + +		if (fstat(fileno(fp), &stat_buf) < 0) +			return -1; + +		if (dump_all) +		{ +			start_pos=start_body=0; +		} +		else +		{ +			if (fseek(fp, 0L, SEEK_SET) < 0) +				return -1; + +			if (!(rfcp=rfc2045_alloc())) +				return -1; + +			do +			{ +				i=fread(buffer, 1, sizeof(buffer), fp); + +				if (i < 0) +				{ +					rfc2045_free(rfcp); +					return -1; +				} + +				if (i == 0) +					break; +				rfc2045_parse(rfcp, buffer, i); +			} while (rfcp->workinheader); + +			rfc2045_mimepos(rfcp, &start_pos, &end_pos, +					&start_body, &dummy, +					&dummy); +			rfc2045_free(rfcp); + +			start_pos=0; +		} +		end_pos=stat_buf.st_size; +	} +	else rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body, &dummy, +			     &dummy); + +	if (dump_all) +		start_body=start_pos; + +	if (fseek(fp, start_body, SEEK_SET) < 0) +		return -1; + +	first=1; +	do +	{ +		int n=sizeof(buffer); + +		if (n > end_pos - start_body) +			n=end_pos - start_body; + +		for (i=0; i<n; i++) +		{ +			int ch=getc(fp); + +			if (ch == EOF) +			{ +				errno=EIO; +				return -1; +			} +			buffer[i]=ch; +		} + +		if (first) +		{ +			if (start_body == end_pos) +			{ +				writes("{.0} FETCH "); +				writen(msgNum+1); +				writes(" CONTENTS\n."); +			} +			else +			{ +				writes("{"); +				writen(i); +				writes("/"); +				writen(end_pos - start_body); +				writes("} FETCH "); +				writen(msgNum+1); +				writes(" CONTENTS\n"); +			} +		} +		else +		{ +			writen(i); +			writes("\n"); +		} + +		first=0; +		writemem(buffer, i); + +		start_body += i; +		body_count += i; +	} while (start_body < end_pos); +	writes("\n"); +	return 0; +} + +struct decodeinfo { +	char buffer[SMAP_BUFSIZ]; +	size_t bufptr; + +	int first; +	unsigned long msgNum; +	off_t estSize; +}; + +static void do_dump_decoded_flush(struct decodeinfo *); + +static struct rfc2045 *decodeCreateRfc(FILE *fp); +static int do_dump_decoded(const char *, size_t, void *); + +static int dump_decoded(FILE *fp, unsigned long msgNum, +			struct rfc2045 *rfcp) +{ +	struct decodeinfo myDecodeInfo; +	const char *content_type; +	const char *content_transfer_encoding; +	const char *charset; +        off_t start_pos, end_pos, dummy, start_body; + +	struct rfc2045src *src; +	struct rfc2045 *myrfcp=NULL; +	int fd; +	int i; + +	if (!rfcp) +	{ +		rfcp=myrfcp=decodeCreateRfc(fp); +		if (!rfcp) +			return -1; +	} + +	if ((fd=dup(fileno(fp))) < 0) +	{ +		if (myrfcp) +			rfc2045_free(myrfcp); +		return -1; +	} + +	myDecodeInfo.first=1; +	myDecodeInfo.msgNum=msgNum; +	myDecodeInfo.bufptr=0; + +	rfc2045_mimeinfo(rfcp, &content_type, &content_transfer_encoding, +			 &charset); +	rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body, &dummy, +			&dummy); +	myDecodeInfo.estSize=end_pos - start_body; + +	if (content_transfer_encoding +	    && strlen(content_transfer_encoding) == 6) +	{ +		char buf[7]; + +		strcpy(buf, content_transfer_encoding); +		up(buf); + +		if (strcmp(buf, "BASE64") == 0) +			myDecodeInfo.estSize = myDecodeInfo.estSize / 4 * 3; + +		/* Better estimate of base64 content */ +	} + +	src=rfc2045src_init_fd(fd); + +	i=src ? rfc2045_decodemimesection(src, rfcp, &do_dump_decoded, +					  &myDecodeInfo):-1; + +	do_dump_decoded_flush(&myDecodeInfo); + +	if (src) +		rfc2045src_deinit(src); + +	close(fd); + +	if (i == 0 && myDecodeInfo.first) /* Empty body, punt */ +	{ +		writes("{.0} FETCH "); +		writen(msgNum+1); +		writes(" CONTENTS\n."); +	} +	writes("\n"); +	if (myrfcp) +		rfc2045_free(myrfcp); +	return i; +} + +/* Dummy up a rfc2045 structure for retrieving the entire msg body */ + +static struct rfc2045 *decodeCreateRfc(FILE *fp) +{ +	char buffer[SMAP_BUFSIZ]; +	struct stat stat_buf; +	int i; +	struct rfc2045 *myrfcp; + +	if (fstat(fileno(fp), &stat_buf) < 0) +		return NULL; + +	if (fseek(fp, 0L, SEEK_SET) < 0) +		return NULL; + +	if (!(myrfcp=rfc2045_alloc())) +		return NULL; + +	do +	{ +		i=fread(buffer, 1, sizeof(buffer), fp); + +		if (i < 0) +		{ +			rfc2045_free(myrfcp); +			return NULL; +			} + +		if (i == 0) +			break; +		rfc2045_parse(myrfcp, buffer, i); +	} while (myrfcp->workinheader); + +	myrfcp->endpos=stat_buf.st_size; +	return myrfcp; +} + +static int do_dump_decoded(const char *chunk, size_t chunkSize, +			   void *vp) +{ +	struct decodeinfo *myDecodeInfo=(struct decodeinfo *) vp; + +	while (chunkSize) +	{ +		size_t n; + +		if (myDecodeInfo->bufptr >= sizeof(myDecodeInfo->buffer)) +			do_dump_decoded_flush(myDecodeInfo); + +		n=sizeof(myDecodeInfo->buffer)-myDecodeInfo->bufptr; + +		if (n > chunkSize) +			n=chunkSize; +		memcpy(myDecodeInfo->buffer + myDecodeInfo->bufptr, chunk, n); +		myDecodeInfo->bufptr += n; +		chunk += n; +		chunkSize -= n; +	} +	return 0; +} + +static void do_dump_decoded_flush(struct decodeinfo *myDecodeInfo) +{ +	size_t chunkSize= myDecodeInfo->bufptr; + +	myDecodeInfo->bufptr=0; + +	if (chunkSize == 0) +		return; + +	if (myDecodeInfo->first) +	{ +		myDecodeInfo->first=0; +		writes("{"); +		writen(chunkSize); +		writes("/"); +		writen(myDecodeInfo->estSize); +		writes("} FETCH "); +		writen(myDecodeInfo->msgNum+1); +		writes(" CONTENTS\n"); +	} +	else +	{ +		writen(chunkSize); +		writes("\n"); +	} +	writemem(myDecodeInfo->buffer, chunkSize); +	body_count += chunkSize; +} + +static int mime(int fd, unsigned long n, +		struct rfc2045 *rfcp, const char *hdrs) +{ +	int rc=dump_hdrs(fd, n, rfcp, hdrs, NULL); + +	if (rc) +		return rc; + +	for (rfcp=rfcp->firstpart; rfcp; rfcp=rfcp->next) +		if (!rfcp->isdummy) +		{ +			rc=mime(fd, n, rfcp, hdrs); +			if (rc) +				return rc; +		} + +	return 0; +} + +/* +** Find the specified MIME id. +*/ + +static struct rfc2045 *findmimeid(struct rfc2045 *rfcp, +				  const char *mimeid) +{ +	unsigned long n; + +	while (mimeid && *mimeid) +	{ +		const char *d; + +		n=0; + +		if (strchr(digit, *mimeid) == NULL) +			return NULL; + +		while (*mimeid && (d=strchr(digit, *mimeid)) != NULL) +		{ +			n=n * 10 + d-digit; +			mimeid++; +		} + +		while (rfcp) +		{ +			if (!rfcp->isdummy && rfcp->pindex == n) +				break; +			rfcp=rfcp->next; +		} + +		if (!rfcp) +			return NULL; + +		if (*mimeid == '.') +		{ +			++mimeid; +			rfcp=rfcp->firstpart; +		} +	} +	return rfcp; +} + +static int do_fetch(unsigned long n, void *vp) +{ +	struct smapfetchinfo *fi=(struct smapfetchinfo *)vp; +	FILE *fp=open_cached_fp(n); +	int rc=0; + +	if (!fp) +		return -1; + +	if (strcmp(fi->entity, "MIME") == 0) +	{ +		struct rfc2045 *rfcp=fetch_alloc_rfc2045(n, fp); +		int fd; + +		if (!rfcp) +			return -1; + +		fd=dup(fileno(fp)); +		if (fd < 0) +			return -1; + +		rc=mime(fd, n, rfcp, fi->hdrs); +		close(fd); +	} +	else if (strcmp(fi->entity, "HEADERS") == 0 || +		 strcmp(fi->entity, "RAWHEADERS") == 0) +	{ +		int fd; +		struct rfc2045 *rfcp; + +		fd=dup(fileno(fp)); +		if (fd < 0) +			return -1; + +		if (!fi->mimeid || !*fi->mimeid) +			rfcp=NULL; +		else +		{ +			rfcp=fetch_alloc_rfc2045(n, fp); + +			rfcp=findmimeid(rfcp, fi->mimeid); + +			if (!rfcp) +			{ +				close(fd); +				errno=EINVAL; +				return -1; +			} +		} + +		rc=dump_hdrs(fd, n, rfcp, fi->hdrs, fi->entity); +		close(fd); +	} +	else if (strcmp(fi->entity, "BODY") == 0 +		 || strcmp(fi->entity, "ALL") == 0) +	{ +		struct rfc2045 *rfcp; + +		if (!fi->mimeid || !*fi->mimeid) +			rfcp=NULL; +		else +		{ +			rfcp=fetch_alloc_rfc2045(n, fp); + +			rfcp=findmimeid(rfcp, fi->mimeid); + +			if (!rfcp) +			{ +				errno=EINVAL; +				return -1; +			} +		} + +		rc=dump_body(fp, n, rfcp, fi->entity[0] == 'A'); +	} +	else if (strcmp(fi->entity, "BODY.DECODED") == 0) +	{ +		struct rfc2045 *rfcp; + +		if (!fi->mimeid || !*fi->mimeid) +			rfcp=NULL; +		else +		{ +			rfcp=fetch_alloc_rfc2045(n, fp); + +			rfcp=findmimeid(rfcp, fi->mimeid); + +			if (!rfcp) +			{ +				errno=EINVAL; +				return -1; +			} +		} + +		rc=dump_decoded(fp, n, rfcp); +	} +	else +	{ +		rc=0; +	} + +	if (rc == 0 && !fi->peek) +	{ +		struct	imapflags	flags; + +		get_message_flags(current_maildir_info.msgs+n, +				  0, &flags); +		if (!flags.seen) +		{ +			flags.seen=1; +			reflag_filename(¤t_maildir_info.msgs[n], +					&flags, fileno(fp)); +			current_maildir_info.msgs[n].changedflags=1; +		} +	} + +	if (current_maildir_info.msgs[n].changedflags) +		fetchflags(n); + +	return rc; +} + +void smap_fetchflags(unsigned long n) +{ +	int items=FETCH_FLAGS | FETCH_KEYWORDS; + +	do_attrfetch(n, &items); +} + +static int do_attrfetch(unsigned long n, void *vp) +{ +	int items=*(int *)vp; + +	if (n >= current_maildir_info.nmessages) +		return 0; + +	writes("* FETCH "); +	writen(n+1); + +	if (items & FETCH_FLAGS) +	{ +		char	buf[256]; + +		get_message_flags(current_maildir_info.msgs+n, buf, 0); + +		writes(" FLAGS="); +		writes(buf); + +		current_maildir_info.msgs[n].changedflags=0; +	} + +	if ((items & FETCH_KEYWORDS) && keywords()) +	{ +		struct libmail_kwMessageEntry *kme; + +		writes(" \"KEYWORDS="); + +		if (current_maildir_info.msgs[n].keywordMsg && +		    current_maildir_info.msgs[n].keywordMsg->firstEntry) +		{ +			const char *p=""; + +			for (kme=current_maildir_info.msgs[n] +				     .keywordMsg->firstEntry; +			     kme; kme=kme->next) +			{ +				writes(p); +				p=","; +				writes(keywordName(kme->libmail_keywordEntryPtr)); +			} +		} +		writes("\""); +	} + +	if (items & FETCH_UID) +	{ +		char *p, *q; + +		writes(" \"UID="); + +		p=current_maildir_info.msgs[n].filename; + +		q=strrchr(p, MDIRSEP[0]); +		if (q) +			*q=0; +		smapword_s(p); +		if (q) +			*q=MDIRSEP[0]; +		writes("\""); +	} + +	if (items & FETCH_SIZE) +	{ +		char *p=current_maildir_info.msgs[n].filename; +		unsigned long cnt; + +		if (maildir_parsequota(p, &cnt)) +		{ +			FILE *fp=open_cached_fp(n); +			struct stat stat_buf; + +			if (fp && fstat(fileno(fp), &stat_buf) == 0) +				cnt=stat_buf.st_size; +			else +				cnt=0; +		} + +		writes(" SIZE="); +		writen(cnt); +	} + +	if (items & FETCH_INTERNALDATE) +	{ +		struct stat stat_buf; +		FILE *fp=open_cached_fp(n); + +		if (fp && fstat(fileno(fp), &stat_buf) == 0) +		{ +			char buf[256]; + +			rfc822_mkdate_buf(stat_buf.st_mtime, buf); +			writes(" \"INTERNALDATE="); +			smapword_s(buf); +			writes("\""); +		} +	} +	writes("\n"); +	return 0; +} + +struct add_rcptlist { +	struct add_rcptlist *next; +	char *rcptto; +}; + +static unsigned long add_msg(FILE *fp, const char *format, +			     char *buffer, +			     size_t bufsize) +{ +	unsigned long n=0; + +	writes("> Go ahead\n"); +	writeflush(); + +	if (*format == '.') +	{ +		int last_eol=1; +		int dot_stuffed=0; +		int counter=-1; + +		for (;;) +		{ +			char c; + +			if ( ((counter=counter + 1 ) % 8192) == 0) +				read_timeout(60); + +			c=READ(); + +			if (c == '\r') +				continue; + +			if (dot_stuffed && c == '\n') +				break; +			dot_stuffed=0; + +			if (c == '.') +			{ +				if (last_eol) +				{ +					dot_stuffed=1; +					continue; +				} +			} +			last_eol= c == '\n'; +			putc( (int)(unsigned char)c, fp); +			n++; +		} + +		if (!last_eol) +		{ +			putc('\n', fp); +			n++; +		} +	} +	else +	{ +		unsigned long chunkSize; +		char last_char='\n'; + +		while (sscanf(format, "%lu", &chunkSize) == 1) +		{ +			while (chunkSize) +			{ +				size_t nn=bufsize; +				size_t i; + +				if (nn > chunkSize) +					nn=(size_t)chunkSize; + +				read_timeout(60); +				nn=doread(buffer, nn); + +				chunkSize -= nn; +				n += nn; + +				for (i=0; i<nn; i++) +				{ +					last_char=buffer[i]; + +					if (last_char == '\r') +						continue; +					putc((int)(unsigned char)last_char, +					     fp); +				} +			} + +			read_timeout(60); +			smap_readline(buffer, bufsize); +			format=buffer; +		} + +		if (last_char != '\n') +		{ +			putc('\n', fp); +			n++; +		} +	} + +	if (n == 0) +	{ +		++n; +		putc('\n', fp); +	} + +	if (fflush(fp) < 0 || ferror(fp)) +		return 0; +	return n; +} + +static void adduid(char *n) +{ +	char *q; + +	q=strrchr(n, '/'); +	if (q) +		n=q+1; + +	q=strrchr(n, MDIRSEP[0]); +	if (q) +		*q=0; +	writes("* ADD \"UID="); +	smapword_s(n); +	writes("\"\n"); +	if (q) +		*q=MDIRSEP[0]; +} + +static void senderr(char *errmsg) +{ +	writes("-ERR "); +	writes(errmsg); +	writes("\n"); +} + +static int calc_quota(unsigned long n, void *voidptr) +{ +	return do_copy_quota_calc(n+1, 0, voidptr); +} + +/* Copy msg to another folder */ + +static void copieduid(unsigned long n, char *newname) +{ +	char *p, *q; + +	writes("* COPY "); +	writen(n); +	writes(" \"NEWUID="); + +	p=strrchr(newname, '/')+1; + +	if ((q=strrchr(p, MDIRSEP[0])) != NULL) +		*q=0; +	 +	smapword_s(p); +	writes("\"\n"); +} + +static int do_copyKeywords(struct libmail_kwMessage *msg, +			   const char *destmailbox, +			   const char *newname) +{ +	char *tmpkname, *newkname; + +	if (!msg || !msg->firstEntry) +		return 0; + +	if (maildir_kwSave(destmailbox, newname, +			   msg, &tmpkname, &newkname, 0)) +	{ +		perror("maildir_kwSave"); +		return -1; +	} + +	rename(tmpkname, newkname); +	free(tmpkname); +	free(newkname); +	return 0; +} + +static void fixnewfilename(char *p) +{ +	char *q; + +	/* Nice hack: */ + +	q=strrchr(strrchr(p, '/'), MDIRSEP[0]); + +	if (strcmp(q, MDIRSEP "2,") == 0) +	{ +		*q=0; +		memcpy(strrchr(p, '/')-3, "new", 3); +	} +} + +static int do_copymsg(unsigned long n, void *voidptr) +{ +	char buf[SMAP_BUFSIZ]; +	struct copyquotainfo *cqinfo=(struct copyquotainfo *)voidptr; +	struct imapflags new_flags; +	int fd; +	struct stat stat_buf; +	FILE *fp; +	char *tmpname, *newname; + +	fd=imapscan_openfile(current_mailbox, ¤t_maildir_info, n); +	if (fd < 0)	return (0); + +	if (fstat(fd, &stat_buf) < 0) +	{ +		close(fd); +		return (0); +	} + +	get_message_flags(current_maildir_info.msgs+n, 0, &new_flags); + +	fp=maildir_mkfilename(cqinfo->destmailbox, +			      &new_flags, stat_buf.st_size, +			      &tmpname, &newname); + +	fixnewfilename(newname); + +	if (!fp) +	{ +		close(fd); +		return (-1); +	} + +	while (stat_buf.st_size) +	{ +	int	n=sizeof(buf); + +		if (n > stat_buf.st_size) +			n=stat_buf.st_size; + +		n=read(fd, buf, n); + +		if (n <= 0 || fwrite(buf, 1, n, fp) != n) +		{ +			close(fd); +			fclose(fp); +			unlink(tmpname); +			free(tmpname); +			free(newname); +			fprintf(stderr, +			"ERR: error copying a message, user=%s, errno=%d\n", +				getenv("AUTHENTICATED"), errno); + +			return (-1); +		} +		stat_buf.st_size -= n; +	} +	close(fd); + +	if (fflush(fp) || ferror(fp)) +	{ +		fclose(fp); +		unlink(tmpname); +		free(tmpname); +		free(newname); +		fprintf(stderr, +			"ERR: error copying a message, user=%s, errno=%d\n", +			getenv("AUTHENTICATED"), errno); +		return (-1); +	} +	fclose(fp); + +	if (do_copyKeywords(current_maildir_info.msgs[n].keywordMsg, +			    cqinfo->destmailbox, +			    strrchr(newname, '/')+1)) +	{ +		unlink(tmpname); +		free(tmpname); +		free(newname); +		fprintf(stderr, +			"ERR: error copying keywords, " +			"user=%s, errno=%d\n", +			getenv("AUTHENTICATED"), errno); +		return (-1); +	} + +	current_maildir_info.msgs[n].copiedflag=1; + +	maildir_movetmpnew(tmpname, newname); +	set_time(newname, stat_buf.st_mtime); +	free(tmpname); + +	copieduid(n+1, newname); +	free(newname); +	return 0; +} + +static int do_movemsg(unsigned long n, void *voidptr) +{ +	char *filename; +	struct copyquotainfo *cqinfo=(struct copyquotainfo *)voidptr; +	char *newfilename; + +	if (n >= current_maildir_info.nmessages) +		return 0; + +	filename=maildir_filename(current_mailbox, 0, +				  current_maildir_info.msgs[n].filename); + +	if (!filename) +		return 0; + +	newfilename=malloc(strlen(cqinfo->destmailbox) + sizeof("/cur") +			   + strlen(strrchr(filename, '/'))); + +	if (!newfilename) +	{ +		free(filename); +		write_error_exit(0); +	} + +	strcat(strcat(strcpy(newfilename, cqinfo->destmailbox), +		      "/cur"), strrchr(filename, '/')); + +	if (do_copyKeywords(current_maildir_info.msgs[n].keywordMsg, +			    cqinfo->destmailbox, +			    strrchr(newfilename, '/')+1)) +	{ +		fprintf(stderr, +			"ERR: error copying keywords, " +			"user=%s, errno=%d\n", +			getenv("AUTHENTICATED"), errno); + +		free(filename); +		free(newfilename); +		return -1; +	} + + +	if (maildir_movetmpnew(filename, newfilename) == 0) +	{ +		copieduid(n+1, newfilename); +		free(filename); +		free(newfilename); +		return 0; +	} + +	if (do_copymsg(n, voidptr)) +		return -1; + +	unlink(filename); +	free(filename); +	free(newfilename); +	return 0; +} + +static struct searchinfo *createSearch2(char *w, +					struct searchinfo **head, char **ptr); + +static struct searchinfo *createSearch(struct searchinfo **head, char **ptr) +{ +	char *w=getword(ptr); +	struct searchinfo *siAnd, *n; + +	up(w); + +	if (strcmp(w, "MARKED") == 0) +	{ +		w=getword(ptr); +		up(w); + +		n=createSearch2(w, head, ptr); + +		if (!n) +			return NULL; + +		siAnd=alloc_search(head); +		siAnd->type=search_and; + +		siAnd->b=n; + +		siAnd->a=n=alloc_search(head); + +		n->type=search_msgflag; +		if (!(n->as=strdup("\\FLAGGED"))) +			write_error_exit(0); + +		return siAnd; +	} + +	if (strcmp(w, "UNMARKED") == 0) +	{ +		w=getword(ptr); +		up(w); + +		n=createSearch2(w, head, ptr); + +		if (!n) +			return NULL; + +		siAnd=alloc_search(head); +		siAnd->type=search_and; + +		siAnd->b=n; + +		siAnd->a=n=alloc_search(head); + +		n->type=search_not; + +		n=n->a=alloc_search(head); + +		n->type=search_msgflag; +		if (!(n->as=strdup("\\FLAGGED"))) +			write_error_exit(0); + +		return siAnd; +	} + +	if (strcmp(w, "ALL") == 0) +	{ +		w=getword(ptr); +		up(w); +		return createSearch2(w, head, ptr); +	} + +	{ +		char *ww=getword(ptr); +		up(ww); +		n=createSearch2(ww, head, ptr); + +		if (!n) +			return NULL; + +		siAnd=alloc_search(head); +		siAnd->type=search_and; + +		siAnd->b=n; + +		siAnd->a=n=alloc_search(head); + +		n->type=search_messageset; +		if (!(n->as=strdup(w))) +			write_error_exit(0); + +		for (ww=n->as; *ww; ww++) +			if (*ww == '-') +				*ww=':'; + +		if (!ismsgset_str(n->as)) +		{ +			errno=EINVAL; +			return NULL; +		} +	} +	return siAnd; +} + +static struct searchinfo *createSearch2(char *w, +					struct searchinfo **head, char **ptr) +{ +	int notflag=0; +	struct searchinfo *n; + +	if (strcmp(w, "NOT") == 0) +	{ +		notflag=1; +		w=getword(ptr); +		up(w); +	} + +	if (strcmp(w, "REPLIED") == 0) +	{ +		n=alloc_search(head); +		n->type=search_msgflag; +		if (!(n->as=strdup("\\ANSWERED"))) +			write_error_exit(0); +	} +	else if (strcmp(w, "DELETED") == 0) +	{ +		n=alloc_search(head); +		n->type=search_msgflag; +		if (!(n->as=strdup("\\DELETED"))) +			write_error_exit(0); +	} +	else if (strcmp(w, "DRAFT") == 0) +	{ +		n=alloc_search(head); +		n->type=search_msgflag; +		if (!(n->as=strdup("\\DRAFT"))) +			write_error_exit(0); +	} +	else if (strcmp(w, "SEEN") == 0) +	{ +		n=alloc_search(head); +		n->type=search_msgflag; +		if (!(n->as=strdup("\\SEEN"))) +			write_error_exit(0); +	} +	else if (strcmp(w, "KEYWORD") == 0) +	{ +		n=alloc_search(head); +		n->type=search_msgkeyword; +		if (!(n->as=strdup(getword(ptr)))) +			write_error_exit(0); +	} +	else if (strcmp(w, "FROM") == 0 || +		 strcmp(w, "TO") == 0 || +		 strcmp(w, "CC") == 0 || +		 strcmp(w, "BCC") == 0 || +		 strcmp(w, "SUBJECT") == 0) +	{ +		n=alloc_search(head); +		n->type=search_header; +		if (!(n->cs=strdup(w))) +			write_error_exit(0); +		n->as=strdup(getword(ptr)); +		if (!n->as) +			write_error_exit(0); +	} +	else if (strcmp(w, "HEADER") == 0) +	{ +		n=alloc_search(head); +		n->type=search_header; +		if (!(n->cs=strdup(getword(ptr)))) +			write_error_exit(0); +		up(n->cs); +		n->as=strdup(getword(ptr)); +		if (!n->as) +			write_error_exit(0); +	} +	else if (strcmp(w, "BODY") == 0) +	{ +		n=alloc_search(head); +		n->type=search_body; +		n->as=strdup(getword(ptr)); +		if (!n->as) +			write_error_exit(0); +	} +	else if (strcmp(w, "TEXT") == 0) +	{ +		n=alloc_search(head); +		n->type=search_text; +		n->as=strdup(getword(ptr)); +		if (!n->as) +			write_error_exit(0); +	} +	else if (strcmp(w, "BEFORE") == 0) +	{ +		n=alloc_search(head); +		n->type=search_before; +		n->as=strdup(getword(ptr)); +		if (!n->as) +			write_error_exit(0); +	} +	else if (strcmp(w, "ON") == 0) +	{ +		n=alloc_search(head); +		n->type=search_on; +		n->as=strdup(getword(ptr)); +		if (!n->as) +			write_error_exit(0); +	} +	else if (strcmp(w, "SINCE") == 0) +	{ +		n=alloc_search(head); +		n->type=search_since; +		n->as=strdup(getword(ptr)); +		if (!n->as) +			write_error_exit(0); +	} +	else if (strcmp(w, "SENTBEFORE") == 0) +	{ +		n=alloc_search(head); +		n->type=search_sentbefore; +		n->as=strdup(getword(ptr)); +		if (!n->as) +			write_error_exit(0); +	} +	else if (strcmp(w, "SENTON") == 0) +	{ +		n=alloc_search(head); +		n->type=search_senton; +		n->as=strdup(getword(ptr)); +		if (!n->as) +			write_error_exit(0); +	} +	else if (strcmp(w, "SINCE") == 0) +	{ +		n=alloc_search(head); +		n->type=search_sentsince; +		n->as=strdup(getword(ptr)); +		if (!n->as) +			write_error_exit(0); +	} +	else if (strcmp(w, "SMALLER") == 0) +	{ +		n=alloc_search(head); +		n->type=search_smaller; +		n->as=strdup(getword(ptr)); +		if (!n->as) +			write_error_exit(0); +	} +	else if (strcmp(w, "LARGER") == 0) +	{ +		n=alloc_search(head); +		n->type=search_larger; +		n->as=strdup(getword(ptr)); +		if (!n->as) +			write_error_exit(0); +	} +	else +	{ +		errno=EINVAL; +		return NULL; +	} + +	if (notflag) +	{ +		struct searchinfo *p=alloc_search(head); + +		p->type=search_not; +		p->a=n; +		n=p; +	} +	return n; +} + +static int do_copyto(char *toFolder, +		     int (*do_func)(unsigned long, void *), +		     const char *acls) +{ +	int has_quota=0; +	struct copyquotainfo cqinfo; +	struct maildirsize quotainfo; + +	cqinfo.destmailbox=toFolder; +	cqinfo.nbytes=0; +	cqinfo.nfiles=0; +	cqinfo.acls=acls; + +	if (maildirquota_countfolder(toFolder)) +	{ +		if (maildir_openquotafile("ainfo, ".") == 0) +		{ +			if (quotainfo.fd >= 0) +				has_quota=1; +			maildir_closequotafile("ainfo); +		} + +		if (has_quota > 0 && applymsgset( &calc_quota, &cqinfo )) +			has_quota= -1; +	} + +	if (has_quota > 0 && cqinfo.nfiles > 0) +	{ +		if (maildir_quota_add_start(".", "ainfo, +					    cqinfo.nbytes, +					    cqinfo.nfiles, +					    getenv("MAILDIRQUOTA"))) +		{ +			errno=ENOSPC; +			return (-1); +		} + +		maildir_quota_add_end("ainfo, +				      cqinfo.nbytes, +				      cqinfo.nfiles); +	} + +	return applymsgset(do_func, &cqinfo); +} + +static int copyto(char *toFolder, int do_move, const char *acls) +{ +	if (!do_move) +		return do_copyto(toFolder, &do_copymsg, acls); + +	if (!current_mailbox_shared && +	    maildirquota_countfolder(current_mailbox) == +	    maildirquota_countfolder(toFolder)) +	{ +		if (do_copyto(toFolder, do_movemsg, acls)) +			return -1; + +		doNoop(0); +		return(0); +	} + +	if (do_copyto(toFolder, &do_copymsg, acls)) +		return -1; + +	applymsgset(&msg_expunge, NULL); +	doNoop(0); +	return 0; +} + +struct smap1_search_results { + +	unsigned prev_runs; + +	unsigned long prev_search_hit; +	unsigned long prev_search_hit_start; +}; + +static void smap1_search_cb_range(struct smap1_search_results *searchResults) +{ +	if (searchResults->prev_runs > 100) +	{ +		writes("\n"); +		searchResults->prev_runs=0; +	} + +	if (searchResults->prev_runs == 0) +		writes("* SEARCH"); + +	writes(" "); +	writen(searchResults->prev_search_hit_start); +	if (searchResults->prev_search_hit_start != +	    searchResults->prev_search_hit) +	{ +		writes("-"); +		writen(searchResults->prev_search_hit); +	} +	++searchResults->prev_runs; +} + +static void smap1_search_cb(struct searchinfo *si, +			    struct searchinfo *sihead, +			    int isuid, unsigned long i, void *dummy) +{ +	struct smap1_search_results *searchResults= +		(struct smap1_search_results *)dummy; + +	++i; + +	if (searchResults->prev_search_hit == 0) +	{ +		searchResults->prev_search_hit= +			searchResults->prev_search_hit_start=i; +		return; +	} + +	if (i != searchResults->prev_search_hit+1) +	{ +		smap1_search_cb_range(searchResults); +		searchResults->prev_search_hit_start=i; +	} + +	searchResults->prev_search_hit=i; +} + +static void accessdenied(const char *acl_required) +{ +	writes("-ERR Access denied: ACL \""); +	writes(acl_required); +	writes("\" is required\n"); +} + +static int getacl(const char *ident, +		  const maildir_aclt *acl, +		  void *cb_arg) +{ +	int *n=(int *)cb_arg; + +	if (*n > 5) +	{ +		writes("\n"); +		*n=0; +	} + +	if (*n == 0) +		writes("* GETACL"); + +	writes(" "); +	smapword(ident); +	writes(" "); +	smapword(maildir_aclt_ascstr(acl)); +	++*n; +	return 0; +} + +struct setacl_info { +	struct maildir_info minfo; +	char **ptr; +}; + +static int dosetdeleteacl(void *cb_arg, int); + +static int setacl(void *cb_arg) +{ +	return dosetdeleteacl(cb_arg, 0); +} + +static int deleteacl(void *cb_arg) +{ +	return dosetdeleteacl(cb_arg, 1); +} + +static int dosetdeleteacl(void *cb_arg, int dodelete) +{ +	struct setacl_info *sainfo=(struct setacl_info *)cb_arg; +	char *q; +	int cnt; +	const char *identifier; +	const char *action; +	const char *err_failedrights; +	char *path; + +	maildir_aclt_list aclt_list; + +	if (read_acls(&aclt_list, &sainfo->minfo) < 0) +	{ +		writes("-ERR Unable to read existing ACLS: "); +		writes(strerror(errno)); +		writes("\n"); +		return 0; +	} + +	q=compute_myrights(&aclt_list, +			   sainfo->minfo.owner); + +	if (!q || !strchr(q, ACL_ADMINISTER[0])) +	{ +		if (q) free(q); +		maildir_aclt_list_destroy(&aclt_list); +		accessdenied(ACL_ADMINISTER); +		return 0; +	} + +	free(q); + +	while (*(identifier=getword(sainfo->ptr))) +	{ +		if (dodelete) +		{ +			if (maildir_aclt_list_del(&aclt_list, +						  identifier) < 0) +			{ +				maildir_aclt_list_destroy(&aclt_list); +				writes("-ERR Error: "); +				writes(strerror(errno)); +				writes("\n"); +				return 0; +			} +			continue; +		} + +		action=getword(sainfo->ptr); + +		if (*action == '+') +		{ +			maildir_aclt newacl; +			const maildir_aclt *oldacl; + +			if (maildir_aclt_init(&newacl, +					      action+1, +					      NULL) < 0) +			{ +				maildir_aclt_list_destroy(&aclt_list); +				writes("-ERR Error: "); +				writes(strerror(errno)); +				writes("\n"); +				return 0; +			} + + +			oldacl=maildir_aclt_list_find(&aclt_list, +						      identifier +						      ); +			if (oldacl) +			{ +				if (maildir_aclt_add(&newacl, +						     NULL, +						     oldacl) +				    < 0) +				{ +					maildir_aclt_destroy(&newacl); +					maildir_aclt_list_destroy(&aclt_list); +					writes("-ERR Error: "); +					writes(strerror(errno)); +					writes("\n"); +					return 0; +				} +			} + +			if (maildir_aclt_list_add(&aclt_list, +						  identifier, +						  NULL, +						  &newacl) < 0) +			{ +				maildir_aclt_destroy(&newacl); +				maildir_aclt_list_destroy(&aclt_list); +				writes("-ERR Error: "); +				writes(strerror(errno)); +				writes("\n"); +				return 0; + +			} +			maildir_aclt_destroy(&newacl); +			continue; +		} + +		if (*action == '-') +		{ +			maildir_aclt newacl; +			const maildir_aclt *oldacl; + +			oldacl=maildir_aclt_list_find(&aclt_list, +						      identifier +						      ); + +			if (!oldacl) +				continue; + +			if (maildir_aclt_init(&newacl, +					      NULL, +					      oldacl) < 0) +			{ +				maildir_aclt_list_destroy(&aclt_list); +				writes("-ERR Error: "); +				writes(strerror(errno)); +				writes("\n"); +				return 0; +			} + + +			if (maildir_aclt_del(&newacl, +					     action+1, NULL) +				    < 0) +			{ +				maildir_aclt_destroy(&newacl); +				maildir_aclt_list_destroy(&aclt_list); +				writes("-ERR Error: "); +				writes(strerror(errno)); +				writes("\n"); +				return 0; +			} + +			if (strlen(maildir_aclt_ascstr(&newacl)) +			    == 0 ? +			    maildir_aclt_list_del(&aclt_list, +						  identifier) +			    :maildir_aclt_list_add(&aclt_list, +						   identifier, +						   NULL, +						   &newacl) < 0) +			{ +				maildir_aclt_destroy(&newacl); +				maildir_aclt_list_destroy(&aclt_list); +				writes("-ERR Error: "); +				writes(strerror(errno)); +				writes("\n"); +				return 0; + +			} +			maildir_aclt_destroy(&newacl); +			continue; +		} + +		if (strlen(action) == 0 ? +		    maildir_aclt_list_del(&aclt_list, +					  identifier): +		    maildir_aclt_list_add(&aclt_list, +					  identifier, +					  action, NULL) < 0) +		{ +			maildir_aclt_list_destroy(&aclt_list); +			writes("-ERR Error: "); +			writes(strerror(errno)); +			writes("\n"); +			return 0; +		} +	} + +	path=maildir_name2dir(".", sainfo->minfo.maildir); + +	err_failedrights=NULL; +	if (!path || +	    maildir_acl_write(&aclt_list, sainfo->minfo.homedir, +			      path[0] == '.' && path[1] == '/' +			      ? path+2:path, +			      sainfo->minfo.owner, +			      &err_failedrights)) +	{ +		if (path) +			free(path); + +		if (err_failedrights) +		{ +			writes("* ACLMINIMUM "); +			writes(err_failedrights); +			writes(" "); +			aclminimum(err_failedrights); +			writes("\n"); +		} +		writes("-ERR ACL update failed\n"); +		maildir_aclt_list_destroy(&aclt_list); +		return 0; +	} + +	cnt=0; +	maildir_aclt_list_enum(&aclt_list, +			       getacl, &cnt); +	if (cnt) +		writes("\n"); +	maildir_aclt_list_destroy(&aclt_list); + +	/* Reread ACLs if the current mailbox's ACLs have changed */ + +	if (read_acls(&aclt_list, &sainfo->minfo) < 0) +	{ +		writes("-ERR Unable to re-read ACLS: "); +		writes(strerror(errno)); +		writes("\n"); +		return 0; +	} + +	maildir_aclt_list_destroy(&aclt_list); +	writes("+OK Updated ACLs\n"); +	return 0; +} + +static int checkacl(char **folder, struct maildir_info *minfo, +		    const char *acls) +{ +	char *q; + +	maildir_aclt_list aclt_list; + +	if (maildir_info_smap_find(minfo, folder, getenv("AUTHENTICATED")) < 0) +		return -1; + +	if (read_acls(&aclt_list, minfo) < 0) +	{ +		maildir_info_destroy(minfo); +		return -1; +	} + +	q=compute_myrights(&aclt_list, minfo->owner); +	maildir_aclt_list_destroy(&aclt_list); + +	while (*acls) +	{ +		if (q == NULL || strchr(q, *acls) == NULL) +		{ +			if (q) free(q); +			maildir_info_destroy(minfo); +			return -1; +		} +		++acls; +	} +	if (q) +		free(q); +	return 0; +} + +void smap() +{ +	char buffer[8192]; +	char *ptr; +	struct imapflags add_flags; +	int in_add=0; +	char *add_from=NULL; +	char *add_folder=NULL; +	time_t add_internaldate=0; +	char *add_notify=NULL; +	unsigned add_rcpt_count=0; +	struct libmail_kwMessage *addKeywords=NULL; + +	struct add_rcptlist *add_rcpt_list=NULL; + +	char rights_buf[40]; + +	imapscan_init(¤t_maildir_info); +	memset(&add_flags, 0, sizeof(add_flags)); + +#define GETFOLDER(acl) ( strcpy(rights_buf, (acl)), \ +			getAccessToFolder(&ptr, rights_buf)) + +	for (;;) +	{ +		char *p; + +		writeflush(); +		read_timeout(30 * 60); +		smap_readline(buffer, sizeof(buffer)); + +		ptr=buffer; + +		p=getword(&ptr); +		up(p); + +		if (strcmp(p, "ADD") == 0) +		{ +			char **argvec; +			const char *okmsg="So far, so good..."; +			int err_sent=0; + +			in_add=1; +			while (*(p=getword(&ptr))) +			{ +				char *q=strchr(p, '='); + +				if (q) +					*q++=0; +				up(p); + +				if (strcmp(p, "FOLDER") == 0) +				{ +					if (add_folder) +						free(add_folder); + +					add_folder= +						GETFOLDER(ACL_INSERT +							  ACL_DELETEMSGS +							  ACL_SEEN +							  ACL_WRITE); +					if (!add_folder) +					{ +						writes("-ERR Invalid folder: "); +						writes(strerror(errno)); +						writes("\n"); +						break; +					} + +					if (strchr(rights_buf, +						   ACL_INSERT[0]) +					    == NULL) +					{ +						accessdenied(ACL_INSERT); +						free(add_folder); +						add_folder=NULL; +						break; +					} + +					okmsg="Will add to this folder"; +				} + +				if (strcmp(p, "MAILFROM") == 0 && q) +				{ +					if (add_from) +						free(add_from); +					if ((add_from=strdup(q)) == NULL) +					{ +						writes("-ERR "); +						writes(strerror(errno)); +						writes("\n"); +						break; +					} +					okmsg="MAIL FROM set"; +				} + +				if (strcmp(p, "NOTIFY") == 0 && q) +				{ +					if (add_notify) +						free(add_notify); +					if ((add_notify=strdup(q)) == NULL) +					{ +						writes("-ERR "); +						writes(strerror(errno)); +						writes("\n"); +						break; +					} +					okmsg="NOTIFY set"; +				} + +				if (strcmp(p, "RCPTTO") == 0 && q) +				{ +					struct add_rcptlist *rcpt= +						malloc(sizeof(struct +							      add_rcptlist)); + +					if (rcpt == NULL || +					    (rcpt->rcptto=strdup(q)) == NULL) +					{ +						if (rcpt) +							free(rcpt); +						writes("-ERR "); +						writes(strerror(errno)); +						writes("\n"); +						break; +					} +					rcpt->next=add_rcpt_list; +					add_rcpt_list=rcpt; +					++add_rcpt_count; +					okmsg="RCPT TO set"; +				} + +				if (strcmp(p, "FLAGS") == 0 && q) +				{ +					memset(&add_flags, 0, +					       sizeof(add_flags)); +					*--q='='; +					parseflags(q, &add_flags); + +					if (strchr(rights_buf, +						   ACL_SEEN[0]) +					    == NULL) +						add_flags.seen=0; +					if (strchr(rights_buf, +						   ACL_DELETEMSGS[0]) +					    == NULL) +						add_flags.deleted=0; +					if (strchr(rights_buf, +						   ACL_WRITE[0]) +					    == NULL) +						add_flags.answered= +							add_flags.flagged= +							add_flags.drafts=0; + +					okmsg="FLAGS set"; +				} + +				if (strcmp(p, "KEYWORDS") == 0 && q && +				    keywords() && strchr(rights_buf, +							 ACL_WRITE[0])) +				{ +					if (addKeywords) +						libmail_kwmDestroy(addKeywords); + +					addKeywords=libmail_kwmCreate(); + +					if (addKeywords == NULL) +					{ +						write_error_exit(0); +					} + +					*--q='='; +					parsekeywords(q, &addKeywords); +					okmsg="KEYWORDS set"; +				} + +				if (strcmp(p, "INTERNALDATE") == 0 && q) +				{ +					add_internaldate=rfc822_parsedt(q); + +					if (add_internaldate) +						okmsg="INTERNALDATE set"; +				} + +				if (p[0] == '{') +				{ +					char *tmpname, *newname; +					char *s; +					char *tmpKeywords=NULL; +					char *newKeywords=NULL; +					FILE *fp; +					unsigned long n; + +					fp=maildir_mkfilename(add_folder +							      ?add_folder +							      :".", +							      &add_flags, +							      0, +							      &tmpname, +							      &newname); + +					if (!fp) +					{ +						writes("-ERR "); +						writes(strerror(errno)); +						writes("\n"); +						break; +					} + +					fixnewfilename(newname); + +					current_temp_fd=fileno(fp); +					current_temp_fn=tmpname; + +					n=add_msg(fp, p+1, buffer, +						  sizeof(buffer)); + +					if (n) +					{ +						s=maildir_requota(newname, n); + +						if (!s) +							n=0; +						else +						{ +							free(newname); +							newname=s; +						} +					} + +					current_temp_fd= -1; +					current_temp_fn= NULL; + +					if (n > 0 && add_folder && +					    maildirquota_countfolder(add_folder) +					    && maildirquota_countfile(newname)) +					{ +						struct maildirsize quotainfo; + +						if (maildir_quota_add_start(add_folder, "ainfo, n, 1, +									    getenv("MAILDIRQUOTA"))) +						{ +							errno=ENOSPC; +							n=0; +						} +						else +							maildir_quota_add_end("ainfo, n, 1); +					} + +					fclose(fp); + +					chmod(tmpname, 0600); + +					if (add_folder && n && addKeywords) +					{ +						if (maildir_kwSave(add_folder, +								   strrchr(newname, '/')+1, +								   addKeywords, +								   &tmpKeywords, +								   &newKeywords, +								   0)) +						{ +							tmpKeywords=NULL; +							newKeywords=NULL; +							n=0; +							perror("maildir_kwSave"); +						} +					} + +					argvec=NULL; + +					if (add_rcpt_count > 0 && n) +					{ +						argvec=malloc(sizeof(char *) +							      * (add_rcpt_count +								 +10)); + +						if (!argvec) +							n=0; +					} + +					if (argvec) +					{ +						int i=1; +						struct add_rcptlist *l; + +						argvec[i++]="-oi"; + +						argvec[i++]="-f"; +						argvec[i++]=add_from +							? add_from: +							(char *) +							defaultSendFrom(); + +						if (add_notify) +						{ +							argvec[i++]="-N"; +							argvec[i++]=add_notify; +						} + +						for (l=add_rcpt_list; l; +						     l=l->next) +						{ +							argvec[i++]=l->rcptto; +						} +						argvec[i]=0; + +						i=imapd_sendmsg(tmpname, argvec, +								&senderr); +						free(argvec); +						if (i) +						{ +							n=0; +							err_sent=1; +						} +					} + +					if (tmpKeywords) +					{ +						rename(tmpKeywords, +						       newKeywords); +						free(tmpKeywords); +						free(newKeywords); +					} + +					if (add_folder && n) +					{ +						if (maildir_movetmpnew(tmpname, +								       newname) +						    ) +							n=0; +						else +						{ +							if (add_internaldate) +								set_time(newname, +									 add_internaldate); +							adduid(newname); +						} +					} + +					if (n == 0) +					{ +						unlink(tmpname); +						free(tmpname); +						free(newname); +						if (!err_sent) +						{ +							writes("-ERR "); +							writes(strerror(errno)); +							writes("\n"); +						} +						break; +					} + +					unlink(tmpname); + +					free(tmpname); +					free(newname); +					okmsg="Message saved"; +					p=NULL; +					break; +				} +			} + +			if (p && *p) +				continue; /* Error inside the loop */ + +			writes("+OK "); +			writes(okmsg); +			writes("\n"); + +			if (p) +				continue; +		} + +		if (in_add) +		{ +			struct add_rcptlist *l; + +			while ((l=add_rcpt_list) != NULL) +			{ +				add_rcpt_list=l->next; +				free(l->rcptto); +				free(l); +			} +			memset(&add_flags, 0, sizeof(add_flags)); +			if (add_from) +				free(add_from); +			if (add_folder) +				free(add_folder); +			if (add_notify) +				free(add_notify); + +			if (addKeywords) +				libmail_kwmDestroy(addKeywords); + +			in_add=0; +			add_from=NULL; +			add_folder=NULL; +			add_internaldate=0; +			add_notify=NULL; +			addKeywords=NULL; +			add_rcpt_count=0; +			if (!p) +				continue; /* Just added a message */ +		} + +		if (strcmp(p, "LOGOUT") == 0) +			break; + +		if (strcmp(p, "RSET") == 0) +		{ +			writes("+OK Reset\n"); +			continue; +		} + +		if (strcmp(p, "GETACL") == 0 || +		    strcmp(p, "ACL") == 0) +		{ +			char **fn=fn_fromwords(&ptr); +			struct maildir_info minfo; +			maildir_aclt_list aclt_list; +			char *q; +			int cnt; + +			if (!fn) +			{ +				writes("-ERR Invalid folder: "); +				writes(strerror(errno)); +				writes("\n"); +				continue; +			} + +			if (maildir_info_smap_find(&minfo, fn, +						   getenv("AUTHENTICATED")) +			    < 0) +			{ +				maildir_smapfn_free(fn); +				writes("-ERR Invalid folder: "); +				writes(strerror(errno)); +				writes("\n"); +				continue; +			} + +			if (read_acls(&aclt_list, &minfo) < 0) +			{ +				maildir_info_destroy(&minfo); +				maildir_smapfn_free(fn); +				writes("-ERR Unable to read" +				       " existing ACLS: "); +				writes(strerror(errno)); +				writes("\n"); +				continue; +			} + +			q=compute_myrights(&aclt_list, +					   minfo.owner); + +			if (!q || +			    strcmp(p, "ACL") ? +			    !strchr(q, ACL_ADMINISTER[0]) +			    : +			    !maildir_acl_canlistrights(q) +			    ) +			{ +				if (q) free(q); +				maildir_aclt_list_destroy(&aclt_list); +				maildir_info_destroy(&minfo); +				maildir_smapfn_free(fn); +				accessdenied(ACL_ADMINISTER); +				continue; +			} +			if (strcmp(p, "ACL") == 0) +			{ +				writes("* ACL "); +				smapword(q); +				writes("\n"); +				free(q); +			} +			else +			{ +				free(q); +				cnt=0; +				maildir_aclt_list_enum(&aclt_list, +						       getacl, &cnt); +				if (cnt) +					writes("\n"); +			} +			maildir_aclt_list_destroy(&aclt_list); +			maildir_info_destroy(&minfo); +			maildir_smapfn_free(fn); +			writes("+OK ACLs retrieved\n"); +			continue; +		} + +		if (strcmp(p, "SETACL") == 0 || +		    strcmp(p, "DELETEACL") == 0) +		{ +			char **fn=fn_fromwords(&ptr); +			struct setacl_info sainfo; + +			if (!fn) +			{ +				writes("-ERR Invalid folder: "); +				writes(strerror(errno)); +				writes("\n"); +				continue; +			} + +			if (maildir_info_smap_find(&sainfo.minfo, +						   fn, getenv("AUTHENTICATED")) +			    < 0) +			{ +				maildir_smapfn_free(fn); +				writes("-ERR Invalid folder: "); +				writes(strerror(errno)); +				writes("\n"); +				continue; +			} + + +			sainfo.ptr= &ptr; + +			acl_lock(sainfo.minfo.homedir, +				 *p == 'S' ? setacl:deleteacl, +				 &sainfo); + +			maildir_smapfn_free(fn); +			maildir_info_destroy(&sainfo.minfo); +			continue; +		} + +		if (strcmp(p, "LIST") == 0) +		{ +			struct list_hier *hier=NULL; + +			listcmd(&hier, &hier, &ptr); +			continue; +		} + +		if (strcmp(p, "STATUS") == 0) +		{ +			char *t; +			struct imapscaninfo other_info, *loaded_infoptr, +				*infoptr; +			unsigned long n, i; + +			getword(&ptr); + +			t=GETFOLDER(ACL_LOOKUP); + +			if (!t) +			{ +				writes("-ERR Cannot read folder status: "); +				writes(strerror(errno)); +				writes("\n"); +				continue; +			} + +			if (strchr(rights_buf, ACL_LOOKUP[0]) == NULL) +			{ +				accessdenied(ACL_LOOKUP); +				continue; +			} + +			if (current_mailbox && +			    strcmp(current_mailbox, t) == 0) +			{ +				loaded_infoptr=0; +				infoptr= ¤t_maildir_info; +			} +			else +			{ +				loaded_infoptr= &other_info; +				infoptr=loaded_infoptr; + +				imapscan_init(loaded_infoptr); + +				if (imapscan_maildir(infoptr, t, 1, 1, NULL)) +				{ +					writes("-ERR Cannot read" +					       " folder status: "); +					writes(strerror(errno)); +					writes("\n"); +					continue; +				} +			} + +			writes("* STATUS EXISTS="); +			writen(infoptr->nmessages+infoptr->left_unseen); + +			n=infoptr->left_unseen, i; + +			for (i=0; i<infoptr->nmessages; i++) +			{ +				const char *p=infoptr->msgs[i].filename; + +				p=strrchr(p, MDIRSEP[0]); +				if (p && strncmp(p, MDIRSEP "2,", 3) == 0 && +				    strchr(p, 'S'))	continue; +				++n; +			} +			writes(" UNSEEN="); +			writen(n); +			writes("\n+OK Folder status retrieved\n"); +			if (loaded_infoptr) +				imapscan_free(loaded_infoptr); +			continue; +		} + +		if (strcmp(p, "CREATE") == 0) +		{ +			char *t; + +			strcpy(rights_buf, ACL_CREATE); +			t=getCreateFolder(&ptr, rights_buf); + +			if (t) +			{ +				if (mdcreate(t)) +				{ +					writes("-ERR Cannot create folder: "); +					writes(strerror(errno)); +					writes("\n"); +				} +				else +				{ +					writes("+OK Folder created\n"); +				} +				free(t); +			} +			else +			{ +				if (rights_buf[0] == 0) +					accessdenied(ACL_CREATE); +				else +				{ +					writes("-ERR Cannot create folder: "); +					writes(strerror(errno)); +					writes("\n"); +				} +			} +			continue; +		} + +		if (strcmp(p, "MKDIR") == 0) +		{ +			char *t; + +			strcpy(rights_buf, ACL_CREATE); +			t=getCreateFolder(&ptr, rights_buf); + +			if (t) +			{ +				writes("+OK Folder created\n"); +				free(t); +			} +			else if (rights_buf[0] == 0) +				accessdenied(ACL_CREATE); +			else +			{ +				writes("-ERR Cannot create folder: "); +				writes(strerror(errno)); +				writes("\n"); +			} +			continue; +		} + +		if (strcmp(p, "RMDIR") == 0) +		{ +			char *t; + +			strcpy(rights_buf, ACL_DELETEFOLDER); +			t=getCreateFolder(&ptr, rights_buf); + +			if (t) +			{ +				writes("+OK Folder deleted\n"); +				free(t); +			} +			else if (rights_buf[0] == 0) +				accessdenied(ACL_DELETEFOLDER); +			else +			{ +				writes("-ERR Cannot create folder: "); +				writes(strerror(errno)); +				writes("\n"); +			} +			continue; +		} + +		if (strcmp(p, "DELETE") == 0) +		{ +			char **fn; +			char *t=NULL; + +			fn=fn_fromwords(&ptr); + +			if (fn) +			{ +				struct maildir_info minfo; + +				if (maildir_info_smap_find(&minfo, fn, +							   getenv("AUTHENTICATED")) == 0) +				{ +					if (minfo.homedir && minfo.maildir) +					{ +						maildir_aclt_list list; +						char *q; + +						if (strcmp(minfo.maildir, +							   INBOX) == 0) +						{ +							writes("-ERR INBOX may" +							       " not be deleted\n"); +							maildir_info_destroy(&minfo); +							continue; +						} + +						if (read_acls(&list, &minfo) +						    < 0) +						{ +							maildir_info_destroy(&minfo); +							accessdenied(ACL_DELETEFOLDER); +							continue; +						} + +						q=compute_myrights(&list, +								   minfo.owner +								   ); + +						if (!q || +						    strchr(q, +							   ACL_DELETEFOLDER[0]) +						    == NULL) +						{ +							if (q) +								free(q); +							maildir_info_destroy(&minfo); +							accessdenied(ACL_DELETEFOLDER); +							continue; +						} +						free(q); +						maildir_aclt_list_destroy(&list); +						t=maildir_name2dir(minfo.homedir, +								   minfo.maildir); +					} +					maildir_info_destroy(&minfo); +				} +			} + +			if (t && current_mailbox && +			    strcmp(t, current_mailbox) == 0) +			{ +				writes("-ERR Cannot DELETE currently open folder.\n"); +				free(t); +				continue; +			} + + +			if (t) +			{ +				if (mddelete(t) == 0) +				{ +					maildir_quota_recalculate("."); +					writes("+OK Folder deleted"); +				} +				else +				{ +					writes("-ERR Cannot delete folder: "); +					writes(strerror(errno)); +				} +				writes("\n"); +				free(t); + +			} +			else +			{ +				if (t) +				{ +					free(t); +					errno=EINVAL; +				} + +				writes("-ERR Unable to delete folder: "); +				writes(strerror(errno)); +				writes("\n"); +			} +			continue; +		} + +		if (strcmp(p, "RENAME") == 0) +		{ +			struct maildir_info msrc, mdst; +			char **fnsrc, **fndst; +			size_t i; +			char *save; +			const char *errmsg; + +			if ((fnsrc=fn_fromwords(&ptr)) == NULL) +			{ +				writes("-ERR "); +				writes(strerror(errno)); +				writes("\n"); +				continue; +			} + +			if ((fndst=fn_fromwords(&ptr)) == NULL) +			{ +				maildir_smapfn_free(fnsrc); +				writes("-ERR "); +				writes(strerror(errno)); +				writes("\n"); +				continue; +			} + +			for (i=0; fndst[i]; i++) +				; + +			if (i == 0) +			{ +				maildir_smapfn_free(fnsrc); +				maildir_smapfn_free(fndst); +				writes("-ERR Invalid destination folder name\n"); +				continue; +			} + +			if (checkacl(fnsrc, &msrc, ACL_DELETEFOLDER)) +			{ +				maildir_smapfn_free(fnsrc); +				maildir_smapfn_free(fndst); +				accessdenied(ACL_DELETEFOLDER); +				continue; +			} +			save=fndst[--i]; +			fndst[i]=NULL; + +			if (checkacl(fndst, &mdst, ACL_CREATE)) +			{ +				fndst[i]=save; +				maildir_smapfn_free(fnsrc); +				maildir_smapfn_free(fndst); +				maildir_info_destroy(&msrc); +				accessdenied(ACL_CREATE); +				continue; +			} + +			fndst[i]=save; + +			maildir_info_destroy(&mdst); + +			if (maildir_info_smap_find(&mdst, fndst, +						   getenv("AUTHENTICATED")) < 0) +			{ +				maildir_smapfn_free(fnsrc); +				maildir_smapfn_free(fndst); +				maildir_info_destroy(&msrc); +				writes("-ERR Internal error in RENAME: "); +				writes(strerror(errno)); +				writes("\n"); +				continue; +			} + +			if (folder_rename(&msrc, &mdst, &errmsg)) +			{ +				writes("-ERR "); +				writes(*errmsg == '@' ? errmsg+1:errmsg); +				if (*errmsg == '@') +					writes(strerror(errno)); +				writes("\n"); +			} +			else +			{ +				writes("+OK Folder renamed.\n"); +			} +			maildir_info_destroy(&msrc); +			maildir_info_destroy(&mdst); +			continue; +		} + +		if (strcmp(p, "OPEN") == 0 || +		    strcmp(p, "SOPEN") == 0) +		{ +			char **fn; +			char *q; +			const char *snapshot=0; +			struct maildir_info minfo; +			maildir_aclt_list aclt_list; + +			if (current_mailbox) +			{ +				free(current_mailbox); +				imapscan_free(¤t_maildir_info); +				imapscan_init(¤t_maildir_info); +				current_mailbox=0; +			} +			if (current_mailbox_acl) +				free(current_mailbox_acl); +			current_mailbox_acl=NULL; +			current_mailbox_shared=0; + +			fetch_free_cache(); + +			if (p[0] == 'S') +				snapshot=getword(&ptr); + +			fn=fn_fromwords(&ptr); + +			if (!fn) +			{ +				writes("-ERR Invalid folder: "); +				writes(strerror(errno)); +				writes("\n"); +				continue; +			} + +			if (maildir_info_smap_find(&minfo, fn, +						   getenv("AUTHENTICATED")) +			    < 0) +			{ +				maildir_smapfn_free(fn); +				writes("-ERR Invalid folder: "); +				writes(strerror(errno)); +				writes("\n"); +				continue; +			} + +			if (read_acls(&aclt_list, &minfo) < 0) +			{ +				maildir_info_destroy(&minfo); +				maildir_smapfn_free(fn); +				writes("-ERR Unable to read" +				       " existing ACLS: "); +				writes(strerror(errno)); +				writes("\n"); +				continue; +			} + +			q=compute_myrights(&aclt_list, minfo.owner); +			maildir_aclt_list_destroy(&aclt_list); +			maildir_smapfn_free(fn); + +			if (!q || strchr(q, ACL_READ[0]) == NULL) +			{ +				if (q) free(q); +				maildir_info_destroy(&minfo); +				accessdenied(ACL_READ); +				maildir_info_destroy(&minfo); +				continue; +			} +			current_mailbox_acl=q; +			current_mailbox=maildir_name2dir(minfo.homedir, +							 minfo.maildir); + +			if (current_mailbox == NULL) +			{ +				fprintf(stderr, "ERR: Internal error" +					" in maildir_name2dir(%s,%s)\n", +					minfo.homedir, +					minfo.maildir); +				maildir_info_destroy(&minfo); +				continue; +			} +			maildir_info_destroy(&minfo); + +			snapshot_select(snapshot != NULL); + +			if (snapshot_init(current_mailbox, snapshot)) +			{ +				writes("* SNAPSHOTEXISTS "); +				smapword(snapshot); +				writes("\n"); +				smap1_noop(0); +				continue; +			} + +			if (imapscan_maildir(¤t_maildir_info, +					     current_mailbox, 0, 0, NULL) == 0) +			{ +				snapshot_init(current_mailbox, NULL); +				writes("* EXISTS "); +				writen(current_maildir_info.nmessages); +				writes("\n+OK Folder opened\n"); +				continue; +			} + +			writes("-ERR Cannot open the folder: "); +			writes(strerror(errno)); +			writes("\n"); + +			free(current_mailbox); +			current_mailbox=NULL; +			continue; +		} + +		if (strcmp(p, "CLOSE") == 0) +		{ +			if (current_mailbox) +			{ +				free(current_mailbox); +				imapscan_free(¤t_maildir_info); +				imapscan_init(¤t_maildir_info); +				current_mailbox=0; +			} +			writes("+OK Folder closed\n"); +			continue; +		} + +		if (strcmp(p, "NOOP") == 0) +		{ +			smap1_noop(1); +			continue; +		} + +		if (strcmp(p, "IDLE") == 0) +		{ +			if ((p=getenv("IMAP_ENHANCEDIDLE")) == NULL +			    || !atoi(p) +			    || imapenhancedidle()) +				imapidle(); + +			read_timeout(60); +			smap_readline(buffer, sizeof(buffer)); +			ptr=buffer; +			p=getword(&ptr); +			up(p); +			if (strcmp(p, "RESUME")) +			{ +				writes("-ERR RESUME is required to follow IDLE\n"); +			} +			else +				writes("+OK Resumed...\n"); +			continue; +		} + +		if (!current_mailbox) +			p=""; /* FALLTHROUGH */ + +		if (strcmp(p, "EXPUNGE") == 0) +		{ +			int hasSet; + +			p=markmsgset(&ptr, &hasSet); + +			if (p) +			{ +				if (strchr(current_mailbox_acl, +					   ACL_EXPUNGE[0]) == NULL) +				{ +					accessdenied(ACL_EXPUNGE); +					continue; +				} + +				if (hasSet) +				{ +					if (strchr(current_mailbox_acl, +						   ACL_DELETEMSGS[0]) == NULL) +					{ +						accessdenied(ACL_DELETEMSGS); +						continue; +					} + +					applymsgset( &msg_expunge, NULL); +				} +				else +					expunge(); +				smap1_noop(0); +				continue; +			} +		} + +		if (strcmp(p, "STORE") == 0) +		{ +			struct storeinfo si; +			int dummy; + +			p=markmsgset(&ptr, &dummy); + +			dummy=0; + +			if (!p) +				dummy=1; + +			while (p && *p) +			{ +				char *q=strchr(p, '='); + +				if (q) +					*q=0; +				up(p); +				/* Uppercase only the keyword, for now */ +				if (q) +					*q='='; + +				if (strncmp(p, "FLAGS=", 6) == 0) +				{ +					memset(&si, 0, sizeof(si)); +					up(p); +					parseflags(p, &si.flags); +					if ((dummy=applymsgset(&applyflags, +							       &si)) != 0) +						break; +				} +				else if (strncmp(p, "+FLAGS=", 7) == 0 || +					 strncmp(p, "-FLAGS=", 7) == 0) +				{ +					memset(&si, 0, sizeof(si)); +					up(p); +					si.plusminus=p[0]; +					parseflags(p, &si.flags); +					if ((dummy=applymsgset(&applyflags, +							       &si)) != 0) +						break; +				} +				else if (strncmp(p, "KEYWORDS=", 9) == 0 && +					 keywords()) +				{ +					struct libmail_kwMessage *kwm; + +					memset(&si, 0, sizeof(si)); +					kwm=si.keywords=libmail_kwmCreate(); + +					if (!kwm) +						write_error_exit(0); + +					parsekeywords(p, &si.keywords); +					dummy=applymsgset(&applyflags, +							  &si); + +					libmail_kwmDestroy(kwm); + +					if (dummy != 0) +						break; +				} +				else if ((strncmp(p, "+KEYWORDS=", 10) == 0 || +					  strncmp(p, "-KEYWORDS=", 10) == 0) && +					 keywords()) +				{ +					memset(&si, 0, sizeof(si)); +					si.keywords=libmail_kwmCreate(); + +					if (!si.keywords) +						write_error_exit(0); +					si.plusminus=p[0]; +					parsekeywords(p, &si.keywords); +					dummy=applymsgset(&applyflags, +							  &si); + +					if (dummy == 0) +						dummy=addRemoveSmapKeywords(&si); +					libmail_kwmDestroy(si.keywords); + +					if (dummy != 0) +						break; +				} +				else if (strncmp(p, "INTERNALDATE=", 13) == 0) +				{ +					time_t t; + +					up(p); + +					t=rfc822_parsedt(p+13); + +					if (t && +					    (dummy=applymsgset(&setdate, &t)) +					    != 0) +						break; +				} + +				p=getword(&ptr); +			} +			if (dummy) +			{ +				writes("-ERR Cannot update folder status: "); +				writes(strerror(errno)); +				writes("\n"); +			} +			else +				writes("+OK Folder status updated\n"); +			continue; +		} + +		if (strcmp(p, "FETCH") == 0) +		{ +			int dummy; +			struct smapfetchinfo fi; +			int fetch_items=0; + +			for (p=markmsgset(&ptr, &dummy); +			     p && *p; p=getword(&ptr)) +			{ +				if ((fi.entity=strchr(p, '=')) == NULL) +				{ +					up(p); + +					if (strcmp(p, "UID") == 0) +						fetch_items |= FETCH_UID; +					if (strcmp(p, "SIZE") == 0) +						fetch_items |= FETCH_SIZE; +					if (strcmp(p, "FLAGS") == 0) +						fetch_items |= FETCH_FLAGS; +					if (strcmp(p, "KEYWORDS") == 0) +						fetch_items |= FETCH_KEYWORDS; +					if (strcmp(p, "INTERNALDATE") == 0) +						fetch_items +							|= FETCH_INTERNALDATE; +					continue; +				} + +				*fi.entity++=0; + +				fi.hdrs=strrchr(fi.entity, '('); +				if (fi.hdrs) +				{ +					char *q; + +					*fi.hdrs++=0; + +					q=strrchr(fi.hdrs, ')'); +					if (q) +						*q=0; +					up(fi.hdrs); +				} + +				fi.mimeid=strrchr(fi.entity, '['); +				if (fi.mimeid) +				{ +					char *q; + +					*fi.mimeid++=0; +					q=strrchr(fi.mimeid, ']'); +					if (q) +						*q=0; +				} + +				up(p); + +				if (strcmp(p, "CONTENTS") == 0 || +				    strcmp(p, "CONTENTS.PEEK") == 0) +				{ +					fi.peek=strchr(p, '.') != NULL; +					if (applymsgset(&do_fetch, &fi) == 0) +					{ +						continue; +					} +				} +				else +				{ +					continue; +				} + +				writes("-ERR Cannot retrieve message: "); +				writes(strerror(errno)); +				writes("\n"); +				break; +			} + +			if (!p || !*p) +			{ +				if (fetch_items && +				    applymsgset(&do_attrfetch, &fetch_items)) +				{ +					writes("-ERR Cannot retrieve message: "); +					writes(strerror(errno)); +					writes("\n"); +				} +				else +					writes("+OK Message retrieved.\n"); +			} +			continue; +		} + +		if (strcmp(p, "COPY") == 0 +		    || strcmp(p, "MOVE") == 0) +		{ +			int dummy; +			int domove= *p == 'M'; + +			p=markmsgset(&ptr, &dummy); + +			if (dummy && *p == 0) +			{ +				p=GETFOLDER(ACL_INSERT +					    ACL_DELETEMSGS +					    ACL_SEEN +					    ACL_WRITE); + +				if (p) +				{ +					if (strchr(rights_buf, ACL_INSERT[0]) +					    == NULL) +					{ +						free(p); +						accessdenied(ACL_INSERT); +						continue; +					} + +					if (copyto(p, domove, rights_buf) == 0) +					{ +						free(p); +						writes("+OK Messages copied.\n" +						       ); +						continue; +					} +					free(p); +				} + +				writes("-ERR Cannot copy messages: "); +				writes(strerror(errno)); +				writes("\n"); +				continue; +			} +			writes("-ERR Syntax error.\n"); +			continue; +		} + +		if (strcmp(p, "SEARCH") == 0) +		{ +			struct searchinfo *searchInfo=NULL; +			struct searchinfo *si; +			struct smap1_search_results searchResults; + +			if ((si=createSearch(&searchInfo, &ptr)) == NULL) +			{ +				writes("-ERR SEARCH failed: "); +				writes(strerror(errno)); +				writes("\n"); +				free_search(searchInfo); +				continue; +			} + +			searchResults.prev_runs=0; +			searchResults.prev_search_hit=0; +			searchResults.prev_search_hit_start=0; + +			search_internal(si, searchInfo, "utf-8", 0, +					smap1_search_cb, &searchResults); + +			if (searchResults.prev_search_hit) +				smap1_search_cb_range(&searchResults); + +			if (searchResults.prev_runs) +				writes("\n"); + +			writes("+OK Search completed.\n"); +			free_search(searchInfo); +			continue; +		} + + +		writes("-ERR Syntax error.\n"); +	} + +	writes("* BYE Courier-SMAP server shutting down\n" +	       "+OK LOGOUT completed\n"); +	writeflush(); +} | 
