diff options
Diffstat (limited to 'maildir/maildirinfo.c')
| -rw-r--r-- | maildir/maildirinfo.c | 938 | 
1 files changed, 938 insertions, 0 deletions
| diff --git a/maildir/maildirinfo.c b/maildir/maildirinfo.c new file mode 100644 index 0000000..cb2d530 --- /dev/null +++ b/maildir/maildirinfo.c @@ -0,0 +1,938 @@ +/* +** Copyright 2003 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	<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 + +#include	<sys/types.h> +#include	<sys/stat.h> + +#include	"config.h" +#include	"maildirinfo.h" + +#include	"maildirmisc.h" +#include	"maildirnewshared.h" +#include	"unicode/unicode.h" + +void maildir_info_destroy(struct maildir_info *info) +{ +	if (info->homedir) +		free(info->homedir); +	if (info->maildir) +		free(info->maildir); +	if (info->owner) +		free(info->owner); +	info->homedir=NULL; +	info->maildir=NULL; +	info->owner=NULL; +} + +struct imap_find_shared { +	struct maildir_info *info; +	const char *path; +	size_t path_l; +	char *homedir; +	char *maildir; +}; + +static int imap_find_cb(struct maildir_newshared_enum_cb *cb) +{ +	struct imap_find_shared *ifs=(struct imap_find_shared *)cb->cb_arg; + +	if (cb->homedir) +	{ +		ifs->homedir=strdup(cb->homedir); +		if (!ifs->homedir) +			return -1; +	} + +	if (cb->maildir) +	{ +		ifs->maildir=strdup(cb->maildir); + +		if (!ifs->maildir) +		{ +			if (cb->homedir) +			{ +				free(ifs->homedir); +				ifs->homedir=NULL; +			} +			return -1; +		} +	} + +	return 0; +} + +int maildir_info_imap_find(struct maildir_info *info, const char *path, +			   const char *myId) +{ +	const char *p; +	struct imap_find_shared ifs; +	const char *indexfile; +	char *indexfile_cpy; +	struct maildir_shindex_cache *curcache; +	const char *subhierarchy; + +	info->homedir=NULL; +	info->maildir=NULL; +	info->owner=NULL; + +	if (strchr(path, '/')) +	{ +		errno=EINVAL; +		return -1; +	} + +	for (p=path; *p; p++) +		if (*p == '.' && p[1] == '.') +		{ +			errno=EINVAL; +			return -1; +		} + +	if (strncmp(path, SHARED, sizeof(SHARED)-1) == 0) +	{ +		path += sizeof(SHARED)-1; + +		info->homedir=strdup("."); +		if (!info->homedir) +			return -1; + +		info->mailbox_type=MAILBOXTYPE_OLDSHARED; +		info->owner=strdup("anonymous"); + +		if (!info->owner) +		{ +			maildir_info_destroy(info); +			return -1; +		} + +		/* We need to specialcase "shared" and "shared.name". +		** maildir_shareddir will return NULL for these cases, because +		** it will insist on "name.folder", but we need to return a +		** non NULL value to indicate that this is a valid hierarchy +		** name.  We return a special value of an empty string, which +		** is checked for in situations where a valid folder is +		** required. +		*/ + +		if (*path && *path != '.') +		{ +			maildir_info_destroy(info); +			errno=EINVAL; +			return -1; +		} + +		return 0; +	} + +	if (strncasecmp(path, INBOX, sizeof(INBOX)-1) == 0) +	{ +		switch (path[sizeof(INBOX)-1]) { +		case 0: +		case '.': +			break; +		default: +			errno=EINVAL; +			return -1; +		} + +		info->homedir=strdup("."); + +		if (!info->homedir) +			return -1; + +		info->maildir=strdup(path); +		if (!info->maildir) +		{ +			maildir_info_destroy(info); +			return -1; +		} + +		info->owner=malloc(strlen(myId)+sizeof("user=")); + +		if (!info->owner) +		{ +			maildir_info_destroy(info); +			return -1; +		} + +		info->mailbox_type=MAILBOXTYPE_INBOX; +		strcat(strcpy(info->owner, "user="), myId); +		return 0; +	} + +	if (strncmp(path, NEWSHARED, +		    sizeof(NEWSHARED)-1) != 0) +	{ +		errno=EINVAL; +		return -1; +	} + +	ifs.info=info; +	ifs.path=path+sizeof(NEWSHARED)-1; + +	info->mailbox_type=MAILBOXTYPE_NEWSHARED; +	info->homedir=NULL; +	info->maildir=NULL; +	info->owner=strdup("vendor=courier.internal"); + +	if (!info->owner) +		return -1; + +	ifs.homedir=NULL; +	ifs.maildir=NULL; + +	indexfile=NULL; +	indexfile_cpy=NULL; +	curcache=NULL; +	subhierarchy=NULL; + +	while (*ifs.path) +	{ +		int rc, eof; +		size_t i; + +		curcache=maildir_shared_cache_read(curcache, indexfile, +						   subhierarchy); + +		if (indexfile_cpy) +		{ +			free(indexfile_cpy); +			indexfile_cpy=NULL; +		} + +		if (!curcache) +			break; + +		p=strchr(ifs.path, '.'); + +		if (p) +			ifs.path_l=p-ifs.path; +		else +			ifs.path_l=strlen(ifs.path); + + +		if (ifs.homedir) +			free(ifs.homedir); +		if (ifs.maildir) +			free(ifs.maildir); + +		ifs.homedir=NULL; +		ifs.maildir=NULL; + +		for (i=0; i < curcache->nrecords; i++) +		{ +			char *n=maildir_info_imapmunge(curcache-> +						       records[i].name); + +			if (n == NULL) +			{ +				i=curcache->nrecords; +				break; +			} + +			if (strlen(n) == ifs.path_l && +			    strncmp(n, ifs.path, ifs.path_l) == 0) +			{ +				free(n); +				break; +			} +			free(n); +		} + +		if (i >= curcache->nrecords) +			break; + +		curcache->indexfile.startingpos= +			curcache->records[i].offset; +		rc=maildir_newshared_nextAt(&curcache->indexfile, +					    &eof, +					    imap_find_cb, &ifs); + +		if (rc || eof) +		{ +			fprintf(stderr, "ERR: Internal error -" +				" maildir_newshared_nextAt: %s\n", +				strerror(errno)); +			fflush(stderr); +			break; +		} + +		if (!ifs.homedir && !ifs.maildir) +			break; + +		if (!ifs.homedir) +		{ +			indexfile=indexfile_cpy=ifs.maildir; +			ifs.maildir=NULL; +			subhierarchy=curcache->records[i].name; + +			ifs.path += ifs.path_l; +			if (*ifs.path) +				++ifs.path; +			continue; +		} + +		info->homedir=maildir_location(ifs.homedir, +					     ifs.maildir); + +		free(ifs.homedir); +		free(ifs.maildir); + +		free(info->owner); + +		if (!info->homedir) +		{ +			info->maildir=NULL; +			info->owner=NULL; +			return -1; +		} + +		if (!subhierarchy || !*subhierarchy) +		{ +			info->owner=strdup("vendor=courier.internal"); +			if (!info->owner) +			{ +				free(info->homedir); +				info->homedir=NULL; +				info->maildir=NULL; +				return -1; +			} +		} +		else +		{ +			char *owner_utf8; + +			info->owner=malloc(strlen(subhierarchy) +					   +sizeof("user=")); + +			if (!info->owner) +			{ +				free(info->homedir); +				info->homedir=NULL; +				info->maildir=NULL; +				info->owner=NULL; +				return -1; +			} +			strcpy(info->owner, "user="); +			strcat(info->owner, subhierarchy); + +			/* +			** The folder path is in modified-UTF7.  The owner is +			** obtained from shared hierarchy, but in ACL2 the +			** identifiers are in UTF8. +			*/ + +			owner_utf8= +				libmail_u_convert_tobuf(info->owner, +							unicode_x_imap_modutf7, +							"utf-8", NULL); + +			if (!owner_utf8) +			{ +				free(info->homedir); +				info->homedir=NULL; +				return (0); +			} + +			free(info->owner); +			info->owner=owner_utf8; +		} + +		ifs.path += ifs.path_l; + +		info->maildir=malloc(strlen(INBOX)+1+strlen(ifs.path)); +		if (!info->maildir) +		{ +			free(info->owner); +			free(info->homedir); +			info->owner=NULL; +			info->homedir=NULL; +			return -1; +		} +		strcat(strcpy(info->maildir, INBOX), ifs.path); + +		if (maildir_info_suppress(info->homedir)) +		{ + +			free(info->homedir); +			free(info->maildir); +			info->homedir=NULL; +			info->maildir=NULL; +			info->mailbox_type=MAILBOXTYPE_IGNORE; +			free(info->owner); +			info->owner=NULL; +			info->owner=strdup("vendor=courier.internal"); +			if (!info->owner) +			{ +				return -1; +			} +		} + +		return 0; +	} + +	if (indexfile_cpy) +		free(indexfile_cpy); +	if (ifs.homedir) +	{ +		free(ifs.homedir); +		ifs.homedir=NULL; +	} + +	if (ifs.maildir) +	{ +		free(ifs.maildir); +		ifs.maildir=NULL; +	} +	return 0; +} + +/***************************************************************************/ + +/* +** Maildir folders are named in IMAP-compatible modified-UTF7 encoding, +** with periods as hierarchy delimiters.  One exception: ".", "/", "~", and +** ":" are also encoded using modified-UTF7, making folder names that contain +** those characters incompatible with IMAP. +** +** smaptoUtf7 crates a modified-UTF7-encoded folder name from a vector +** of UTF-8 words. +** +** input:  "INBOX" "a" "b" +** output: "INBOX.a.b" +** +*/ + +static char *smaptoUtf7(char **ptr) +{ +	char *f=NULL; +	char *n; + +	while ((n=*ptr++) != NULL && *n) +	{ +		char *p=libmail_u_convert_tobuf(n, "utf-8", +						unicode_x_imap_modutf7 " ./~:", +						NULL); + +		if (!p) +		{ +			if (f) +				free(f); +			return NULL; +		} + +		n= f ? realloc(f, strlen(f)+strlen(p)+2):malloc(strlen(p)+1); + +		if (!n) +		{ +			free(p); +			if (f) +				free(f); +			return NULL; +		} +		if (f) +			f=strcat(strcat(n, "."), p); +		else +			f=strcpy(n, p); +		free(p); +	} + +	if (!f) +		errno=EINVAL; +	return f; +} + +/* +** Legacy IMAP creates maildir folders using modified-UTF7. +** +** Convert modified-UTF7 folder name into an array of UTF-8 words, that +** represent a folder name. +*/ + +char **maildir_smapfn_fromutf7(const char *modutf7) +{ +	char *p=strdup(modutf7), *q; +	size_t n, i; +	char **fn; + +	if (!p) +		return NULL; + +	n=1; +	for (i=0; p[i]; i++) +		if (p[i] == '.' && p[i+1] && p[i+1] != '.') +		{ +			++n; +		} + +	fn=malloc((n+1)*sizeof(char *)); + +	if (!fn) +	{ +		free(p); +		return NULL; +	} + +	n=0; +	q=p; +	do +	{ +		for (i=0; q[i]; i++) +			if (q[i] == '.' && q[i+1] && q[i+1] != '.') +			{ +				q[i++]=0; +				break; +			} + +		fn[n]=libmail_u_convert_tobuf(q, +					      unicode_x_imap_modutf7 " ./~:", +					      "utf-8", NULL); +		q += i; + +		if (!fn[n]) +		{ +			while (n) +				free(fn[--n]); +			free(fn); +			free(p); +			return NULL; +		} +		n++; +	} while (*q); +	fn[n]=0; +	free(p); +	return fn; +} + +void maildir_smapfn_free(char **fn) +{ +	size_t i; + +	for (i=0; fn[i]; i++) +		free(fn[i]); +	free(fn); +} + +struct get_existing_folder_info { +	char **fn; +	char *pathname; +}; + +static void get_existing_callback(const char *f, void *vp) +{ +	char **fn; + +	struct get_existing_folder_info *gefi= +		(struct get_existing_folder_info *)vp; +	size_t i; +	size_t j; + +	if (gefi->pathname) +		return; + +	fn=maildir_smapfn_fromutf7(f); +	if (!fn) +	{ +		perror(f); +		return; +	} + +	for (i=0; gefi->fn[i]; i++) +		if (fn[i] == NULL || strcmp(fn[i], gefi->fn[i])) +		{ +			maildir_smapfn_free(fn); +			return; +		} + +	maildir_smapfn_free(fn); + +	for (j=0; i && f[j]; j++) +		if (f[j] == '.' && f[j+1] && f[j+1] != '.') +		{ +			--i; +			if (i == 0) +				break; +		} + +	gefi->pathname=malloc(j+1); + +	if (!gefi->pathname) +	{ +		perror("malloc"); +		return; +	} + +	memcpy(gefi->pathname, f, j); +	gefi->pathname[j]=0; +} + +static char *smap_path(const char *homedir, +		       char **words)  /* words[0] better be INBOX! */ +{ +	struct get_existing_folder_info gefi; +	char *n, *p; +	struct stat stat_buf; + +	if ((n=smaptoUtf7(words)) == NULL) +		return NULL; + +	p=maildir_name2dir(homedir, n); + +	if (!p) +	{ +		free(n); +		return NULL; +	} + +	if (stat(p, &stat_buf) == 0) +	{ +		free(p); +		return n; +	} + +	gefi.fn=words; +	gefi.pathname=NULL; + +	maildir_list(homedir ? homedir:".", +		     &get_existing_callback, &gefi); + +	if (gefi.pathname) +	{ +		free(n); +		free(p); + +		return gefi.pathname; +	} + +	free(p); +	return n; +} + +int maildir_info_smap_find(struct maildir_info *info, char **folder, +			   const char *myId) +{ +	char *p; +	size_t n; +	const char *indexfile; +	struct maildir_shindex_cache *curcache; +	const char *subhierarchy; +	struct imap_find_shared ifs; +	int rc, eof; +	char *indexfile_cpy=NULL; + +	info->homedir=NULL; +	info->maildir=NULL; +	info->owner=NULL; +	info->mailbox_type=MAILBOXTYPE_IGNORE; + +	if (folder[0] == NULL) +	{ +		errno=EINVAL; +		return -1; +	} + +	if (strcmp(folder[0], PUBLIC)) +	{ +		if (strcmp(folder[0], INBOX)) +		{ +			errno=EINVAL; +			return -1; +		} + +		info->maildir=smap_path(NULL, folder); + +		if (info->maildir == NULL) +			return -1; +		info->homedir=strdup("."); +		if (!info->homedir) +		{ +			maildir_info_destroy(info); +			return -1; +		} + +		info->mailbox_type=MAILBOXTYPE_INBOX; + +		info->owner=malloc(strlen(myId)+sizeof("user=")); + +		if (!info->owner) +		{ +			maildir_info_destroy(info); +			return -1; +		} + +		strcat(strcpy(info->owner, "user="), myId); + +		return 0; +	} + +	indexfile=NULL; +	curcache=NULL; +	subhierarchy=NULL; +	n=1; +	ifs.homedir=NULL; +	ifs.maildir=NULL; + +	while (folder[n]) +	{ +		size_t i; + +		curcache=maildir_shared_cache_read(curcache, indexfile, +						   subhierarchy); + +		if (!curcache) +			break; + +		for (i=0; i<curcache->nrecords; i++) +			if (strcmp(curcache->records[i].name, +				   folder[n]) == 0) +				break; + +		if (i >= curcache->nrecords) +			break; +		curcache->indexfile.startingpos= +			curcache->records[i].offset; + +		if (ifs.homedir) +			free(ifs.homedir); +		if (ifs.maildir) +			free(ifs.maildir); +		ifs.homedir=NULL; +		ifs.maildir=NULL; + +		rc=maildir_newshared_nextAt(&curcache->indexfile, +					    &eof, +					    imap_find_cb, &ifs); + +		if (rc || eof) +		{ +			fprintf(stderr, "ERR: Internal error -" +				" maildir_newshared_nextAt: %s\n", +				strerror(errno)); +			fflush(stderr); +			break; +		} + +		if (!ifs.homedir && !ifs.maildir) +			break; + +		if (!ifs.homedir) +		{ +			if (indexfile_cpy) +				free(indexfile_cpy); +			indexfile=indexfile_cpy=ifs.maildir; +			ifs.maildir=NULL; +			subhierarchy=curcache->records[i].name; +			++n; +			continue; +		} + +		if (indexfile_cpy) +			free(indexfile_cpy); +		info->homedir=maildir_location(ifs.homedir, +					       ifs.maildir); +		free(ifs.homedir); +		free(ifs.maildir); + +		info->maildir=NULL; + +		if (maildir_info_suppress(info->homedir)) +		{ + +			free(info->homedir); +			info->homedir=NULL; +			info->maildir=NULL; +			info->mailbox_type=MAILBOXTYPE_IGNORE; +			info->owner=NULL; +			info->owner=strdup("vendor=courier.internal"); +			if (!info->owner) +			{ +				maildir_info_destroy(info); +				return -1; +			} + +			return 0; +		} + + +		if (!subhierarchy || !*subhierarchy) +		{ +			info->owner=strdup("vendor=courier.internal"); +			if (!info->owner) +			{ +				maildir_info_destroy(info); +				return -1; +			} +		} +		else +		{ +			info->owner=malloc(strlen(subhierarchy) +					   +sizeof("user=")); + +			if (!info->owner) +			{ +				free(info->homedir); +				info->homedir=NULL; +				info->maildir=NULL; +				return -1; +			} +			strcpy(info->owner, "user="); +			strcat(info->owner, subhierarchy); +		} + +		p=folder[n]; +		folder[n]=INBOX; +		info->maildir=smap_path(info->homedir, folder+n); +		folder[n]=p; + +		if (!info->maildir) +		{ +			free(info->homedir); +			free(info->owner); +			info->homedir=NULL; +			info->maildir=NULL; +			info->owner=NULL; +			return -1; +		} + +		info->mailbox_type=MAILBOXTYPE_NEWSHARED; +		return 0; +	} + +	if (ifs.homedir) +		free(ifs.homedir); +	if (ifs.maildir) +		free(ifs.maildir); +	if (indexfile_cpy) +		free(indexfile_cpy); + +	if (folder[n] == 0) +	{ +		info->mailbox_type=MAILBOXTYPE_NEWSHARED; +		info->owner=strdup("vendor=courier.internal"); +		if (!info->owner) +		{ +			maildir_info_destroy(info); +			return -1; +		} + +		/* Intermediate shared namespce */ +		return 0; +	} + +	return -1; +} + +static int complex_flag; + +void maildir_info_munge_complex(int f) +{ +	complex_flag=f; +} + +static size_t munge_complex(const char *, char *); + +char *maildir_info_imapmunge(const char *name) +{ +	char *n=libmail_u_convert_tobuf(name, "utf8", +					unicode_x_imap_modutf7, NULL); +	char *p; +	size_t cnt; + +	if (!n) +		return NULL; + +	if (!complex_flag) +	{ +		for (p=n; *p; p++) +		{ +			if (*p == '.' || *p == '/') +				*p=' '; +		} + +		return n; +	} + +	cnt=munge_complex(n, NULL); +	p=malloc(cnt); +	if (!p) +	{ +		free(n); +		return NULL; +	} + +	munge_complex(n, p); + +	free(n); +	return p; +} + +static size_t munge_complex(const char *orig, char *n) +{ +	size_t cnt=0; + +	while (*orig) +	{ +		switch (*orig) { +		case '.': +			if (n) +			{ +				*n++='\\'; +				*n++=':'; +			} +			cnt += 2; +			break; +		case '/': +			if (n) +			{ +				*n++='\\'; +				*n++=';'; +			} +			cnt += 2; +			break; +		case '\\': +			if (n) +			{ +				*n++='\\'; +				*n++='\\'; +			} +			cnt += 2; +			break; +		default: +			if (n) *n++ = *orig; +			++cnt; +		} +		++orig; +	} + +	if (n) *n=0; +	return cnt+1; +} + | 
