diff options
Diffstat (limited to 'maildir/maildiraclt.c')
| -rw-r--r-- | maildir/maildiraclt.c | 1290 | 
1 files changed, 1290 insertions, 0 deletions
| diff --git a/maildir/maildiraclt.c b/maildir/maildiraclt.c new file mode 100644 index 0000000..d4b0966 --- /dev/null +++ b/maildir/maildiraclt.c @@ -0,0 +1,1290 @@ +/* +** Copyright 2003-2012 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"maildiraclt.h" +#include	"maildirmisc.h" +#include	"maildircreate.h" +#include	<time.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif +#if HAVE_DIRENT_H +#include <dirent.h> +#define NAMLEN(dirent) strlen((dirent)->d_name) +#else +#define dirent direct +#define NAMLEN(dirent) (dirent)->d_namlen +#if HAVE_SYS_NDIR_H +#include <sys/ndir.h> +#endif +#if HAVE_SYS_DIR_H +#include <sys/dir.h> +#endif +#if HAVE_NDIR_H +#include <ndir.h> +#endif +#endif +#include	<string.h> +#include	<errno.h> +#include	<stdio.h> +#include	<stdlib.h> +#include	<assert.h> + + +int maildir_acl_disabled=0; + +static int compar_aclt(const void *a, const void *b) +{ +	char ca=*(const char *)a; +	char cb=*(const char *)b; + +	return (int)(unsigned char)ca - (int)(unsigned char)cb; +} + +/* Post-op fixup of an aclt: collate, remove dupes. */ + +static void fixup(maildir_aclt *aclt) +{ +	char *a, *b; + +	qsort(*aclt, strlen(*aclt), 1, compar_aclt); + +	for (a=b=*aclt; *a; a++) +	{ +		if (*a == a[1]) +			continue; +		if ((int)(unsigned char)*a <= ' ') +			continue; /* Silently drop bad access rights */ + +		*b++= *a; +	} +	*b=0; +} + +static int validacl(const char *p) +{ +	while (*p) +	{ +		if ((int)(unsigned char)*p <= ' ') +		{ +			errno=EINVAL; +			return -1; +		} +		++p; +	} + +	return 0; +} + +int maildir_aclt_init(maildir_aclt *aclt, +		      const char *initvalue_cstr, +		      const maildir_aclt *initvalue_cpy) +{ +	if (initvalue_cpy) +		initvalue_cstr= *initvalue_cpy; + +	*aclt=NULL; + +	if (!initvalue_cstr || !*initvalue_cstr) +		return 0; + +	if (validacl(initvalue_cstr) < 0) +		return -1; + +	if ( (*aclt=strdup(initvalue_cstr)) == NULL) +		return -1; +	fixup(aclt); +	return 0; +} + +/* Destroy an aclt after it is no longer used. */ + +void maildir_aclt_destroy(maildir_aclt *aclt) +{ +	if (*aclt) +		free(*aclt); +} + + +/* Add or remove access chars. */ + +int maildir_aclt_add(maildir_aclt *aclt, +		     const char *add_strs, +		     const maildir_aclt *add_aclt) +{ +	if (add_aclt) +		add_strs= *add_aclt; + +	if (!add_strs || !*add_strs) +		return 0; + +	if (validacl(add_strs) < 0) +		return -1; + +	if (*aclt) +	{ +		char *p=realloc(*aclt, strlen(*aclt)+strlen(add_strs)+1); + +		if (!p) +			return -1; +		strcat(p, add_strs); +		*aclt=p; + +	} +	else if ( ((*aclt)=strdup(add_strs)) == NULL) +		return -1; + +	fixup(aclt); +	return 0; +} + +int maildir_aclt_del(maildir_aclt *aclt, +		     const char *del_strs, +		     const maildir_aclt *del_aclt) +{ +	char *a, *b; + +	if (del_aclt) +		del_strs= *del_aclt; + +	if (!del_strs) +		return 0; + +	if (!*aclt) +		return 0; + +	for (a=b=*aclt; *a; a++) +	{ +		if (strchr(del_strs, *a)) +			continue; +		*b++= *a; +	} +	*b=0; + +	if (**aclt == 0) +	{ +		free(*aclt); +		*aclt=NULL; +	} +	return 0; +} + +/* -------------------------------------------------------------------- */ + + +void maildir_aclt_list_init(maildir_aclt_list *aclt_list) +{ +	aclt_list->head=NULL; +	aclt_list->tail=NULL; +} + +void maildir_aclt_list_destroy(maildir_aclt_list *aclt_list) +{ +	struct maildir_aclt_node *p; + +	for (p=aclt_list->head; p; ) +	{ +		struct maildir_aclt_node *q=p->next; + +		free(p->identifier); +		maildir_aclt_destroy(&p->acl); +		free(p); +		p=q; +	} +	maildir_aclt_list_init(aclt_list); +} + + +/* Add an <identifier,acl> pair.  Returns 0 on success, -1 on failure */ + +int maildir_aclt_list_add(maildir_aclt_list *aclt_list, +			  const char *identifier, +			  const char *aclt_str, +			  maildir_aclt *aclt_cpy) +{ +	struct maildir_aclt_node *p; +	const char *q; + +	/* Check for valid identifiers */ + +	for (q=identifier; *q; q++) +		if ( (int)(unsigned char)*q <= ' ') +		{ +			errno=EINVAL; +			return -1; +		} + +	if (*identifier == 0) +	{ +		errno=EINVAL; +		return -1; +	} + +	if (aclt_cpy && *aclt_cpy) +		aclt_str= *aclt_cpy; + +	for (p=aclt_list->head; p; p=p->next) +	{ +		if (strcmp(p->identifier, identifier) == 0) +		{ +			maildir_aclt_destroy(&p->acl); +			return maildir_aclt_init(&p->acl, aclt_str, NULL); +		} +	} + +	if ((p=malloc(sizeof(*p))) == NULL || +	    (p->identifier=strdup(identifier)) == NULL) +	{ +		if (p) free(p); +		return -1; +	} + +	if (maildir_aclt_init(&p->acl, aclt_str, NULL) < 0) +	{ +		free(p->identifier); +		free(p); +		return -1; +	} + +	p->next=NULL; +	if ((p->prev=aclt_list->tail) != NULL) +		p->prev->next=p; +	else +		aclt_list->head=p; +	aclt_list->tail=p; +	return 0; +} + +/* +** Remove 'identifier' from the ACL list. +*/ + +int maildir_aclt_list_del(maildir_aclt_list *aclt_list, +			  const char *identifier) +{ +	struct maildir_aclt_node *p; + +	for (p=aclt_list->head; p; p=p->next) +	{ +		if (strcmp(p->identifier, identifier) == 0) +		{ +			if (p->prev) +				p->prev->next=p->next; +			else aclt_list->head=p->next; + +			if (p->next) +				p->next->prev=p->prev; +			else aclt_list->tail=p->prev; + +			maildir_aclt_destroy(&p->acl); +			free(p->identifier); +			free(p); +			return 0; +		} +	} +	return 0; +} + +/* +** Generic enumeration. +*/ + +int maildir_aclt_list_enum(maildir_aclt_list *aclt_list, +			   int (*cb_func)(const char *identifier, +					  const maildir_aclt *acl, +					  void *cb_arg), +			   void *cb_arg) +{ +	struct maildir_aclt_node *p; +	int rc; + +	for (p=aclt_list->head; p; p=p->next) +	{ +		rc= (*cb_func)(p->identifier, &p->acl, cb_arg); + +		if (rc) +			return rc; +	} +	return 0; +} + +const maildir_aclt *maildir_aclt_list_find(maildir_aclt_list *aclt_list, +					   const char *identifier) +{ +	struct maildir_aclt_node *p; + +	for (p=aclt_list->head; p; p=p->next) +	{ +		if (strcmp(p->identifier, identifier) == 0) +			return &p->acl; +	} +	return NULL; +} + +/* ---------------------------------------------------------------------- */ + +static int maildir_acl_read_check(maildir_aclt_list *aclt_list, +				 const char *maildir, +				 const char *path); + +int maildir_acl_read(maildir_aclt_list *aclt_list, +		     const char *maildir, +		     const char *path) +{ +	int rc=maildir_acl_read_check(aclt_list, maildir, path); +	char *p, *q; + +	if (rc) +		maildir_aclt_list_destroy(aclt_list); + +	if (rc <= 0) +		return rc; + +	/* +	** If the ACL config file for this folder was not found, +	** check for the ACL config file for its parent folder. +	*/ + +	if ((p=strdup(path)) == NULL) +		return -1; + +	strcpy(p, path); + +	q=strrchr(p, '.'); + +	if (!q) +	{ +		free(p); +		errno=EIO; /* Should not happen */ +		return -1; +	} + +	*q=0; + +	rc=maildir_acl_read(aclt_list, maildir, p); +	if (rc == 0) +	{ +		/* Make sure to save the default acl list */ + +		rc=maildir_acl_write(aclt_list, maildir, path, NULL, NULL); +		if (rc >= 0) /* Ok if rc=1 */ +			rc=0; +		if (rc) +			maildir_aclt_list_destroy(aclt_list); +	} +	free(p); +	return rc; +} + +/* +** Attempt to retrieve the ACL set for the specified folder. +** +** Returns -1 if error. +** Returns 0 if the ACL was retrieved. +** Returns 1 if the ACL configuration file does not exist. +*/ + +static int maildir_aclt_add_default_admin(maildir_aclt_list *aclt_list); + +static int maildir_acl_read_check(maildir_aclt_list *aclt_list, +				  const char *maildir, +				  const char *path) +{ +	char *p, *q; +	FILE *fp; +	char buffer[BUFSIZ]; + +	maildir_aclt_list_init(aclt_list); + +	if (!maildir || !*maildir) +		maildir="."; +	if (!path || !*path) +		path="."; + +	if (strchr(path, '/') || *path != '.') +	{ +		errno=EINVAL; +		return -1; +	} + +	if (maildir_acl_disabled) +	{ +		if (maildir_aclt_list_add(aclt_list, "owner", +					  ACL_LOOKUP ACL_READ +					  ACL_SEEN ACL_WRITE ACL_INSERT +					  ACL_CREATE +					  ACL_DELETEFOLDER +					  ACL_DELETEMSGS ACL_EXPUNGE, +					  NULL) < 0 || +		    maildir_aclt_add_default_admin(aclt_list)) +		{ +			maildir_aclt_list_destroy(aclt_list); +			return -1; +		} +		return 0; +	} + +	p=malloc(strlen(maildir)+strlen(path)+2); + +	if (!p) +		return -1; + +	strcat(strcat(strcpy(p, maildir), "/"), path); + +	q=malloc(strlen(p)+sizeof("/" ACLFILE)); +	if (!q) +	{ +		free(p); +		return -1; +	} +	fp=fopen(strcat(strcpy(q, p), "/" ACLFILE), "r"); +	free(p); +	free(q); + +	if (fp == NULL) +	{ +		if (strcmp(path, ".") == 0) +		{ +			/* INBOX ACL default */ + +			if (maildir_aclt_list_add(aclt_list, "owner", +						  ACL_ALL, NULL) < 0 || +			    maildir_aclt_add_default_admin(aclt_list)) +			{ +				return -1; +			} +			return 0; +		} + +		q=malloc(strlen(maildir)+sizeof("/" ACLHIERDIR "/") + +			 strlen(path)); +		if (!q) +			return -1; + +		strcat(strcat(strcpy(q, maildir), "/" ACLHIERDIR "/"), +		       path+1); + +		fp=fopen(q, "r"); +		free(q); +	} + +	if (!fp && errno != ENOENT) +		return -1; + +	if (!fp) +		return 1; + +	errno=0; + +	while (fgets(buffer, sizeof(buffer), fp) != NULL) +	{ +		char *p=strchr(buffer, '\n'); + +		if (p) *p=0; + +		for (p=buffer; *p; p++) +			if (*p == ' ') +			{ +				*p=0; +				do +				{ +					++p; +				} while (*p && *p == ' '); +				break; +			} + +		if (maildir_aclt_list_add(aclt_list, buffer, p, NULL) < 0) +		{ +			if (errno != EINVAL) +				return -1; +			/* Sweep crap in the ACL file under the carpet */ +		} +	} +	if (ferror(fp)) +	{ +		fclose(fp); +		return -1; +	} +	fclose(fp); +	if (maildir_aclt_add_default_admin(aclt_list)) +		return -1; +	return 0; +} + +/* +** Add the default ACL permissions to the administrators group. +** +** Make sure that the ACL entry for "administrators" includes all +** rights. +** +** Make sure that any ACL entries for "-administrators" or +** "-group=administrators" do not have LOOKUP and ADMIN. +*/ + +static int maildir_aclt_add_default_admin(maildir_aclt_list *aclt_list) +{ +	const maildir_aclt *old_acl; + +	static const char * const drop_acls[]={"-administrators", +					       "-group=administrators"}; +	size_t i; + +	if ((old_acl=maildir_aclt_list_find(aclt_list, "group=administrators")) +	    != NULL) +	{ +		maildir_aclt new_acl; + +		if (maildir_aclt_init(&new_acl, ACL_ALL, NULL)) +			return -1; + +		if (maildir_aclt_add(&new_acl, NULL, old_acl) || +		    maildir_aclt_list_add(aclt_list, "group=administrators", +					  NULL, &new_acl)) +		{ +			maildir_aclt_destroy(&new_acl); +			return -1; +		} +		maildir_aclt_destroy(&new_acl); +	} +	else +	{ +		maildir_aclt new_acl; + +		old_acl=maildir_aclt_list_find(aclt_list, "administrators"); + +		if (maildir_aclt_init(&new_acl, ACL_ALL, NULL)) +			return -1; + +		if (maildir_aclt_add(&new_acl, NULL, old_acl) || +		    maildir_aclt_list_add(aclt_list, "administrators", +					  NULL, &new_acl)) +		{ +			maildir_aclt_destroy(&new_acl); +			return -1; +		} +		maildir_aclt_destroy(&new_acl); +	} + +	for (i=0; i<2; i++) +	{ +		const char *n=drop_acls[i]; +		if (maildir_aclt_list_del(aclt_list, n) < 0) +			return -1; +	} + +	return 0; +} + +int maildir_acl_delete(const char *maildir, +		       const char *path) +{ +	char *p, *q; + +#if 0 +	if (strcmp(path, SHARED) == 0) +		return 0; + +	if (strncmp(path, SHARED ".", sizeof(SHARED)) == 0) +		return 0; +#endif +	if (!maildir || !*maildir) +		maildir="."; +	if (!path || !*path) +		path="."; + +	if (strchr(path, '/') || *path != '.') +	{ +		errno=EINVAL; +		return -1; +	} + +	p=malloc(strlen(maildir)+strlen(path)+2); + +	if (!p) +		return -1; + +	strcat(strcat(strcpy(p, maildir), "/"), path); + +	q=malloc(strlen(p)+sizeof("/" ACLFILE)); +	if (!q) +	{ +		free(p); +		return -1; +	} + +	unlink(strcat(strcpy(q, p), "/" ACLFILE)); +	free(p); +	free(q); + +	if (strcmp(path, ".") == 0) +	{ +		/* INBOX ACL default */ + +		return 0; +	} + +	q=malloc(strlen(maildir)+sizeof("/" ACLHIERDIR "/") + +		 strlen(path)); +	if (!q) +	{ +		return -1; +	} +	strcat(strcat(strcpy(q, maildir), "/" ACLHIERDIR "/"), +	       path+1); + +	unlink(q); +	free(q); +	return 0; +} + +static int save_acl(const char *identifier, const maildir_aclt *acl, +		    void *cb_arg); + + +static int is_owner(const char *isme, void *void_arg) +{ +	if (void_arg && strcmp(isme, (const char *)void_arg) == 0) +		return 1; + +	return strcmp(isme, "owner") == 0; +} + +static int is_admin(const char *isme, void *void_arg) +{ +	return strcmp(isme, "administrators") == 0; + +	/* We don't need to check for group=administrators, see chk_admin() */ +} + +static int check_adminrights(maildir_aclt *list) +{ +	if (strchr(maildir_aclt_ascstr(list), ACL_LOOKUP[0]) == NULL || +	    strchr(maildir_aclt_ascstr(list), ACL_ADMINISTER[0]) == NULL) +	{ +		maildir_aclt_destroy(list); +		return -1; +	} + +	maildir_aclt_destroy(list); +	return 0; +} + +static int check_allrights(maildir_aclt *list) +{ +	const char *all=ACL_ALL; + +	while (*all) +	{ +		if (strchr(maildir_aclt_ascstr(list), *all) == NULL) +		{ +			maildir_aclt_destroy(list); +			return -1; +		} +		++all; +	} + +	maildir_aclt_destroy(list); +	return 0; +} + +static int maildir_acl_compute_chkowner(maildir_aclt *aclt, +					maildir_aclt_list *aclt_list, +					int (*cb_func)(const char *isme, +						       void *void_arg), +					void *void_arg, +					int chkowner); + +int maildir_acl_write(maildir_aclt_list *aclt_list, +		      const char *maildir, +		      const char *path, + +		      const char *owner, +		      const char **err_failedrights) +{ +	int trycreate; +	struct maildir_tmpcreate_info tci; +	FILE *fp; +	char *p, *q; +	const char *dummy_string; +	maildir_aclt chkacls; + +	if (!err_failedrights) +		err_failedrights= &dummy_string; + +	if (!maildir || !*maildir) +		maildir="."; +	if (!path || !*path) +		path="."; + +	if (strchr(path, '/') || *path != '.') +	{ +		errno=EINVAL; +		return -1; +	} + +	if (strcmp(path, ".")) /* Sanity check */ +		for (dummy_string=path; *dummy_string; dummy_string++) +			if (*dummy_string == '.' && +			    (dummy_string[1] == '.' || +			     dummy_string[1] == 0)) +			{ +				errno=EINVAL; +				return -1; +			} + + +	if (maildir_acl_compute_chkowner(&chkacls, aclt_list, is_owner, NULL, +					 0)) +	{ +		maildir_aclt_destroy(&chkacls); +		errno=EINVAL; +		return -1; +	} + +	if (check_adminrights(&chkacls)) +	{ +		*err_failedrights="owner"; +		errno=EINVAL; +		return -1; +	} + +	if (owner) +	{ +		if (maildir_acl_compute_chkowner(&chkacls, aclt_list, is_owner, +						 (void *)owner, 0)) +		{ +			maildir_aclt_destroy(&chkacls); +			errno=EINVAL; +			return -1; +		} +		if (check_adminrights(&chkacls)) +		{ +			*err_failedrights=owner; +			errno=EINVAL; +			return -1; +		} +	} + +	if (maildir_acl_compute(&chkacls, aclt_list, is_admin, NULL)) +	{ +		maildir_aclt_destroy(&chkacls); +		errno=EINVAL; +		return -1; +	} +	if (check_allrights(&chkacls)) +	{ +		errno=EINVAL; +		return -1; +	} + +	p=malloc(strlen(maildir)+strlen(path)+2); + +	if (!p) +		return -1; + +	strcat(strcat(strcpy(p, maildir), "/"), path); + +	maildir_tmpcreate_init(&tci); + +	tci.maildir=p; +	tci.uniq="acl"; +	tci.doordie=1; + +	fp=maildir_tmpcreate_fp(&tci); + +	trycreate=0; + +	if (fp) +	{ +		q=malloc(strlen(p) + sizeof("/" ACLFILE)); +		if (!q) +		{ +			fclose(fp); +			unlink(tci.tmpname); +			maildir_tmpcreate_free(&tci); +			free(p); +			return -1; +		} +		strcat(strcpy(q, p), "/" ACLFILE); +		free(tci.newname); +		tci.newname=q; +		free(p); +	} +	else +	{ +		free(p); + +		q=malloc(strlen(maildir)+sizeof("/" ACLHIERDIR "/") + +			 strlen(path)); +		if (!q) +		{ +			maildir_tmpcreate_free(&tci); +			return -1; +		} +		strcat(strcat(strcpy(q, maildir), "/" ACLHIERDIR "/"), path+1); + +		tci.maildir=maildir; +		tci.uniq="acl"; +		tci.doordie=1; + +		fp=maildir_tmpcreate_fp(&tci); + +		if (!fp) +		{ +			free(q); +			maildir_tmpcreate_free(&tci); +			return -1; +		} +		free(tci.newname); +		tci.newname=q; +		trycreate=1; +	} + +	if (maildir_aclt_list_enum(aclt_list, save_acl, fp) < 0 || +	    ferror(fp) || fflush(fp) < 0) +	{ +		fclose(fp); +		unlink(tci.tmpname); +		maildir_tmpcreate_free(&tci); +		return -1; +	} +	fclose(fp); + +	if (rename(tci.tmpname, tci.newname) < 0) +	{ +		/* Perhaps ACLHIERDIR needs to be created? */ + +		if (!trycreate) +		{ +			unlink(tci.tmpname); +			maildir_tmpcreate_free(&tci); +			return -1; +		} + +		p=strrchr(tci.newname, '/'); +		*p=0; +		mkdir(tci.newname, 0755); +		*p='/'; + +		if (rename(tci.tmpname, tci.newname) < 0) +		{ +			unlink(tci.tmpname); +			maildir_tmpcreate_free(&tci); +			return -1; +		} +	} +	maildir_tmpcreate_free(&tci); +	return 0; +} + +static int save_acl(const char *identifier, const maildir_aclt *acl, +		    void *cb_arg) +{ +	if (fprintf((FILE *)cb_arg, "%s %s\n", +		    identifier, +		    maildir_aclt_ascstr(acl)) < 0) +		return -1; +	return 0; +} + +struct maildir_acl_resetList { +	struct maildir_acl_resetList *next; +	char *mbox; +}; + +/* +** When a maildir is opened check for stale entries in Maildir/ACLHIERDIR. +** +** Maildir/ACLHIERDIR/folder.subfolder should be removed unless there exists +** Maildir/.folder.subfolder.subsubfolder +** +** +** acl_check_cb is the callback function for maildir_list, which receives +** INBOX.folder.subfolder.subsubfolder.  It goes through the link list with +** Maildir/ACLHIERDIR's contents, and removes folder.subfolder if its found. +** +** After maildir_list is done, anything that's left in the list can be safely +** removed. +*/ + +static void acl_check_cb(const char *mbox, void *voidarg) +{ +	struct maildir_acl_resetList **l= +		(struct maildir_acl_resetList **)voidarg; + +	if (strncmp(mbox, INBOX ".", sizeof(INBOX ".")-1)) +		return; /* Huh? */ + +	mbox += sizeof(INBOX ".")-1; + +	while (*l) +	{ +		int cl= strlen( (*l)->mbox ); + +		if (strncmp(mbox, (*l)->mbox, cl) == 0 && +		    mbox[cl] == '.') +		{ +			struct maildir_acl_resetList *p= *l; + +			*l= p->next; +			free(p->mbox); +			free(p); +			continue; +		} + +		l= &(*l)->next; +	} +} + +int maildir_acl_reset(const char *maildir) +{ +	DIR *dirp; +	struct dirent *de; +	char *p; +	struct maildir_acl_resetList *rl=NULL; +	struct maildir_acl_resetList *r; +	time_t now; +	struct stat stat_buf; + +	p=malloc(strlen(maildir) + sizeof("/" ACLHIERDIR)); +	if (!p) +		return -1; + +	strcat(strcpy(p, maildir), "/" ACLHIERDIR); + +	dirp=opendir(p); + +	if (!dirp) +	{ +		mkdir(p, 0755); +		dirp=opendir(p); +	} +	free(p); + +	while (dirp && (de=readdir(dirp)) != NULL) +	{ +		if (de->d_name[0] == '.') +			continue; + +		if ((r=malloc(sizeof(struct maildir_acl_resetList))) == NULL +		    || (r->mbox=strdup(de->d_name)) == NULL) +		{ +			if (r) +				free(r); + +			while (rl) +			{ +				r=rl; +				rl=r->next; +				free(r->mbox); +				free(r); +			} +			closedir(dirp); +			return -1; +		} + +		r->next=rl; +		rl=r; +	} +	if (dirp) closedir(dirp); + +	maildir_list(maildir, acl_check_cb, &rl); + +	time(&now); + +	while (rl) +	{ +		r=rl; +		rl=r->next; + +		p=malloc(strlen(maildir)+strlen(r->mbox) + +			 sizeof("/" ACLHIERDIR "/")); +		if (p) +		{ +			strcat(strcat(strcpy(p, maildir), +				      "/" ACLHIERDIR "/"), r->mbox); + +			/* Only unlink stale entries after 1 hour (race) */ + +			if (stat(p, &stat_buf) == 0 && +			    stat_buf.st_mtime < now - 60*60) +				unlink(p); +			free(p); +		} +		free(r->mbox); +		free(r); +	} +	return 0; +} + +/* +** An ACL entry for "administrators" or "group=administrators" will match +** either one. +*/ + +static int chk_admin(int (*cb_func)(const char *isme, +				    void *void_arg), +		     const char *identifier, +		     void *void_arg) +{ +	if (strcmp(identifier, "administrators") == 0 || +	    strcmp(identifier, "group=administrators") == 0) +	{ +		int rc=(*cb_func)("administrators", void_arg); + +		if (rc == 0) +			rc=(*cb_func)("group=administrators", void_arg); +		return rc; +	} + +	return (*cb_func)(identifier, void_arg); +} + +#define ISIDENT(s) \ +	(MAILDIR_ACL_ANYONE(s) ? 1: chk_admin(cb_func, (s), void_arg)) + +static int maildir_acl_compute_chkowner(maildir_aclt *aclt, +					maildir_aclt_list *aclt_list, +					int (*cb_func)(const char *isme, +						       void *void_arg), +					void *void_arg, +					int chkowner) +{ +	struct maildir_aclt_node *p; +	int rc; + +	if (maildir_aclt_init(aclt, "", NULL) < 0) +		return -1; + +	for (p=aclt_list->head; p; p=p->next) +	{ +		if (p->identifier[0] == '-') +			continue; + +		rc= ISIDENT(p->identifier); + +		if (rc < 0) +		{ +			maildir_aclt_destroy(aclt); +			return rc; +		} + +		if (rc == 0) +			continue; + +		if (maildir_aclt_add(aclt, NULL, &p->acl) < 0) +		{ +			maildir_aclt_destroy(aclt); +			return rc; +		} +	} + +	for (p=aclt_list->head; p; p=p->next) +	{ +		if (p->identifier[0] != '-') +			continue; + +		rc= ISIDENT(p->identifier+1); + +		if (rc < 0) +		{ +			maildir_aclt_destroy(aclt); +			return rc; +		} + +		if (rc == 0) +			continue; + +		if (maildir_aclt_del(aclt, NULL, &p->acl) < 0) +		{ +			maildir_aclt_destroy(aclt); +			return rc; +		} +	} + +	/* +	** In our scheme, the owner always gets admin rights. +	*/ + +	rc=chkowner ? (*cb_func)("owner", void_arg):0; + +	if (maildir_acl_disabled) +		rc=0;	/* Except when ACLs are disabled */ + +	if (rc < 0) +	{ +		maildir_aclt_destroy(aclt); +		return rc; +	} + +	if (rc) +	{ +		if (maildir_aclt_add(aclt, ACL_ADMINISTER, NULL) < 0) +		{ +			maildir_aclt_destroy(aclt); +			return rc; +		} +	} +	return 0; +} + +int maildir_acl_compute(maildir_aclt *aclt, maildir_aclt_list *aclt_list, +			int (*cb_func)(const char *isme, +				       void *void_arg), void *void_arg) +{ +	return maildir_acl_compute_chkowner(aclt, aclt_list, cb_func, void_arg, +					    1); +} + +static int chk_array(const char *identifier, void *void_arg); + +int maildir_acl_compute_array(maildir_aclt *aclt, +			      maildir_aclt_list *aclt_list, +			      const char * const *identifiers) +{ +	return maildir_acl_compute(aclt, aclt_list, chk_array, +				   (void *)identifiers); +} + +static int chk_array(const char *identifier, void *void_arg) +{ +	const char * const *p=(const char * const *)void_arg; +	size_t i; + +	for (i=0; p[i]; i++) +		if (strcmp(identifier, p[i]) == 0) +			return 1; +	return 0; +} + +/* -------------------------------------------------------------------- */ + +int maildir_acl_canlistrights(const char *myrights) +{ +	return (strchr(myrights, ACL_LOOKUP[0]) || +		strchr(myrights, ACL_READ[0]) || +		strchr(myrights, ACL_INSERT[0]) || +		strchr(myrights, ACL_CREATE[0]) || +		strchr(myrights, ACL_DELETEFOLDER[0]) || +		strchr(myrights, ACL_EXPUNGE[0]) || +		strchr(myrights, ACL_ADMINISTER[0])); +} + +/* --------------------------------------------------------------------- */ + +/* +** Compute owner ACL identifiers applicable to a mailbox that's owned by +** 'mailbox_owner'. +*/ + +static int get_owner_list( int (*cb_func)(const char *, void *), +			   const char *c, +			   const char *mailbox_owner, void *arg) +{ +	char *a; +	int rc; +	const char *p, *q; + +	a=malloc(sizeof("user=")+strlen(c)); +	if (!a) +		return -1; + +	strcat(strcpy(a, "user="), c); + +	rc=(*cb_func)(a, arg); + +	if (rc == 0 && strcmp(a, mailbox_owner) == 0) +		rc=(*cb_func)("owner", arg); +	free(a); + +	if (rc) +		return rc; + +	c=getenv("OPTIONS"); + +	for (p=c; p && *p; ) +	{ +		if (*p == ',') +		{ +			++p; +			continue; +		} + +		q=p; +		while (*p && *p != ',') +			++p; + +		if (strncmp(q, "group=", 6) == 0) +		{ +			a=malloc(p-q+1); +			if (!a) +				return -1; + +			memcpy(a, q, p-q); +			a[p-q]=0; +			rc=(*cb_func)(a, arg); +			free(a); +			if (rc) +				return rc; +		} +	} +	return 0; +} + +static int count_owner_list(const char *o, void *arg) +{ +	++*(size_t *)arg; + +	return 0; +} + +static int save_owner_list(const char *o, void *arg) +{ +	char ***p=(char ***)arg; + +	**p=strdup(o); + +	if (**p == NULL) +		return -1; + +	++*p; +	return 0; +} + +int maildir_acl_computerights(maildir_aclt *rights, +			      maildir_aclt_list *acl_list, +			      const char *me, +			      const char *folder_owner) +{ +	char **owner_array; +	size_t owner_cnt; +	char **p; +	int rc; + +	owner_cnt=1; + +	if (get_owner_list(count_owner_list, me, folder_owner, +			   (void *)&owner_cnt) || +	    (owner_array=calloc(owner_cnt, sizeof(char *))) == NULL) +		return -1; + +	p=owner_array; + +	if (get_owner_list(save_owner_list, me, folder_owner, (void *)&p)) +	{ +		for (owner_cnt=0; owner_array[owner_cnt]; ++owner_cnt) +			free(owner_array[owner_cnt]); +		free(owner_array); +		return -1; +	} + +	rc=maildir_acl_compute_array(rights, acl_list, +				     (const char * const *)owner_array); + +	for (owner_cnt=0; owner_array[owner_cnt]; ++owner_cnt) +		free(owner_array[owner_cnt]); +	free(owner_array); +	return rc; +} | 
