diff options
Diffstat (limited to 'maildir/maildirshared.c')
| -rw-r--r-- | maildir/maildirshared.c | 914 | 
1 files changed, 914 insertions, 0 deletions
| diff --git a/maildir/maildirshared.c b/maildir/maildirshared.c new file mode 100644 index 0000000..7123ce6 --- /dev/null +++ b/maildir/maildirshared.c @@ -0,0 +1,914 @@ +/* +** Copyright 2000-2007 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#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	<sys/types.h> +#include	<sys/stat.h> +#include	<string.h> +#include	<stdlib.h> +#include	<time.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif +#include	<stdio.h> +#include	<ctype.h> +#include	<errno.h> +#include	<fcntl.h> + +#include	"maildirmisc.h" +#include	"maildircreate.h" +#include	"maildirsharedrc.h" + + +/* Prerequisited for shared folder support */ + +#if	HAVE_READLINK +#if	HAVE_SYMLINK +#if	HAVE_DBOBJ + +#define	YES_WE_CAN_DO_SHARED	1 + +#endif +#endif +#endif + +#if	YES_WE_CAN_DO_SHARED + +#include	"dbobj.h" + +static void list_sharable(const char *, const char *, +	void (*)(const char *, void *), +	void *); + +extern FILE *maildir_shared_fopen(const char *, const char *); +extern void maildir_shared_fparse(char *, char **, char **); + +void maildir_list_sharable(const char *maildir, +	void (*func)(const char *, void *), +	void *voidp) +{ +char	buf[BUFSIZ]; +FILE	*fp; +char	*p; +int	pass; + +	if (!maildir)	maildir="."; + +	for (pass=0; pass<2; pass++) +	{ +		fp=pass ? maildir_shared_fopen(maildir, "r") +			: fopen (MAILDIRSHAREDRC, "r"); + +		if (!fp)	continue; + +		while ((p=fgets(buf, sizeof(buf), fp)) != 0) +		{ +		char	*name, *dir; + +			maildir_shared_fparse(p, &name, &dir); +			if (name) +				list_sharable(name, dir, func, voidp); +		} +		fclose(fp); +	} +} + +static void list_sharable(const char *pfix, const char *path, +	void (*func)(const char *, void *), +	void *voidp) +{ +DIR	*dirp; +struct	dirent *de; +struct	stat	stat_buf; + +	dirp=opendir(path); +	while (dirp && (de=readdir(dirp)) != 0) +	{ +	char	*z; + +		if (de->d_name[0] != '.')	continue; +		if (strcmp(de->d_name, ".") == 0 || +			strcmp(de->d_name, "..") == 0)	continue; + +		z=malloc(strlen(path)+strlen(de->d_name)+12); +		if (!z)	continue; + +		strcat(strcat(strcat(strcpy(z, path), +			"/"), de->d_name), "/cur/."); + +		if (stat(z, &stat_buf)) +		{ +			free(z); +			continue; +		} +		free(z); +		z=malloc(strlen(pfix)+strlen(de->d_name)+1); +		if (!z)	continue; +		strcat(strcpy(z, pfix), de->d_name); +		(*func)(z, voidp); +		free(z); +	} +	if (dirp)	closedir(dirp); +} + +int maildir_shared_subscribe(const char *maildir, const char *folder) +{ +char	linebuf[BUFSIZ]; +FILE	*fp; +char	*p; +char	*name=strchr(folder, '.'); +char	*s, *n, *dir; +char	*buf, *link; +unsigned l; +int	pass; + +	if (!name) +	{ +		errno=EINVAL; +		return (-1); +	} + +	if (!maildir)	maildir="."; +	p=maildir_shareddir(maildir, folder);	/* valid folder name? */ +	if (!p) +	{ +		errno=EINVAL; +		return (-1); +	} +	free(p); + +	p=0; + +	for (pass=0; pass<2; pass++) +	{ +		fp=pass ? maildir_shared_fopen(maildir, "r") +			: fopen (MAILDIRSHAREDRC, "r"); + +		if (!fp)	continue; + +		while ((p=fgets(linebuf, sizeof(linebuf), fp)) != 0) +		{ +			maildir_shared_fparse(p, &n, &dir); + +			if (!n)	continue; + +			if (strlen(n) == name - folder && +				memcmp(n, folder, name-folder) == 0)	break; +		} +		fclose(fp); + +		if (p)	break; +	} + +	if (p) +	{ +		/* +		** We will create: +		** +		**  maildir/shared-folders/ (name-folder) /(name) +		** +		**  there we'll have subdirs cur/new/tmp  and shared link +		*/ + +		l=sizeof("/" SHAREDSUBDIR "//shared") + +			strlen(maildir) + strlen(folder); +		buf=malloc(l); +		if (!buf)	return (-1); +		strcat(strcpy(buf, maildir), "/" SHAREDSUBDIR); +		mkdir(buf, 0700); +		strcat(buf, "/"); +		strncat(buf, folder, name-folder); +		mkdir(buf, 0700); +		strcat(buf, "/"); +		strcat(buf, name+1); +		if ( mkdir(buf, 0700))	return (-1); +		s=buf+strlen(buf); +		*s++='/'; +		strcpy(s, "tmp"); +		if ( mkdir(buf, 0700)) +		{ +			s[-1]=0; +			rmdir(buf); +			free(buf); +			return (-1); +		} +		strcpy(s, "cur"); +		if ( mkdir(buf, 0700)) +		{ +			strcpy(s, "tmp"); +			rmdir(buf); +			s[-1]=0; +			rmdir(buf); +			free(buf); +			return (-1); +		} +		strcpy(s, "new"); +		if ( mkdir(buf, 0700)) +		{ +			strcpy(s, "cur"); +			rmdir(buf); +			strcpy(s, "tmp"); +			rmdir(buf); +			s[-1]=0; +			rmdir(buf); +			free(buf); +			return (-1); +		} + +		strcpy(s, "shared"); +		if ((link=malloc(strlen(dir)+strlen(name)+2)) == 0 || +			symlink( strcat(strcat(strcpy(link, dir), "/"), name), +				buf)) +		{ +			if (link)	free(link); +			strcpy(s, "new"); +			rmdir(buf); +			strcpy(s, "cur"); +			rmdir(buf); +			strcpy(s, "tmp"); +			rmdir(buf); +			s[-1]=0; +			rmdir(buf); +			free(buf); +			return (-1); +		} +		free(link); +		free(buf); +		return (0); +	} +	errno=ENOENT; +	return (-1); +} + +void maildir_list_shared(const char *maildir, +	void (*func)(const char *, void *), +	void *voidp) +{ +char	*sh; +DIR	*dirp; +struct	dirent *de; + +	if (!maildir)	maildir="."; +	sh=malloc(strlen(maildir)+sizeof("/" SHAREDSUBDIR)); +	if (!sh)	return; + +	strcat(strcpy(sh, maildir), "/" SHAREDSUBDIR); + +	dirp=opendir(sh); +	while (dirp && (de=readdir(dirp)) != 0) +	{ +	DIR	*dirp2; +	struct	dirent *de2; +	char	*z; + +		if (de->d_name[0] == '.')	continue; + +		z=malloc(strlen(sh)+strlen(de->d_name)+2); +		if (!z)	continue; +		strcat(strcat(strcpy(z, sh), "/"), de->d_name); +		dirp2=opendir(z); +		free(z); + +		while (dirp2 && (de2=readdir(dirp2)) != 0) +		{ +		char	*s; + +			if (de2->d_name[0] == '.')	continue; +			s=malloc(strlen(de->d_name)+strlen(de2->d_name)+2); +			if (!s)	continue; +			strcat(strcat(strcpy(s, de->d_name), "."), de2->d_name); +			(*func)(s, voidp); +			free(s); +		} +		if (dirp2)	closedir(dirp2); +	} +	free(sh); +	if (dirp)	closedir(dirp); +} + +int maildir_shared_unsubscribe(const char *maildir, const char *folder) +{ +char	*s; + +	s=maildir_shareddir(maildir, folder); +	if (!s)	return (-1); + +	if (maildir_del(s)) +	{ +		free(s); +		return (-1); +	} +	*strrchr(s, '/')=0;	/* Try to remove the whole folder dir */ +	rmdir(s); +	free(s); +	return (0); +} + +/*                    LET'S SYNC IT                  */ + +static void do_maildir_shared_sync(const char *, const char *); + +void maildir_shared_sync(const char *dir) +{ +char	*shareddir; +char	*buf; + +	shareddir=malloc(strlen(dir)+sizeof("/shared")); +	if (!shareddir) +	{ +		perror("malloc"); +		return; +	} +	strcat(strcpy(shareddir, dir),"/shared"); + +	buf=maildir_getlink(shareddir); +	free(shareddir); +	if (buf) +	{ +		do_maildir_shared_sync(dir, buf); +		free(buf); +	} +} + +/* Step 1 - safely create a temporary database */ + +static int create_db(struct dbobj *obj, +	const char *dir, +	char **dbname) +{ +	struct maildir_tmpcreate_info createInfo; + +	maildir_tmpcreate_init(&createInfo); + +	createInfo.maildir=dir; +	createInfo.uniq="sync"; +	createInfo.doordie=1; + +	{ +		int	fd; + +		fd=maildir_tmpcreate_fd(&createInfo); + +		if (fd < 0) +		{ +			perror(dir); +			return -1; +		} +		close(fd); + +		dbobj_init(obj); +		if (dbobj_open(obj, createInfo.tmpname, "N") < 0) +		{ +			perror(createInfo.tmpname); +			unlink(createInfo.tmpname); +			maildir_tmpcreate_free(&createInfo); +			return (-1); +		} +	} + +	*dbname=createInfo.tmpname; +	createInfo.tmpname=NULL; +	maildir_tmpcreate_free(&createInfo); +	return (0); +} + +/* +** Populate the DB by building the db with the messages in the sharable +** folder's cur.  The key is the stripped message filename, the value is +** the complete message filename. +*/ + +static int build_db(const char *shared, struct dbobj *obj) +{ +char	*dummy=malloc(strlen(shared)+sizeof("/cur")); +DIR	*dirp; +struct	dirent *de; + +	if (!dummy) +	{ +		perror("malloc"); +		return (-1); +	} + +	strcat(strcpy(dummy, shared), "/cur"); + +	dirp=opendir(dummy); +	while (dirp && (de=readdir(dirp)) != 0) +	{ +	char	*a, *b; +	char	*c; + +		if (de->d_name[0] == '.') +			continue; +		if ((a=malloc(strlen(de->d_name)+1)) == 0) +		{ +			perror("malloc"); +			closedir(dirp); +			free(dummy); +			return (-1); +		} +		if ((b=malloc(strlen(de->d_name)+1)) == 0) +		{ +			perror("malloc"); +			closedir(dirp); +			free(dummy); +			free(a); +			return (-1); +		} +		strcpy(a, de->d_name); +		strcpy(b, de->d_name); +		c=strrchr(a, MDIRSEP[0]); +		if (c)	*c=0; + +		if (dbobj_store(obj, a, strlen(a), b, strlen(b), "R")) +		{ +			perror("dbobj_store"); +			free(a); +			free(b); +			closedir(dirp); +			free(dummy); +			return (-1); +		} +		free(a); +		free(b); +	} +	if (dirp)	closedir(dirp); +	free(dummy); +	return (0); +} + +static int update_link(const char *, +	const char *, const char *, +	const char *, +	const char *, +	size_t); + +/* +**	Now, read our synced cur directory, and make sure that the soft +**	links are up to date.  Remove messages that have been deleted from +**	the sharable maildir, and make sure that the remaining links are +**	valid. +*/ + +static int update_cur(const char *cur, const char *shared, struct dbobj *obj) +{ +DIR	*dirp; +struct	dirent *de; +char	*p; + +	dirp=opendir(cur); +	while (dirp && (de=readdir(dirp)) != 0) +	{ +	char	*cur_base; + +	char	*cur_name_ptr; +	size_t	cur_name_len; + +	char	*linked_name_buf; +	size_t	linked_name_len; +	int	n; + +		if (de->d_name[0] == '.')	continue; + +		/* +		** Strip the maildir flags, and look up the message in the +		** db. +		*/ + +		cur_base=malloc(strlen(de->d_name)+1); +		if (!cur_base) +		{ +			perror("malloc"); +			closedir(dirp); +			return (-1); +		} +		strcpy(cur_base, de->d_name); +		p=strrchr(cur_base, MDIRSEP[0]); +		if (p)	*p=0; + +		cur_name_ptr=dbobj_fetch(obj, cur_base, strlen(cur_base), +			&cur_name_len, ""); + +		/* If it's there, delete the db entry. */ + +		if (cur_name_ptr) +			dbobj_delete(obj, cur_base, strlen(cur_base)); + +		/* +		** We'll either delete this soft link, or check its +		** contents, so we better build its complete pathname in +		** any case. +		*/ + +		free(cur_base); +		cur_base=malloc(strlen(de->d_name)+strlen(cur)+2); +		if (!cur_base) +		{ +			perror("malloc"); +			if (cur_name_ptr)	free(cur_name_ptr); +			closedir(dirp); +			return (-1); +		} +		strcat(strcat(strcpy(cur_base, cur), "/"), de->d_name); + +		if (!cur_name_ptr)	/* Removed from sharable dir */ +		{ +			unlink(cur_base); +			free(cur_base); +			continue; +		} + +		linked_name_len=strlen(shared)+strlen(de->d_name)+100; +			/* should be enough */ + +		if ((linked_name_buf=malloc(linked_name_len)) == 0) +		{ +			perror("malloc"); +			free(cur_base); +			free(cur_name_ptr); +			closedir(dirp); +			return (-1); +		} + +		if ((n=readlink(cur_base, linked_name_buf, linked_name_len))< 0) +		{ +			/* This is stupid, let's just unlink this nonsense */ + +			n=0; +		} + +		if (n == 0 || n >= linked_name_len || +			(linked_name_buf[n]=0, +			update_link(cur, +				cur_base, linked_name_buf, shared, cur_name_ptr, +				cur_name_len))) +		{ +			unlink(cur_base); +			free(linked_name_buf); +			free(cur_base); +			free(cur_name_ptr); +			closedir(dirp); +			return (-1); +		} +		free(cur_base); +		free(linked_name_buf); +		free(cur_name_ptr); +	} +	if (dirp)	closedir(dirp); +	return (0); +} + +/* Update the link pointer */ + +static int update_link(const char *curdir, +	const char *linkname, const char *linkvalue, +	const char *shareddir, +	const char *msgfilename, +	size_t msgfilenamelen) +{ +	char	*p=malloc(strlen(shareddir)+sizeof("/cur/")+msgfilenamelen); +	char	*q; +	int	fd; +	struct maildir_tmpcreate_info createInfo; + +	if (!p) +	{ +		perror("malloc"); +		return (-1); +	} + +	strcat(strcpy(p, shareddir), "/cur/"); +	q=p+strlen(p); +	memcpy(q, msgfilename, msgfilenamelen); +	q[msgfilenamelen]=0; + +	if (linkvalue && strcmp(p, linkvalue) == 0) +	{ +		/* the link is good */ + +		free(p); +		return (0); +	} + +	/* Ok, we want this to be an atomic operation. */ + +	maildir_tmpcreate_init(&createInfo); +	createInfo.maildir=curdir; +	createInfo.uniq="relink"; +	createInfo.doordie=1; + +	if ((fd=maildir_tmpcreate_fd(&createInfo)) < 0) +		return -1; + +	close(fd); +	unlink(createInfo.tmpname); + +	if (symlink(p, createInfo.tmpname) < 0 || +	    rename(createInfo.tmpname, linkname) < 0) +	{ +		perror(createInfo.tmpname); +		maildir_tmpcreate_free(&createInfo); +		return (-1); +	} + +	maildir_tmpcreate_free(&createInfo); +	return (0); +} + +/* and now, anything that's left in the temporary db must be new messages */ + +static int newmsgs(const char *cur, const char *shared, struct dbobj *obj) +{ +	char	*key, *val; +	size_t	keylen, vallen; +	int fd; +	struct maildir_tmpcreate_info createInfo; + +	maildir_tmpcreate_init(&createInfo); +	createInfo.maildir=cur; +	createInfo.uniq="newlink"; +	createInfo.doordie=1; + +	if ((fd=maildir_tmpcreate_fd(&createInfo)) < 0) +		return -1; +	close(fd); + +	unlink(createInfo.tmpname); + +	for (key=dbobj_firstkey(obj, &keylen, &val, &vallen); key; +		key=dbobj_nextkey(obj, &keylen, &val, &vallen)) +	{ +	char	*slink=malloc(strlen(shared)+sizeof("/cur/")+vallen); +	char	*q; + +		if (!slink) +		{ +			free(val); +			maildir_tmpcreate_free(&createInfo); +			return (-1); +		} + +		strcat(strcpy(slink, shared), "/cur/"); +		q=slink+strlen(slink); +		memcpy(q, val, vallen); +		q[vallen]=0; +		free(val); + +		if (symlink(slink, createInfo.tmpname)) +		{ +			perror(createInfo.tmpname); + +			free(slink); +			maildir_tmpcreate_free(&createInfo); +			return (-1); +		} + +		free(slink); +		slink=malloc(strlen(cur)+sizeof("/new/" MDIRSEP "2,")+keylen); +		if (!slink) +		{ +			perror("malloc"); +			maildir_tmpcreate_free(&createInfo); +			return (-1); +		} + +		strcat(strcpy(slink, cur), "/new/"); +		q=slink+strlen(slink); +		memcpy(q, key, keylen); +		strcpy(q+keylen, MDIRSEP "2,"); + +		if (rename(createInfo.tmpname, slink)) +		{ +			free(slink); +			maildir_tmpcreate_free(&createInfo); +			return (-1); +		} +		free(slink); +	} +	maildir_tmpcreate_free(&createInfo); +	return (0); +} + +static void do_maildir_shared_sync(const char *dir, const char *shared) +{ +struct	dbobj obj; +char	*dbname; +char	*cur; +char	*shared_update_name; + +struct	stat	stat1, stat2; +int	fd; + +	maildir_purgetmp(dir);	/* clean up after myself */ +	maildir_getnew(dir, 0, NULL, NULL); + +	maildir_purgetmp(shared); +	maildir_getnew(shared, 0, NULL, NULL); + +	/* Figure out if we REALLY need to sync something */ + +	shared_update_name=malloc(strlen(dir)+sizeof("/shared-timestamp")); +	if (!shared_update_name)	return; +	strcat(strcpy(shared_update_name, dir), "/shared-timestamp"); +	cur=malloc(strlen(shared)+sizeof("/new")); +	if (!cur) +	{ +		free(shared_update_name); +		return; +	} + +	if (stat(shared_update_name, &stat1) == 0) +	{ +		if ( stat( strcat(strcpy(cur, shared), "/new"), &stat2) == 0 && +			stat2.st_mtime < stat1.st_mtime && +			stat( strcat(strcpy(cur, shared), "/cur"), &stat2) +			== 0 && stat2.st_mtime < stat1.st_mtime) +		{ +			free(shared_update_name); +			free(cur); +			return; +		} +	} +	if ((fd=maildir_safeopen(shared_update_name, O_RDWR|O_CREAT, 0600))>= 0) +	{ +		if (write(fd, "", 1) < 0) +			perror("write"); +		close(fd); +	} + +	free(cur); +	free(shared_update_name); + +	if (create_db(&obj, dir, &dbname))	return; + +	if (build_db(shared, &obj)) +	{ +		dbobj_close(&obj); +		unlink(dbname); +		free(dbname); +		return; +	} + +	if ((cur=malloc(strlen(dir)+sizeof("/cur"))) == 0) +	{ +		perror("malloc"); +		dbobj_close(&obj); +		unlink(dbname); +		free(dbname); +		return; +	} +	strcat(strcpy(cur, dir), "/cur"); +	if (update_cur(cur, shared, &obj) == 0) +	{ +		strcat(strcpy(cur, dir), "/new"); +		if (update_cur(cur, shared, &obj) == 0) +		{ +			*strrchr(cur, '/')=0;	/* Chop off the /new */ +			newmsgs(cur, shared, &obj); +		} +	} + +	free(cur); +	dbobj_close(&obj); +	unlink(dbname); +	free(dbname); +} + +int maildir_sharedisro(const char *maildir) +{ +char	*p=malloc(strlen(maildir)+sizeof("/shared/cur")); + +	if (!p) +	{ +		perror("malloc"); +		return (-1); +	} +	strcat(strcpy(p, maildir), "/shared/cur"); + +	if (access(p, W_OK) == 0) +	{ +		free(p); +		return (0); +	} +	free(p); +	return (1); +} + +int maildir_unlinksharedmsg(const char *filename) +{ +char	*buf=maildir_getlink(filename); + +	if (buf) +	{ +		struct stat stat_buf; +		int rc=unlink(buf); + +		/* +		** If we FAILED to unlink the real message in the real +		** sharable folder, but the message still exists, it means +		** that we do not have the permission to do so, so do not +		** purge this folder.  Instead, remove the T flag from +		** this message. +		*/ + +		if (rc && stat(buf, &stat_buf) == 0) +		{ +			char *cpy=strdup(filename); + +			if (cpy) +			{ +				char *p=strrchr(cpy, MDIRSEP[0]); + +				if (p && strchr(p, '/') == 0 && +				    strncmp(p, MDIRSEP "2,", 3) == 0 && +				    (p=strchr(p, 'T')) != 0) +				{ +					while ((*p=p[1]) != 0) +						++p; +					rename(filename, cpy); +				} +				free(cpy); +			} +						 +			free(buf); +			return (0); +		} +		free(buf); +	} +	unlink(filename); +	return (0); +} + + +#else + +/* We cannot implement sharing */ + +void maildir_list_sharable(const char *maildir, +	void (*func)(const char *, void *), +	void *voidp) +{ +} + +int maildir_shared_subscribe(const char *maildir, const char *folder) +{ +	errno=EINVAL; +	return (-1); +} + +void maildir_list_shared(const char *maildir, +	void (*func)(const char *, void *), +	void *voidp) +{ +} + +int maildir_shared_unsubscribe(const char *maildir, const char *folder) +{ +	errno=EINVAL; +	return (-1); +} + +#if 0 +char *maildir_shareddir(const char *maildir, const char *sharedname) +{ +	errno=EINVAL; +	return (0); +} +#endif + +void maildir_shared_sync(const char *maildir) +{ +} + +int maildir_sharedisro(const char *maildir) +{ +	return (-1); +} + +int maildir_unlinksharedmsg(const char *filename) +{ +	return (-1); +} +#endif | 
