maildirshared.c
上传用户:s81996212
上传日期:2007-01-04
资源大小:722k
文件大小:16k
- /*
- ** Copyright 2000 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"
- static const char rcsid[]="$Id: maildirshared.c,v 1.12 2000/05/07 17:14:24 mrsam Exp $";
- /* 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_mddelete(s))
- {
- free(s);
- return (-1);
- }
- *strrchr(s, '/')=0; /* Try to remove the whole folder dir */
- rmdir(s);
- free(s);
- return (0);
- }
- char *maildir_shareddir(const char *maildir, const char *sharedname)
- {
- char *p, *q;
- const char *r;
- size_t l;
- if (!maildir) maildir=".";
- l=strlen(maildir);
- if (strchr(sharedname, '.') == 0 || *sharedname == '.' ||
- strchr(sharedname, '/'))
- {
- errno=EINVAL;
- return (0);
- }
- for (r=sharedname; *r; r++)
- {
- if (*r == '.' && (r[1] == '.' || r[1] == ' '))
- {
- errno=EINVAL;
- return (0);
- }
- }
- p=malloc(strlen(maildir)+sizeof("/" SHAREDSUBDIR "/")+strlen(sharedname));
- if (!p) return (0);
- *p=0;
- if (strcmp(maildir, "."))
- strcat(strcpy(p, maildir), "/");
- strcat(p, SHAREDSUBDIR "/");
- q=p+strlen(p);
- strcpy(q, sharedname);
- *strchr(q, '.')='/';
- return (p);
- }
- /* 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)
- {
- int rc;
- char *tmpname, *dummy;
- for (;;)
- {
- int fd;
- rc=maildir_try_create(dir, "sync", 0, &tmpname, &dummy);
- if (rc < 0)
- {
- perror(dir);
- return (-1);
- }
- if (rc == 0)
- {
- fd=maildir_safeopen(tmpname, O_RDWR|O_CREAT|O_EXCL,
- 0600);
- if (fd >= 0)
- {
- dbobj_init(obj);
- if (dbobj_open(obj, tmpname, "N") < 0)
- {
- perror(tmpname);
- close(fd);
- unlink(tmpname);
- free(tmpname);
- free(dummy);
- return (-1);
- }
- close(fd);
- free(dummy);
- break;
- }
- perror(tmpname);
- free(tmpname);
- free(dummy);
- }
- sleep(3);
- }
- *dbname=tmpname;
- 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, ':');
- 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, ':');
- 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 rc;
- char *tmpname, *dummy;
- 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. */
- for (;;)
- {
- rc=maildir_try_create(curdir, "relink", 0, &tmpname, &dummy);
- if (rc < 0)
- {
- perror(curdir);
- return (-1);
- }
- if (rc == 0)
- {
- free(dummy);
- if (symlink(p, tmpname))
- {
- perror(tmpname);
- free(tmpname);
- free(p);
- return (-1);
- }
- break;
- }
- sleep(3);
- }
- free(p);
- if (rename(tmpname, linkname))
- {
- perror(linkname);
- unlink(tmpname);
- free(tmpname);
- return (-1);
- }
- free(tmpname);
- 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 rc;
- char *tmpname, *dummy;
- 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);
- return (-1);
- }
- strcat(strcpy(slink, shared), "/cur/");
- q=slink+strlen(slink);
- memcpy(q, val, vallen);
- q[vallen]=0;
- free(val);
- for (;;)
- {
- rc=maildir_try_create(cur, "newlink",
- 0, &tmpname, &dummy);
- if (rc < 0)
- {
- perror(cur);
- free(slink);
- return (-1);
- }
- if (rc == 0)
- {
- free(dummy);
- if (symlink(slink, tmpname))
- {
- perror(tmpname);
- free(tmpname);
- free(slink);
- return (-1);
- }
- break;
- }
- sleep(3);
- }
- free(slink);
- slink=malloc(strlen(cur)+sizeof("/new/:2,")+keylen);
- if (!slink)
- {
- perror("malloc");
- unlink(tmpname);
- free(tmpname);
- }
- strcat(strcpy(slink, cur), "/new/");
- q=slink+strlen(slink);
- memcpy(q, key, keylen);
- strcpy(q+keylen, ":2,");
- if (rename(tmpname, slink))
- {
- perror(slink);
- unlink(tmpname);
- free(tmpname);
- }
- }
- 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);
- maildir_purgetmp(shared);
- maildir_getnew(shared, 0);
- /* 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)
- {
- write(fd, "", 1);
- close(fd);
- }
- 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)
- {
- if (access(buf, 0) == 0)
- {
- if (unlink(buf))
- {
- free(buf);
- return (-1);
- }
- }
- 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);
- }
- char *maildir_shareddir(const char *maildir, const char *sharedname)
- {
- errno=EINVAL;
- return (0);
- }
- void maildir_shared_sync(const char *maildir)
- {
- }
- int maildir_sharedisro(const char *maildir)
- {
- return (-1);
- }
- int maildir_unlinksharedmsg(const char *filename)
- {
- return (-1);
- }
- #endif