maildir.c
上传用户:s81996212
上传日期:2007-01-04
资源大小:722k
文件大小:47k
- /*
- ** Copyright 1998 - 2000 Double Precision, Inc. See COPYING for
- ** distribution information.
- */
- /*
- ** $Id: maildir.c,v 1.35 2000/05/02 04:18:51 mrsam Exp $
- */
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <fcntl.h>
- #include <errno.h>
- #include "config.h"
- #include "sqwebmail.h"
- #include "maildir.h"
- #include "folder.h"
- #include "rfc822/rfc822.h"
- #include "pref.h"
- #include "sqconfig.h"
- #include "dbobj.h"
- #include "maildir/maildirquota.h"
- #include "maildir/maildirrequota.h"
- #include "maildir/maildirgetquota.h"
- #include "maildir/maildirmisc.h"
- #include "maildir/maildircreate.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 <sys/types.h>
- #include <sys/stat.h>
- #if HAVE_UTIME_H
- #include <utime.h>
- #endif
- static time_t current_time;
- extern time_t rfc822_parsedt(const char *);
- extern const char *dropre(const char *, int *);
- static char *folderdatname=0; /* Which folder has been cached */
- static struct dbobj folderdat;
- static const char *folderdatmode; /* "R" or "W" */
- static time_t cachemtime; /* Modification time of the cache file */
- static unsigned long new_cnt, all_cnt;
- static int isoldestfirst;
- static char sortorder;
- static void createmdcache(const char *);
- static void maildir_getfoldermsgs(const char *);
- /* Change timestamp on a file */
- static const char *parse_ul(const char *p, unsigned long *ul)
- {
- int err=1;
- while (p && isspace((int)(unsigned char)*p)) ++p;
- *ul=0;
- while (p && *p >= '0' && *p <= '9')
- {
- *ul *= 10;
- *ul += (*p-'0');
- err=0;
- ++p;
- }
- if (err) return (0);
- return (p);
- }
- static void change_timestamp(const char *filename, time_t t)
- {
- #if HAVE_UTIME
- struct utimbuf ut;
- ut.actime=ut.modtime=t;
- utime(filename, &ut);
- #else
- #if HAVE_UTIMES
- struct timeval tv;
- tv.tv_sec=t;
- tv.tv_usec=0;
- utimes(filename, &tv);
- #else
- #error You do not have utime or utimes function. Upgrade your operating system.
- #endif
- #endif
- }
- /* Translate folder name into directory name */
- static char *xlate_mdir(const char *foldername)
- {
- char *p;
- if (strchr(foldername, ':')) enomem();
- p=maildir_folderdir(0, foldername);
- if (!p) enomem();
- return (p);
- }
- static char *xlate_shmdir(const char *foldername)
- {
- if (*foldername == ':')
- {
- char *p;
- p=maildir_shareddir(0, foldername+1);
- if (!p) enomem();
- return (p);
- }
- return (xlate_mdir(foldername));
- }
- /* Display message size in meaningfull form */
- static void cat_n(char *buf, unsigned long n)
- {
- char bb[MAXLONGSIZE+1];
- char *p=bb+sizeof(bb)-1;
- *p=0;
- do
- {
- *--p = "0123456789"[n % 10];
- n=n/10;
- } while (n);
- strcat(buf, p);
- }
- const char *showsize(unsigned long n)
- {
- static char sizebuf[MAXLONGSIZE+10];
- /* If size is less than 1K bytes, display it as 0.xK */
- if (n < 1024)
- {
- strcpy(sizebuf, "0.");
- cat_n(sizebuf, (int)(10 * n / 1024 ));
- strcat(sizebuf, "K");
- }
- /* If size is less than 1 meg, display is as xK */
- else if (n < 1024 * 1024)
- {
- *sizebuf=0;
- cat_n(sizebuf, (unsigned long)(n+512)/1024);
- strcat(sizebuf, "K");
- }
- /* Otherwise, display in megabytes */
- else
- {
- unsigned long nm=(double)n / (1024.0 * 1024.0) * 10;
- *sizebuf=0;
- cat_n( sizebuf, nm / 10);
- strcat(sizebuf, ".");
- cat_n( sizebuf, nm % 10);
- strcat(sizebuf, "M");
- }
- return (sizebuf);
- }
- /* Put together a filename from up to three parts */
- char *alloc_filename(const char *dir1, const char *dir2, const char *filename)
- {
- char *p;
- if (!dir1) dir1="";
- if (!dir2) dir2="";
- p=malloc(strlen(dir1)+strlen(dir2)+strlen(filename)+3);
- if (!p) enomem();
- strcpy(p, dir1);
- if (*dir2)
- {
- if (*p) strcat(p, "/");
- strcat(p, dir2);
- }
- if (*filename)
- {
- if (*p) strcat(p, "/");
- strcat(p, filename);
- }
- return (p);
- }
- /*
- ** char *maildir_find(const char *maildir, const char *filename)
- ** - find a message in a maildir
- **
- ** Return the full path to the indicated message. If the message flags
- ** in filename have changed, we search for the given message.
- */
- char *maildir_find(const char *folder, const char *filename)
- {
- char *p;
- char *d=xlate_shmdir(folder);
- int fd;
- if (!d) return (0);
- p=maildir_filename(d, 0, filename);
- free(d);
- if (!p) enomem();
- if ((fd=open(p, O_RDONLY)) >= 0)
- {
- close(fd);
- return (p);
- }
- free(p);
- return (0);
- }
- /*
- ** char *maildir_basename(const char *filename)
- **
- ** - return base name of the file (strip off cur or new, strip of trailing :)
- */
- char *maildir_basename(const char *filename)
- {
- const char *q=strrchr(filename, '/');
- char *p, *r;
- if (q) ++q;
- else q=filename;
- p=alloc_filename("", "", q);
- if ((r=strchr(p, ':')) != 0) *r=' ';
- return (p);
- }
- /* Display message creation time. If less than one week old (more or less)
- ** show day of the week, and time of day, otherwise show day, month, year
- */
- static const char *displaydate(time_t t)
- {
- struct tm *tmp=localtime(&t);
- static char datebuf[40];
- datebuf[0]=' ';
- if (tmp)
- {
- strftime(datebuf, sizeof(datebuf)-1,
- (t < current_time - 6 * 24 * 60 * 60 ||
- t > current_time + 12 * 60 * 60
- ? "%d %b %Y" : "%a %I:%M %p"), tmp);
- datebuf[sizeof(datebuf)-1]=0;
- }
- return (datebuf);
- }
- /*
- ** Add a flag to a maildir filename
- */
- static char *maildir_addflagfilename(const char *filename, char flag)
- {
- char *new_filename=malloc(strlen(filename)+5);
- /* We can possibly add as many as four character */
- char *p;
- char *q;
- strcpy(new_filename, filename);
- p=strrchr(new_filename, '/');
- if (!p) p=new_filename;
- if ((q=strchr(p, ':')) == 0)
- strcat(new_filename, ":2,");
- else if (q[1] != '2' && q[2] != ',')
- strcpy(p, ":2,");
- p=strchr(p, ':');
- if (strchr(p, flag))
- {
- free(new_filename);
- return (0); /* Already set */
- }
- p += 2;
- while (*p && *p < flag) p++;
- q=p+strlen(p);
- while ((q[1]=*q) != *p)
- --q;
- *p=flag;
- return (new_filename);
- }
- static void closedb()
- {
- if (folderdatname)
- {
- dbobj_close(&folderdat);
- free(folderdatname);
- folderdatname=0;
- }
- }
- static int opencache(const char *folder, const char *mode)
- {
- char *maildir=xlate_shmdir(folder);
- char *cachename;
- size_t l;
- char *p;
- char *q;
- char *r;
- unsigned long ul;
- if (!maildir) return (-1);
- cachename=alloc_filename(maildir, "", MAILDIRCURCACHE "." DBNAME);
- if (folderdatname && strcmp(folderdatname, cachename) == 0)
- {
- if (strcmp(mode, "W") == 0 &&
- strcmp(folderdatmode, "W"))
- ;
- /*
- ** We want to open for write, folder is open for
- ** read
- */
- else
- {
- free(cachename);
- return (0);
- /* We already have this folder cache open */
- }
- }
- closedb();
- folderdatmode=mode;
- dbobj_init(&folderdat);
- if (dbobj_open(&folderdat, cachename, mode)) return (-1);
- folderdatname=cachename;
- if ((p=dbobj_fetch(&folderdat, "HEADER", 6, &l, "")) == 0)
- return (0);
- q=malloc(l+1);
- if (!q) enomem();
- memcpy(q, p, l);
- q[l]=0;
- free(p);
- cachemtime=0;
- new_cnt=0;
- all_cnt=0;
- isoldestfirst=0;
- sortorder=0;
- for (p=q; (p=strtok(p, "n")) != 0; p=0)
- {
- if ((r=strchr(p, '=')) == 0) continue;
- *r++=0;
- if (strcmp(p, "SAVETIME") == 0 &&
- parse_ul(r, &ul))
- cachemtime=ul;
- else if (strcmp(p, "COUNT") == 0)
- parse_ul(r, &all_cnt);
- else if (strcmp(p, "NEWCOUNT") == 0)
- parse_ul(r, &new_cnt);
- else if (strcmp(p, "SORT") == 0)
- {
- unsigned long ul;
- const char *s;
- if ((s=parse_ul(r, &ul)) != 0)
- {
- isoldestfirst=ul;
- sortorder= *s;
- }
- }
- }
- free(q);
- return (0);
- }
- #if 0
- static void maildir_addflag(const char *filename, char flag)
- {
- char *new_filename;
- if ((new_filename=maildir_addflagfilename(filename, flag)) != 0)
- {
- rename(filename, new_filename);
- free(new_filename);
- }
- }
- #endif
- /* And, remove a flag */
- static void maildir_remflagname(char *filename, char flag)
- {
- char *p;
- p=strrchr(filename, '/');
- if (!p) p=filename;
- if ((p=strchr(p, ':')) == 0) return;
- else if (p[1] != '2' && p[2] != ',')
- return;
- p=strchr(p, ':');
- p += 3;
- while (*p && isalpha((int)(unsigned char)*p))
- {
- if (*p == flag)
- {
- while ( (*p=p[1]) != 0)
- p++;
- return;
- }
- p++;
- }
- }
- static MSGINFO *get_msginfo(unsigned long n)
- {
- char namebuf[MAXLONGSIZE+40];
- char *p;
- size_t len;
- unsigned long ul;
- static char *buf=0;
- size_t bufsize=0;
- static MSGINFO msginfo_buf;
- sprintf(namebuf, "REC%lu", n);
- p=dbobj_fetch(&folderdat, namebuf, strlen(namebuf), &len, "");
- if (!p) return (0);
- if (!buf || len > bufsize)
- {
- buf= buf ? realloc(buf, len+1):malloc(len+1);
- if (!buf) enomem();
- bufsize=len;
- }
- memcpy(buf, p, len);
- buf[len]=0;
- memset(&msginfo_buf, 0, sizeof(msginfo_buf));
- msginfo_buf.filename= msginfo_buf.date_s= msginfo_buf.from_s=
- msginfo_buf.subject_s= msginfo_buf.size_s="";
- for (p=buf; (p=strtok(p, "n")) != 0; p=0)
- {
- char *q=strchr(p, '=');
- if (!q) continue;
- *q++=0;
- if (strcmp(p, "FILENAME") == 0)
- msginfo_buf.filename=q;
- else if (strcmp(p, "DATES") == 0)
- msginfo_buf.date_s=q;
- else if (strcmp(p, "FROM") == 0)
- msginfo_buf.from_s=q;
- else if (strcmp(p, "SUBJECT") == 0)
- msginfo_buf.subject_s=q;
- else if (strcmp(p, "SIZES") == 0)
- msginfo_buf.size_s=q;
- else if (strcmp(p, "DATE") == 0 &&
- parse_ul(q, &ul))
- msginfo_buf.date_n=ul;
- else if (strcmp(p, "SIZEN") == 0 &&
- parse_ul(q, &ul))
- msginfo_buf.size_n=ul;
- else if (strcmp(p, "TIME") == 0 &&
- parse_ul(q, &ul))
- msginfo_buf.mi_mtime=ul;
- else if (strcmp(p, "INODE") == 0 &&
- parse_ul(q, &ul))
- msginfo_buf.mi_ino=ul;
- }
- return (&msginfo_buf);
- }
- static MSGINFO *get_msginfo_alloc(unsigned long n)
- {
- MSGINFO *msginfop=get_msginfo(n);
- MSGINFO *p;
- if (!msginfop) return (0);
- if ((p= (MSGINFO *) malloc(sizeof(*p))) == 0)
- enomem();
- memset(p, 0, sizeof(*p));
- if ((p->filename=strdup(msginfop->filename)) == 0 ||
- (p->date_s=strdup(msginfop->date_s)) == 0 ||
- (p->from_s=strdup(msginfop->from_s)) == 0 ||
- (p->subject_s=strdup(msginfop->subject_s)) == 0 ||
- (p->size_s=strdup(msginfop->size_s)) == 0)
- enomem();
- p->date_n=msginfop->date_n;
- p->size_n=msginfop->size_n;
- p->mi_mtime=msginfop->mi_mtime;
- p->mi_ino=msginfop->mi_ino;
- return (p);
- }
- static void put_msginfo(MSGINFO *m, unsigned long n)
- {
- char namebuf[MAXLONGSIZE+40];
- char *rec;
- sprintf(namebuf, "REC%lu", n);
- rec=malloc(strlen(m->filename)+strlen(m->from_s)+
- strlen(m->date_s)+
- strlen(m->subject_s)+strlen(m->size_s)+MAXLONGSIZE*4+
- sizeof("FILENAME=nDATES=nFROM=nSUBJECT=nSIZES=nDATE=n"
- "SIZEN=nTIME=nINODE=n")+100);
- if (!rec) enomem();
- sprintf(rec, "FILENAME=%snDATES=%snFROM=%snSUBJECT=%snSIZES=%sn"
- "DATE=%lun"
- "SIZEN=%lun"
- "TIME=%lun"
- "INODE=%lun",
- m->filename,
- m->date_s,
- m->from_s,
- m->subject_s,
- m->size_s,
- (unsigned long)m->date_n,
- (unsigned long)m->size_n,
- (unsigned long)m->mi_mtime,
- (unsigned long)m->mi_ino);
- if (dbobj_store(&folderdat, namebuf, strlen(namebuf),
- rec, strlen(rec), "R"))
- enomem();
- free(rec);
- }
- static void update_foldermsgs(const char *folder, const char *newname, size_t pos)
- {
- MSGINFO *p;
- char *n;
- n=strrchr(newname, '/')+1;
- if (opencache(folder, "W") || (p=get_msginfo(pos)) == 0)
- {
- error("Internal error in update_foldermsgs");
- return;
- }
- p->filename=n;
- put_msginfo(p, pos);
- }
- static void maildir_markflag(const char *folder, size_t pos, char flag)
- {
- MSGINFO *p;
- char *filename;
- char *new_filename;
- if (opencache(folder, "W") || (p=get_msginfo(pos)) == 0)
- {
- error("Internal error in maildir_markflag");
- return;
- }
- filename=maildir_find(folder, p->filename);
- if (!filename) return;
- if ((new_filename=maildir_addflagfilename(filename, flag)) != 0)
- {
- rename(filename, new_filename);
- update_foldermsgs(folder, new_filename, pos);
- free(new_filename);
- }
- free(filename);
- }
- void maildir_markread(const char *folder, size_t pos)
- {
- maildir_markflag(folder, pos, 'S');
- }
- void maildir_markreplied(const char *folder, const char *message)
- {
- char *filename;
- char *new_filename;
- filename=maildir_find(folder, message);
- if (filename &&
- (new_filename=maildir_addflagfilename(filename, 'R')) != 0)
- {
- rename(filename, new_filename);
- free(new_filename);
- }
- if (filename) free(filename);
- }
- char *maildir_posfind(const char *folder, size_t *pos)
- {
- MSGINFO *p;
- char *filename;
- if (opencache(folder, "R") || (p=get_msginfo( *pos)) == 0)
- {
- error("Internal error in maildir_posfind");
- return (0);
- }
- filename=maildir_find(folder, p->filename);
- return(filename);
- }
- void maildir_msgpurge(const char *folder, size_t pos)
- {
- char *filename=maildir_posfind(folder, &pos);
- if (filename)
- {
- unlink(filename);
- free(filename);
- }
- }
- void maildir_msgpurgefile(const char *folder, const char *msgid)
- {
- char *filename=maildir_find(folder, msgid);
- if (filename)
- {
- unlink(filename);
- free(filename);
- }
- }
- /*
- ** A message is moved to a different folder as follows.
- ** The message is linked to the destination folder, then marked with a 'T'
- ** flag in the original folder. Later, all T-marked messages are deleted.
- */
- static int msgcopy(int fromfd, int tofd)
- /* ... Except moving to/from sharable folder actually
- ** involves copying.
- */
- {
- char buf[8192];
- int i, j;
- char *p;
- while ((i=read(fromfd, buf, sizeof(buf))) > 0)
- {
- p=buf;
- while (i)
- {
- j=write(tofd, p, i);
- if (j <= 0) return (-1);
- p += j;
- i -= j;
- }
- }
- return (i);
- }
- static int do_msgmove(const char *from,
- const char *file, const char *dest, size_t pos)
- {
- char *destdir;
- const char *p;
- char *basename;
- char *newname;
- char *tmpname;
- struct stat stat_buf;
- char *new_filename;
- char quotabuf[QUOTABUFSIZE];
- int quotafd;
- unsigned long filesize=0;
- int no_link=0;
- quotafd= -1;
- if (maildir_getquota(".", quotabuf))
- {
- if (errno != ENOENT) enomem();
- quotabuf[0]=0;
- }
- if (stat(file, &stat_buf) || stat_buf.st_nlink != 1)
- return (0); /* Already moved */
- /* Update quota */
- if (quotabuf[0] && strcmp(from, dest))
- {
- if (maildir_parsequota(file, &filesize))
- filesize=stat_buf.st_size;
- /* Recover from possible corruption */
- if (strcmp(dest, TRASH) == 0) /* Moving to trash */
- {
- int rc;
- if (*from != ':')
- {
- do
- {
- rc=maildir_checkquota(".", "afd,
- quotabuf, -filesize, -1);
- if (rc && errno != EAGAIN)
- {
- if (quotafd >= 0)
- close(quotafd);
- enomem();
- }
- } while (rc);
- maildir_addquota(".", quotafd, quotabuf,
- -filesize, -1);
- }
- if (quotafd >= 0) close(quotafd);
- }
- else /* Moving FROM trash */
- {
- int rc;
- if (*from != ':')
- {
- do
- {
- rc=maildir_checkquota(".", "afd,
- quotabuf, filesize, 1);
- if (rc && errno != EAGAIN)
- {
- if (quotafd >= 0)
- close(quotafd);
- enomem();
- }
- } while (rc);
-
- maildir_addquota(".", quotafd, quotabuf,
- filesize, 1);
- }
- if (quotafd >= 0) close(quotafd);
- }
- }
- destdir=xlate_shmdir(dest);
- if (!destdir) enomem();
- if (*dest == ':' || *from == ':')
- {
- int fromfd, tofd;
- char *l;
- if (*dest == ':') /* Copy to the sharable folder */
- {
- char *p=malloc(strlen(destdir)+sizeof("/shared"));
- if (!p)
- {
- free(destdir);
- enomem();
- }
- strcat(strcpy(p, destdir), "/shared");
- free(destdir);
- destdir=p;
- }
- for (;;)
- {
- int rc=maildir_try_create(destdir, "copy", 0, &tmpname,
- &newname);
- if (rc < 0)
- {
- free(destdir);
- error("Can't create new file.");
- }
- if (rc == 0)
- break;
- sleep(5);
- }
- if (*dest == ':')
- /* We need to copy it directly into /cur of the dest folder */
- {
- memcpy(strrchr(newname, '/')-3, "cur", 3);
- /* HACK!!!!!!!!!!!! */
- }
- if ((fromfd=maildir_semisafeopen(file, O_RDONLY, 0)) < 0)
- {
- free(destdir);
- free(tmpname);
- free(newname);
- enomem();
- }
- if ( *dest == ':' )
- umask (0022);
- if ((tofd=maildir_safeopen(tmpname, O_RDWR|O_CREAT|O_EXCL,
- 0644)) < 0)
- {
- umask (0077);
- close(fromfd);
- free(destdir);
- free(tmpname);
- free(newname);
- return (-1);
- }
- umask (0077);
- if (msgcopy(fromfd, tofd))
- {
- close(fromfd);
- close(tofd);
- free(destdir);
- free(tmpname);
- free(newname);
- enomem();
- }
- close(fromfd);
- close(tofd);
- /*
- ** When we attempt to DELETE a message in the sharable folder,
- ** attempt to remove the UNDERLYING message
- */
- if (*from == ':' && (l=maildir_getlink(file)) != 0)
- {
- if (unlink(l))
- {
- /* Not our message */
- free(l);
- if (strcmp(dest, TRASH) == 0)
- {
- unlink(tmpname);
- free(destdir);
- free(tmpname);
- free(newname);
- return (0);
- }
- }
- free(l);
- }
- if (rename(tmpname, newname))
- {
- free(destdir);
- free(tmpname);
- free(newname);
- enomem();
- }
- no_link=1; /* Don't call link(), below */
- free(tmpname);
- free(newname);
- }
- p=strrchr(file, '/');
- if (p) ++p;
- else p=file;
- if ( (basename=strdup(p)) == NULL)
- enomem();
- maildir_remflagname(basename, 'T'); /* Remove any deleted flag for new name */
- newname=alloc_filename(destdir, "cur", basename);
- free(destdir);
- free(basename);
- /* When DELETE is called for a message in TRASH, from and dest will
- ** be the same, so we just mark the file as Trashed, to be removed
- ** in checknew.
- */
- if (no_link == 0 && strcmp(from, dest))
- link(file, newname);
- free(newname);
- if ((new_filename=maildir_addflagfilename(file, 'T')) != 0)
- {
- rename(file, new_filename);
- update_foldermsgs(from, new_filename, pos);
- free(new_filename);
- }
- return (0);
- }
- int maildir_msgmove(const char *folder, size_t pos, const char *dest)
- {
- char *filename=maildir_posfind(folder, &pos);
- int rc;
- if (!filename) return (0);
- rc=do_msgmove(folder, filename, dest, pos);
- free(filename);
- return (rc);
- }
- void maildir_msgdeletefile(const char *folder, const char *file, size_t pos)
- {
- char *filename=maildir_find(folder, file);
- if (filename)
- {
- (void)do_msgmove(folder, filename, TRASH, pos);
- free(filename);
- }
- }
- int maildir_msgmovefile(const char *folder, const char *file, const char *dest,
- size_t pos)
- {
- char *filename=maildir_find(folder, file);
- int rc;
- if (!filename) return (0);
- rc=do_msgmove(folder, filename, dest, pos);
- free(filename);
- return (rc);
- }
- /*
- ** Grab new messages from new.
- */
- static void maildir_checknew(const char *dir)
- {
- char *dirbuf;
- struct stat stat_buf;
- DIR *dirp;
- struct dirent *dire;
- time_t cntmtime;
- /* Delete old files in tmp */
- maildir_purgetmp(dir);
- /* Move everything from new to cur */
- dirbuf=alloc_filename(dir, "new", "");
- for (dirp=opendir(dirbuf); dirp && (dire=readdir(dirp)) != 0; )
- {
- char *oldname, *newname;
- char *p;
- if (dire->d_name[0] == '.') continue;
- oldname=alloc_filename(dirbuf, dire->d_name, "");
- newname=malloc(strlen(oldname)+4);
- if (!newname) enomem();
- strcat(strcat(strcpy(newname, dir), "/cur/"), dire->d_name);
- p=strrchr(newname, '/');
- if ((p=strchr(p, ':')) != NULL) *p=0; /* Someone screwed up */
- strcat(newname, ":2,");
- rename(oldname, newname);
- free(oldname);
- free(newname);
- }
- if (dirp) closedir(dirp);
- free(dirbuf);
- /* Look for any messages mark as deleted. When we delete a message
- ** we link it into the Trash folder, and mark the original with a T,
- ** which we delete when we check for new messages.
- */
- dirbuf=alloc_filename(dir, "cur", "");
- if (stat(dirbuf, &stat_buf))
- {
- free(dirbuf);
- return;
- }
- /* If the count cache file is still current, the directory hasn't
- ** changed, so we don't need to scan it for deleted messages. When
- ** a message is deleted, the rename bumps up the timestamp.
- **
- ** This depends on dodirscan() being called after this function,
- ** which updates MAILDIRCOUNTCACHE
- */
- if (read_sqconfig(dir, MAILDIRCOUNTCACHE, &cntmtime) != 0 &&
- cntmtime > stat_buf.st_mtime)
- {
- free(dirbuf);
- return;
- }
- for (dirp=opendir(dirbuf); dirp && (dire=readdir(dirp)) != 0; )
- {
- char *p;
- if (dire->d_name[0] == '.') continue;
- if (maildirfile_type(dire->d_name) == MSGTYPE_DELETED)
- {
- p=alloc_filename(dirbuf, "", dire->d_name);
- maildir_unlinksharedmsg(p);
- /* Does The Right Thing if this is a shared
- ** folder
- */
-
- free(p);
- }
- }
- if (dirp) closedir(dirp);
- free(dirbuf);
- }
- /*
- ** Automatically purge deleted messages.
- */
- void maildir_autopurge()
- {
- char *dir;
- char *dirbuf;
- struct stat stat_buf;
- DIR *dirp;
- struct dirent *dire;
- char *filename;
- /* This is called when logging in. Version 0.18 supports maildir
- ** quotas, so automatically upgrade all folders.
- */
- for (dirp=opendir("."); dirp && (dire=readdir(dirp)) != 0; )
- {
- if (dire->d_name[0] != '.') continue;
- if (strcmp(dire->d_name, ".") == 0 ||
- strcmp(dire->d_name, "..") == 0) continue;
- filename=alloc_filename(dire->d_name, "maildirfolder", "");
- if (!filename) enomem();
- close(open(filename, O_RDWR|O_CREAT, 0644));
- free(filename);
- /* Eliminate plain text cache starting with version 0.24 */
- filename=alloc_filename(dire->d_name, MAILDIRCURCACHE, "");
- if (!filename) enomem();
- unlink(filename);
- free(filename);
- }
- /* Version 0.24 top level remove */
- unlink(MAILDIRCURCACHE);
- if (dirp)
- closedir(dirp);
- dir=xlate_mdir(TRASH);
- /* Delete old files in tmp */
- time(¤t_time);
- dirbuf=alloc_filename(dir, "cur", "");
- free(dir);
- for (dirp=opendir(dirbuf); dirp && (dire=readdir(dirp)) != 0; )
- {
- if (dire->d_name[0] == '.') continue;
- filename=alloc_filename(dirbuf, dire->d_name, "");
- if (stat(filename, &stat_buf) == 0 &&
- stat_buf.st_ctime < current_time
- - pref_autopurge * 24 * 60 * 60)
- {
- unlink(filename);
- }
- free(filename);
- }
- if (dirp) closedir(dirp);
- free(dirbuf);
- }
- static int subjectcmp(const char *a, const char *b)
- {
- int aisre;
- int bisre;
- int n;
- a=dropre(a, &aisre);
- b=dropre(b, &bisre);
- n=strcasecmp(a,b);
- if (n == 0) n=aisre - bisre;
- return (n);
- }
- /*
- ** Messages supposed to be arranged in the reverse chronological order of
- ** arrival.
- **
- ** Instead of stat()ing every file in the directory, we depend on the
- ** naming convention that are specified for the Maildir. Therefore, we rely
- ** on Maildir writers observing the required naming conventions.
- */
- static int messagecmp(const MSGINFO **pa, const MSGINFO **pb)
- {
- int gt=1, lt=-1;
- int n;
- const MSGINFO *a= *pa;
- const MSGINFO *b= *pb;
- if (pref_flagisoldest1st)
- {
- gt= -1;
- lt= 1;
- }
- switch (pref_flagsortorder) {
- case 'F':
- n=strcasecmp(a->from_s, b->from_s);
- if (n) return (n);
- break;
- case 'S':
- n=subjectcmp(a->subject_s, b->subject_s);
- if (n) return (n);
- break;
- }
- if (a->date_n < b->date_n) return (gt);
- if (a->date_n > b->date_n) return (lt);
- if (a->mi_ino < b->mi_ino) return (gt);
- if (a->mi_ino > b->mi_ino) return (lt);
- return (0);
- }
- /*
- ** maildirfile_type(directory, filename) - return one of the following:
- **
- ** MSGTYPE_NEW - new message
- ** MSGTYPE_DELETED - trashed message
- ** ' ' - all other kinds
- */
- char maildirfile_type(const char *p)
- {
- const char *q=strrchr(p, '/');
- int seen_trash=0, seen_r=0, seen_s=0;
- if (q) p=q;
- if ( !(p=strchr(p, ':')) || *++p != '2' || *++p != ',')
- return (MSGTYPE_NEW); /* No :2,info */
- ;
- ++p;
- while (p && isalpha((int)(unsigned char)*p))
- switch (*p++) {
- case 'T':
- seen_trash=1;
- break;
- case 'R':
- seen_r=1;
- break;
- case 'S':
- seen_s=1;
- break;
- }
- if (seen_trash)
- return (MSGTYPE_DELETED); /* Trashed message */
- if (seen_s)
- {
- if (seen_r) return (MSGTYPE_REPLIED);
- return (0);
- }
- return (MSGTYPE_NEW);
- }
- static int docount(const char *fn, unsigned *new_cnt, unsigned *other_cnt)
- {
- const char *filename=strrchr(fn, '/');
- char c;
- if (filename) ++filename;
- else filename=fn;
- if (*filename == '.') return (0); /* We don't want this one */
- c=maildirfile_type(filename);
- if (c == MSGTYPE_NEW)
- ++ *new_cnt;
- else
- ++ *other_cnt;
- return (1);
- }
- MSGINFO **maildir_read(const char *dirname, unsigned nfiles,
- size_t *starting_pos,
- int *morebefore, int *moreafter)
- {
- MSGINFO **msginfo;
- size_t i;
- if ((msginfo=malloc(sizeof(MSGINFO *)*nfiles)) == 0)
- enomem();
- for (i=0; i<nfiles; i++)
- msginfo[i]=0;
- if (opencache(dirname, "W")) return (msginfo);
- if (nfiles > all_cnt) nfiles=all_cnt;
- if (*starting_pos + nfiles > all_cnt)
- *starting_pos=all_cnt-nfiles;
- *morebefore = *starting_pos > 0;
- for (i=0; i<nfiles; i++)
- {
- if (*starting_pos + i >= all_cnt) break;
- if ((msginfo[i]= get_msginfo_alloc(*starting_pos + i)) == 0)
- break;
- }
- *moreafter= *starting_pos + i < all_cnt;
- return (msginfo);
- }
- static void dodirscan(const char *, unsigned *, unsigned *);
- void maildir_count(const char *folder,
- unsigned *new_ptr,
- unsigned *other_ptr)
- {
- char *dir=xlate_shmdir(folder);
- *new_ptr=0;
- *other_ptr=0;
- if (dir)
- {
- if (*folder == ':')
- {
- maildir_shared_sync(dir);
- }
- maildir_checknew(dir);
- dodirscan(dir, new_ptr, other_ptr);
- }
- free(dir);
- }
- unsigned maildir_countof(const char *folder)
- {
- maildir_getfoldermsgs(folder);
- return (all_cnt);
- }
- static void dodirscan(const char *dir, unsigned *new_cnt,
- unsigned *other_cnt)
- {
- DIR *dirp;
- struct dirent *de;
- char *curname;
- struct stat cur_stat;
- time_t cntmtime;
- const char *p;
- char cntbuf[MAXLONGSIZE*2+4];
- curname=alloc_filename(dir, "cur", "");
- if (stat(curname, &cur_stat))
- {
- free(curname);
- return;
- }
- if ((p=read_sqconfig(dir, MAILDIRCOUNTCACHE, &cntmtime)) != 0 &&
- cntmtime > cur_stat.st_mtime)
- {
- unsigned long n;
- unsigned long o;
- if ((p=parse_ul(p, &n)) && (p=parse_ul(p, &o)))
- {
- *new_cnt=n;
- *other_cnt=o;
- free(curname);
- return; /* Valid cache of count */
- }
- }
- *new_cnt=0;
- *other_cnt=0;
- dirp=opendir(curname);
- while (dirp && (de=readdir(dirp)) != NULL)
- docount(de->d_name, new_cnt, other_cnt);
- if (dirp) closedir(dirp);
- sprintf(cntbuf, "%u %u", *new_cnt, *other_cnt);
- write_sqconfig(dir, MAILDIRCOUNTCACHE, cntbuf);
- if ( read_sqconfig(dir, MAILDIRCOUNTCACHE, &cntmtime) == 0)
- enomem();
- if (cntmtime == cur_stat.st_mtime) /* Potential race condition */
- {
- free(curname);
- curname=alloc_filename(dir, MAILDIRCOUNTCACHE, "");
- change_timestamp(curname, cntmtime-1);
- /* ... So rebuild it next time */
- }
- free(curname);
- }
- void maildir_free(MSGINFO **files, unsigned nfiles)
- {
- unsigned i;
- for (i=0; i<nfiles; i++)
- {
- if ( files[i] )
- maildir_nfreeinfo( files[i] );
- }
- free(files);
- }
- static char *buf=0;
- size_t bufsize=0, buflen=0;
- static void addbuf(int c)
- {
- if (buflen == bufsize)
- {
- char *newbuf= buf ? realloc(buf, bufsize+512):malloc(bufsize+512);
- if (!newbuf) enomem();
- buf=newbuf;
- bufsize += 512;
- }
- buf[buflen++]=c;
- }
- char *maildir_readline(FILE *fp)
- {
- int c;
- buflen=0;
- while ((c=getc(fp)) != 'n' && c >= 0)
- if (buflen < 8192)
- addbuf(c);
- if (c < 0 && buflen == 0) return (NULL);
- addbuf(0);
- return (buf);
- }
- char *maildir_readheader_mimepart(FILE *fp, char **value, int preserve_nl,
- off_t *mimepos, const off_t *endpos)
- {
- int c;
- buflen=0;
- if (mimepos && *mimepos >= *endpos) return (0);
- while (mimepos == 0 || *mimepos < *endpos)
- {
- if ((c=getc(fp)) != 'n' && c >= 0)
- {
- addbuf(c);
- if (mimepos) ++ *mimepos;
- continue;
- }
- if ( c == 'n' && mimepos) ++ *mimepos;
- if (buflen == 0) return (0);
- if (c < 0) break;
- c=getc(fp);
- if (c >= 0) ungetc(c, fp);
- if (c == 'n' || !isspace(c)) break;
- addbuf(preserve_nl ? 'n':' ');
- }
- addbuf(0);
- for ( *value=buf; **value; (*value)++)
- {
- if (**value == ':')
- {
- **value=' ';
- ++*value;
- break;
- }
- **value=tolower(**value);
- }
- while (**value && isspace((int)(unsigned char)**value)) ++*value;
- return(buf);
- }
- char *maildir_readheader(FILE *fp, char **value, int preserve_nl)
- {
- return (maildir_readheader_mimepart(fp, value, preserve_nl, 0, 0));
- }
- /*****************************************************************************
- The MSGINFO structure contains the summary of the headers found in all
- messages in the cur directory.
- Instead of opening each message every time we need to serve the directory
- contents, the messages are scanned once, and a cache file is built
- containing the contents.
- *****************************************************************************/
- /* Deallocate an individual MSGINFO structure */
- void maildir_nfreeinfo(MSGINFO *mi)
- {
- if (mi->filename) free(mi->filename);
- if (mi->date_s) free(mi->date_s);
- if (mi->from_s) free(mi->from_s);
- if (mi->subject_s) free(mi->subject_s);
- if (mi->size_s) free(mi->size_s);
- free(mi);
- }
- /* Initialize a MSGINFO structure by reading the message headers */
- MSGINFO *maildir_ngetinfo(const char *filename)
- {
- FILE *fp;
- MSGINFO *mi;
- struct stat stat_buf;
- char *hdr, *val;
- const char *p;
- int is_sent_header=0;
- char *fromheader=0;
- int fd;
- /* Hack - see if we're reading a message from the Sent or Drafts
- folder */
- p=strrchr(filename, '/');
- if ((p && p - filename >=
- sizeof(SENT) + 5 && strncmp(p - (sizeof(SENT) + 5),
- "/." SENT "/", sizeof(SENT)+2) == 0)
- || strncmp(filename, "." SENT "/", sizeof(SENT)+1) == 0)
- is_sent_header=1;
- if ((p && p - filename >=
- sizeof(DRAFTS) + 5 && strncmp(p-(sizeof(DRAFTS) + 5),
- "/." DRAFTS "/", sizeof(DRAFTS)+2) == 0)
- || strncmp(filename, "." DRAFTS "/", sizeof(DRAFTS)+1) == 0)
- is_sent_header=1;
- if ((mi=(MSGINFO *)malloc(sizeof(MSGINFO))) == 0)
- enomem();
- memset(mi, ' ', sizeof(*mi));
- fp=0;
- fd=maildir_semisafeopen(filename, O_RDONLY, 0);
- if (fd >= 0)
- if ((fp=fdopen(fd, "r")) == 0)
- close(fd);
- if (fp == NULL)
- {
- free(mi);
- return (NULL);
- }
- /* mi->filename shall be the base filename, normalized as :2, */
- if ((p=strrchr(filename, '/')) != NULL)
- p++;
- else p=filename;
- if (!(mi->filename=strdup(p)))
- enomem();
- if (fstat(fileno(fp), &stat_buf) == 0)
- {
- mi->mi_mtime=stat_buf.st_mtime;
- mi->mi_ino=stat_buf.st_ino;
- mi->size_n=stat_buf.st_size;
- mi->size_s=strdup( showsize(stat_buf.st_size));
- mi->date_n=mi->mi_mtime; /* Default if no Date: */
- if (!mi->size_s) enomem();
- }
- else
- {
- free(mi->filename);
- fclose(fp);
- free(mi);
- return (0);
- }
- while ((hdr=maildir_readheader(fp, &val, 0)) != 0)
- {
- if (strcmp(hdr, "subject") == 0)
- {
- if (mi->subject_s) free(mi->subject_s);
- mi->subject_s=strdup(val);
- if (!mi->subject_s) enomem();
- }
- if (strcmp(hdr, "date") == 0 && mi->date_s == 0)
- {
- time_t t=rfc822_parsedt(val);
- if (t)
- {
- mi->date_n=t;
- mi->date_s=strdup(displaydate(mi->date_n));
- if (!mi->date_s) enomem();
- }
- }
- if ((is_sent_header ?
- strcmp(hdr, "to") == 0 || strcmp(hdr, "cc") == 0:
- strcmp(hdr, "from") == 0) && fromheader == 0)
- {
- struct rfc822t *from_addr;
- struct rfc822a *from_addra;
- char *p;
- int dotflag=0;
- from_addr=rfc822t_alloc(val, NULL);
- if (!from_addr) enomem();
- from_addra=rfc822a_alloc(from_addr);
- if (!from_addra) enomem();
- if (from_addra->naddrs > 1)
- dotflag=1;
- if (from_addra->naddrs > 0)
- p=rfc822_getname(from_addra, 0);
- else
- p=val;
- if (fromheader) free(fromheader);
- if ((fromheader=malloc(strlen(p)+7)) == 0)
- enomem();
- strcpy(fromheader, p);
- if (dotflag)
- strcat(fromheader, "...");
- rfc822a_free(from_addra);
- rfc822t_free(from_addr);
- }
- if (mi->date_s && fromheader && mi->subject_s)
- break;
- }
- fclose(fp);
- mi->from_s=fromheader;
- if (!mi->date_s)
- mi->date_s=strdup(displaydate(mi->date_n));
- if (!mi->date_s) enomem();
- if (!mi->from_s && !(mi->from_s=strdup(""))) enomem();
- if (!mi->subject_s && !(mi->subject_s=strdup(""))) enomem();
- return (mi);
- }
- /************************************************************************/
- /* Save cache file */
- static unsigned long save_cnt, savenew_cnt;
- static time_t save_time;
- static char *save_dbname;
- static char *save_tmpdbname;
- struct dbobj tmpdb;
- static void maildir_save_start(const char *maildir, time_t t)
- {
- char *tptr, *nptr;
- save_dbname=alloc_filename(maildir, "", MAILDIRCURCACHE "." DBNAME);
- save_time=t;
- for (;;)
- {
- int rc=maildir_try_create(maildir, "db", 0, &tptr, &nptr);
- if (rc < 0)
- error("Can't create cache file.");
- if (rc == 0)
- {
- save_tmpdbname=tptr;
- free(nptr);
- break;
- }
- sleep(5);
- }
- dbobj_init(&tmpdb);
- if (dbobj_open(&tmpdb, save_tmpdbname, "N"))
- error("Can't create cache file.");
- save_cnt=0;
- savenew_cnt=0;
- }
- static void maildir_saveinfo(MSGINFO *m)
- {
- char *rec;
- char recnamebuf[MAXLONGSIZE+40];
- rec=malloc(strlen(m->filename)+strlen(m->from_s)+
- strlen(m->date_s)+
- strlen(m->subject_s)+strlen(m->size_s)+MAXLONGSIZE*4+
- sizeof("FILENAME=nDATES=nFROM=nSUBJECT=nSIZES=nDATE=n"
- "SIZEN=nTIME=nINODE=n")+100);
- if (!rec) enomem();
- sprintf(rec, "FILENAME=%snDATES=%snFROM=%snSUBJECT=%snSIZES=%sn"
- "DATE=%lun"
- "SIZEN=%lun"
- "TIME=%lun"
- "INODE=%lun",
- m->filename,
- m->date_s,
- m->from_s,
- m->subject_s,
- m->size_s,
- (unsigned long)m->date_n,
- (unsigned long)m->size_n,
- (unsigned long)m->mi_mtime,
- (unsigned long)m->mi_ino);
- sprintf(recnamebuf, "REC%lu", (unsigned long)save_cnt);
- if (dbobj_store(&tmpdb, recnamebuf, strlen(recnamebuf),
- rec, strlen(rec), "R"))
- enomem();
- free(rec);
- save_cnt++;
- if (maildirfile_type(m->filename) == MSGTYPE_NEW)
- savenew_cnt++;
- }
- static void maildir_save_end(const char *maildir)
- {
- char *curname;
- char *rec;
- curname=alloc_filename(maildir, "", "cur");
- rec=malloc(MAXLONGSIZE*4+sizeof(
- "SAVETIME=n"
- "COUNT=n"
- "NEWCOUNT=n"
- "SORT=n")+100);
- if (!rec) enomem();
- sprintf(rec,
- "SAVETIME=%lunCOUNT=%lunNEWCOUNT=%lunSORT=%d%cn",
- (unsigned long)save_time,
- (unsigned long)save_cnt,
- (unsigned long)savenew_cnt,
- pref_flagisoldest1st,
- pref_flagsortorder);
- if (dbobj_store(&tmpdb, "HEADER", 6, rec, strlen(rec), "R"))
- enomem();
- dbobj_close(&tmpdb);
- free(rec);
- rename(save_tmpdbname, save_dbname);
- unlink(save_tmpdbname);
- #if 0
- What the fuck was I thinking?
- struct stat new_stat_buf;
- struct stat dir_stat_buf;
- if (!replace && (stat(save_cachename, &new_stat_buf)
- || stat(curname, &dir_stat_buf)
- )
- )
- {
- unlink(save_cachename);
- free(save_cachename);
- free(save_tmpcachename);
- free(curname);
- return;
- }
- #endif
- free(curname);
- free(save_dbname);
- free(save_tmpdbname);
- }
- void maildir_savefoldermsgs(const char *folder)
- {
- }
- /************************************************************************/
- struct MSGINFO_LIST {
- struct MSGINFO_LIST *next;
- MSGINFO *minfo;
- } ;
- static void createmdcache(const char *maildir)
- {
- char *curname;
- DIR *dirp;
- struct dirent *de;
- struct MSGINFO_LIST *milist, *newmi;
- MSGINFO *mi;
- unsigned long cnt=0;
- curname=alloc_filename(maildir, "", "cur");
- time(¤t_time);
- maildir_save_start(maildir, current_time);
- milist=0;
- dirp=opendir(curname);
- while (dirp && (de=readdir(dirp)) != NULL)
- {
- char *filename;
- if (de->d_name[0] == '.')
- continue;
- filename=alloc_filename(curname, "", de->d_name);
- mi=maildir_ngetinfo(filename);
- free(filename);
- if (!mi) continue;
- if (!(newmi=malloc(sizeof(struct MSGINFO_LIST)))) enomem();
- newmi->next= milist;
- milist=newmi;
- newmi->minfo=mi;
- ++cnt;
- }
- if (dirp) closedir(dirp);
- free(curname);
- if (milist)
- {
- MSGINFO **miarray=malloc(sizeof(MSGINFO *) * cnt);
- unsigned long i;
- if (!miarray) enomem();
- i=0;
- while (milist)
- {
- miarray[i++]=milist->minfo;
- newmi=milist;
- milist=newmi->next;
- free(newmi);
- }
- qsort(miarray, cnt, sizeof(*miarray),
- ( int (*)(const void *, const void *)) messagecmp);
- for (i=0; i<cnt; i++)
- {
- maildir_saveinfo(miarray[i]);
- maildir_nfreeinfo(miarray[i]);
- }
- free(miarray);
- }
- maildir_save_end(maildir);
- }
- static int chkcache(const char *folder)
- {
- if (opencache(folder, "W")) return (-1);
- if (isoldestfirst != pref_flagisoldest1st) return (-1);
- if (sortorder != pref_flagsortorder) return (-1);
- return (0);
- }
- static void maildir_getfoldermsgs(const char *folder)
- {
- char *dir=xlate_shmdir(folder);
- if (!dir) return;
- while ( chkcache(folder) )
- {
- closedb();
- createmdcache(dir);
- }
- free(dir);
- }
- void maildir_remcache(const char *folder)
- {
- char *dir=xlate_shmdir(folder);
- char *cachename=alloc_filename(dir, "", MAILDIRCURCACHE "." DBNAME);
- unlink(cachename);
- if (folderdatname && strcmp(folderdatname, cachename) == 0)
- closedb();
- free(cachename);
- free(dir);
- }
- void maildir_reload(const char *folder)
- {
- char *dir=xlate_shmdir(folder);
- char *curname;
- struct stat stat_buf;
- if (!dir) return;
- curname=alloc_filename(dir, "cur", ".");
- /* Remove old cache file when: */
- if (opencache(folder, "W") == 0)
- {
- if ( stat(curname, &stat_buf) != 0 ||
- stat_buf.st_mtime >= cachemtime)
- {
- closedb();
- createmdcache(dir);
- }
- }
- free(dir);
- maildir_getfoldermsgs(folder);
- free(curname);
- }
- /*
- maildir_readfolders(char ***) - read all the folders in the mailbox.
- maildir_freefolders(char ***) - deallocate memory
- */
- static void addfolder(const char *name, char ***buf, size_t *size, size_t *cnt)
- {
- if (*cnt >= *size)
- {
- char **newbuf= *buf ? realloc(*buf, (*size + 10) * sizeof(char *))
- : malloc( (*size+10) * sizeof(char *));
- if (!newbuf) enomem();
- *buf=newbuf;
- *size += 10;
- }
- (*buf)[*cnt]=0;
- if ( name && ((*buf)[*cnt]=strdup(name)) == 0) enomem();
- ++*cnt;
- }
- /*
- ** Return a sorted list of folders.
- **
- ** Shared folders:
- **
- ** Shared folders are returned as ":maildirname.foldername".
- **
- ** : characters are not valid in regular folder names, so we use that
- ** to mark shared folders, and they will be manually sorted after
- ** regular folders.
- **
- ** HACK:
- **
- ** Subscribed folders will be listed as ":maildirname.last.0foldername".
- ** Unsubscribed folders will be listed as ":mailfirname.last.1foldername".
- ** So the unsubscribed folders will be sorted after the subscribed ones.
- **
- ** Then, after sorting, remove the 0 or 1 extra character.
- */
- static int mdcomparefunc( char **a, char **b)
- {
- char *ca= *a, *cb= *b;
- if (*ca == ':' && *cb != ':') return (1);
- if (*ca != ':' && *cb == ':') return (-1);
- return (strcasecmp(ca, cb));
- }
- struct add_shared_info {
- char ***p;
- size_t *s;
- size_t *c;
- } ;
- static char *append_suffix(const char *n)
- {
- char *name;
- const char *q=strrchr(n, '.');
- if (!q) return (0); /* ??? */
- ++q;
- name=malloc(strlen(n)+sizeof(":0"));
- if (!name) return (0);
- *name=':';
- memcpy(name+1, n, q-n);
- strcpy( &name[q-n+1], "0");
- strcat(name, q);
- return (name);
- }
- static void add_shared(const char *n, void *p)
- {
- struct add_shared_info *i= (struct add_shared_info *)p;
- char *s;
- if (strchr(n, ':')) return;
- s=append_suffix(n);
- if (!s) return;
- addfolder(s, i->p, i->s, i->c);
- free(s);
- }
- static void add_sharable(const char *n, void *p)
- {
- struct add_shared_info *i= (struct add_shared_info *)p;
- char *s;
- char **c;
- size_t j;
- if (strchr(n, ':')) return;
- /* Only add folders that haven't been subscribed to yet */
- s=append_suffix(n);
- if (!s) return;
- c= *i->p;
- for (j=0; j < *i->c; j++)
- if (strcmp( (*i->p)[j], s) == 0)
- {
- free(s);
- return;
- }
- strrchr(s, '.')[1] = '1';
- addfolder(s, i->p, i->s, i->c);
- free(s);
- }
- void maildir_readfolders(char ***fp)
- {
- size_t fbsize=0;
- size_t fbcnt=0;
- DIR *dirp;
- struct dirent *dire;
- struct stat stat_buf;
- char *temp_buf;
- size_t i;
- *fp=0;
- addfolder(INBOX, fp, &fbsize, &fbcnt);
- dirp=opendir(".");
- while ( dirp && (dire=readdir(dirp)) != 0)
- {
- if (dire->d_name[0] != '.' ||
- strcmp(dire->d_name, ".") == 0 ||
- strcmp(dire->d_name, "..") == 0 ||
- strchr(dire->d_name, ':'))
- continue;
- if ((temp_buf=malloc(strlen(dire->d_name)+5)) == 0)
- enomem();
- if ( stat(
- strcat(strcpy(temp_buf, dire->d_name), "/tmp"),
- &stat_buf) != 0 ||
- !S_ISDIR(stat_buf.st_mode) ||
- stat(
- strcat(strcpy(temp_buf, dire->d_name), "/new"),
- &stat_buf) != 0 ||
- !S_ISDIR(stat_buf.st_mode) ||
- stat(
- strcat(strcpy(temp_buf, dire->d_name), "/cur"),
- &stat_buf) != 0 ||
- !S_ISDIR(stat_buf.st_mode) )
- {
- free(temp_buf);
- continue;
- }
- free(temp_buf);
- addfolder(dire->d_name+1, fp, &fbsize, &fbcnt);
- }
- if (dirp) closedir(dirp);
- {
- struct add_shared_info info;
- info.p=fp;
- info.s= &fbsize;
- info.c= &fbcnt;
- maildir_list_shared(".", &add_shared, &info);
- maildir_list_sharable(".", &add_sharable, &info);
- }
- qsort( (*fp)+1, fbcnt-1, sizeof(**fp),
- (int (*)(const void *, const void *))mdcomparefunc);
- for (i=0; i < fbcnt; i++)
- {
- char *c=(*fp)[i];
- if (*c != ':') continue;
- c=strrchr(c, '.');
- ++c;
- while (*c)
- {
- *c=c[1];
- ++c;
- }
- }
- addfolder(NULL, fp, &fbsize, &fbcnt);
- }
- void maildir_freefolders(char ***fp)
- {
- size_t cnt;
- for (cnt=0; (*fp)[cnt]; cnt++)
- free( (*fp)[cnt] );
- free(*fp);
- }
- int maildir_create(const char *foldername)
- {
- char *dir=xlate_mdir(foldername);
- int rc= -1;
- if (mkdir(dir, 0700) == 0)
- {
- char *tmp=alloc_filename(dir, "tmp", "");
- if (mkdir(tmp, 0700) == 0)
- {
- char *tmp2=alloc_filename(dir, "new", "");
- if (mkdir(tmp2, 0700) == 0)
- {
- char *tmp3=alloc_filename(dir, "cur", "");
- if (mkdir(tmp3, 0700) == 0)
- {
- char *tmp4=alloc_filename(dir, "maildirfolder",
- "");
- close(open(tmp4, O_RDWR|O_CREAT, 0600));
- rc=0;
- free(tmp4);
- }
- free(tmp3);
- }
- if (rc) rmdir(tmp2);
- free (tmp2);
- }
- if (rc) rmdir(tmp);
- free(tmp);
- }
- if (rc) rmdir(dir);
- free(dir);
- return (rc);
- }
- static void rmdirtmp(const char *tmpdir)
- {
- DIR *dirp;
- struct dirent *de;
- dirp=opendir(tmpdir);
- while (dirp && (de=readdir(dirp)) != NULL)
- {
- if (strcmp(de->d_name, ".") && strcmp(de->d_name, ".."))
- {
- char *q=alloc_filename(tmpdir, "", de->d_name);
- if (q) { unlink(q); free(q); }
- }
- }
- if (dirp) closedir(dirp);
- }
- static void rmdirmain(const char *maindir)
- {
- DIR *dirp;
- struct dirent *de;
- dirp=opendir(maindir);
- while (dirp && (de=readdir(dirp)) != NULL)
- {
- if (strcmp(de->d_name, ".") && strcmp(de->d_name, ".."))
- {
- char *q=alloc_filename(maindir, "", de->d_name);
- if (q) { unlink(q); free(q); }
- }
- }
- if (dirp) closedir(dirp);
- }
- int maildir_delete(const char *foldername)
- {
- char *dir, *tmp, *new, *cur;
- int rc=0;
- if (strcmp(foldername, INBOX) == 0 ||
- strcmp(foldername, SENT) == 0 ||
- strcmp(foldername, TRASH) == 0 ||
- strcmp(foldername, DRAFTS) == 0) return (-1);
- dir=xlate_mdir(foldername);
- tmp=alloc_filename(dir, "tmp", "");
- cur=alloc_filename(dir, "cur", "");
- new=alloc_filename(dir, "new", "");
- if ( rmdir(new) || rmdir(cur)
- || ( rmdirtmp(tmp), rmdir(tmp))
- || (rmdirmain(dir), rmdir(dir)) )
- {
- rc= -1;
- mkdir(tmp, 0700);
- mkdir(new, 0700);
- mkdir(cur, 0700);
- }
- free(tmp);
- free(new);
- free(cur);
- free(dir);
- return (rc);
- }
- int maildir_rename(const char *from, const char *to)
- {
- char *fromdir, *todir;
- int rc;
- if (strcmp(from, INBOX) == 0 ||
- strcmp(from, SENT) == 0 ||
- strcmp(from, TRASH) == 0 ||
- strcmp(from, DRAFTS) == 0 ||
- strcmp(to, INBOX) == 0) return (-1);
- fromdir=xlate_mdir(from);
- todir=xlate_mdir(to);
- rc=rename(fromdir, todir);
- free(fromdir);
- free(todir);
- return (rc);
- }
- /* ------------------------------------------------------------------- */
- /* Here's where we create a new message in a maildir. First maildir_createmsg
- ** is called. Then, the message contents are defined via maildir_writemsg,
- ** then maildir_closemsg is called. */
- static char writebuf[BUFSIZ];
- static char *writebufptr;
- static int writebufcnt, writebufleft;
- static int writeerr;
- off_t writebufpos;
- int writebuf8bit;
- int maildir_createmsg(const char *foldername, const char *seq,
- char **retname)
- {
- char *p;
- char *dir=xlate_mdir(foldername);
- char *filename;
- int n;
- /* Create a new file in the tmp directory. */
- for (;;)
- {
- int rc;
- char *nptr;
- rc=maildir_try_create(dir, seq, 0, &filename, &nptr);
- if (rc < 0)
- error("maildir_createmsg: cannot create temp file.");
- if (rc == 0)
- {
- free(nptr);
- break;
- }
- sleep(5);
- }
- n=maildir_safeopen(filename, O_CREAT|O_RDWR|O_TRUNC, 0644);
- if (n < 0)
- {
- free(filename);
- error("maildir_createmsg: cannot create temp file.");
- }
- p=strrchr(filename, '/');
- *retname=strdup(p+1);
- if (*retname == 0)
- {
- close(n);
- free(filename);
- enomem();
- }
- free(filename);
- /* Buffer writes */
- writebufcnt=0;
- writebufpos=0;
- writebuf8bit=0;
- writebufleft=0;
- writeerr=0;
- return (n);
- }
- /* Like createmsg, except we're rewriting the contents of this message here,
- ** so we might as well use the same name. */
- int maildir_recreatemsg(const char *folder, const char *name, char **baseptr)
- {
- char *dir=xlate_mdir(folder);
- char *base;
- char *p;
- int n;
- base=maildir_basename(name);
- p=alloc_filename(dir, "tmp", base);
- free(dir);
- *baseptr=base;
- n=maildir_safeopen(p, O_CREAT|O_RDWR|O_TRUNC, 0644);
- if (n < 0) free(base);
- free(p);
- writebufcnt=0;
- writebufleft=0;
- writeerr=0;
- writebufpos=0;
- writebuf8bit=0;
- return (n);
- }
- /* Flush write buffer */
- static void writeflush(int n)
- {
- const char *q=writebuf;
- int c;
- /* Keep calling write() until there's an error, or we're all done */
- while (!writeerr && writebufcnt)
- {
- c=write(n, q, writebufcnt);
- if ( c <= 0)
- writeerr=1;
- else
- {
- q += c;
- writebufcnt -= c;
- }
- }
- /* We have an empty buffer now */
- writebufcnt=0;
- writebufleft=sizeof(writebuf);
- writebufptr=writebuf;
- }
- /* Add whatever we have to the buffer */
- /* Write to the message file. The writes are buffered, and we will set a
- ** flag if there's error writing to the message file.
- */
- void maildir_writemsgstr(int n, const char *p)
- {
- maildir_writemsg(n, p, strlen(p));
- }
- void maildir_writemsg(int n, const char *p, size_t cnt)
- {
- int c;
- size_t i;
- writebufpos += cnt; /* I'm optimistic */
- for (i=0; i<cnt; i++)
- if (p[i] & 0x80) writebuf8bit=1;
- while (cnt)
- {
- /* Flush buffer if it's full. No need to flush if we've
- ** already had an error before. */
- if (writebufleft == 0) writeflush(n);
- c=writebufleft;
- if (c > cnt) c=cnt;
- memcpy(writebufptr, p, c);
- writebufptr += c;
- p += c;
- cnt -= c;
- writebufcnt += c;
- writebufleft -= c;
- }
- }
- /* The new message has been written out. Move the new file from the tmp
- ** directory to either the new directory (if 'new' is set), or to the
- ** cur directory.
- **
- ** The caller might have encountered an error condition. If 'isok' is zero,
- ** we just delete the file. If we had a write error, we delete the file
- ** as well. We return -1 in both cases, or 0 if the new file has been
- ** succesfully moved into its final resting place.
- */
- int maildir_writemsg_flush(int n )
- {
- writeflush(n);
- return (writeerr);
- }
- int maildir_closemsg(int n, /* File descriptor */
- const char *folder, /* Folder */
- const char *retname, /* Filename in folder */
- int isok, /* 0 - discard it (I changed my mind),
- 1 - keep it
- -1 - keep it even if we'll exceed the quota
- */
- unsigned long prevsize /* Prev size of this msg, used in quota calc */
- )
- {
- char *dir=xlate_mdir(folder);
- char *oldname=alloc_filename(dir, "tmp", retname);
- char *newname;
- struct stat stat_buf;
- char quotabuf[QUOTABUFSIZE];
- int quotafd;
- writeflush(n); /* If there's still anything in the buffer */
- if (fstat(n, &stat_buf))
- {
- close(n);
- unlink(oldname);
- enomem();
- }
- newname=maildir_find(folder, retname);
- /* If we called recreatemsg before */
- if (!newname)
- {
- newname=alloc_filename(dir, "cur:2,S", retname);
- /* Hack of the century ^^^^ */
- strcat(strcat(strcat(strcpy(newname, dir), "/cur/"),
- retname), ":2,S");
- }
- if (writeerr)
- {
- close(n);
- unlink(oldname);
- enomem();
- }
- close(n);
- if (maildir_getquota(dir, quotabuf))
- {
- if (errno != ENOENT) enomem();
- quotabuf[0]=0;
- }
- quotafd= -1;
- if (quotabuf[0])
- {
- /* *ALWAYS* call checkquota */
- if (maildir_checkquota(dir, "afd, quotabuf,
- stat_buf.st_size - prevsize,
- (prevsize == 0 ? 1:0)) && errno != EAGAIN)
- {
- if (prevsize < stat_buf.st_size
- /* But report an error only when quota
- ** gets larger.
- */
- )
- {
- if (isok == -1)
- {
- isok= -2;
- }
- else
- {
- isok=0;
- if (quotafd >= 0)
- close(quotafd);
- }
- }
- }
- }
- if (isok)
- rename(oldname, newname);
-
- unlink(oldname);
- if (isok)
- {
- char *realnewname=maildir_requota(newname, stat_buf.st_size);
- if (strcmp(newname, realnewname))
- rename(newname, realnewname);
- free(realnewname);
- if (quotabuf[0])
- {
- maildir_addquota(dir, quotafd, quotabuf,
- stat_buf.st_size - prevsize,
- (prevsize == 0 ? 1:0));
- }
- }
- free(dir);
- free(oldname);
- free(newname);
- return (isok && isok != -2? 0:-1);
- }
- void maildir_deletenewmsg(int n, const char *folder, const char *retname)
- {
- char *dir=xlate_mdir(folder);
- char *oldname=alloc_filename(dir, "tmp", retname);
- close(n);
- unlink(oldname);
- free(oldname);
- free(dir);
- }
- void maildir_cleanup()
- {
- closedb();
- }