399 lines
6 KiB
C
399 lines
6 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <bio.h>
|
|
#include <auth.h>
|
|
#include "imap4d.h"
|
|
|
|
static int copyData(int ffd, int tfd, MbLock *ml);
|
|
static MbLock mLock =
|
|
{
|
|
.fd = -1
|
|
};
|
|
|
|
static char curDir[MboxNameLen];
|
|
|
|
void
|
|
resetCurDir(void)
|
|
{
|
|
curDir[0] = '\0';
|
|
}
|
|
|
|
int
|
|
myChdir(char *dir)
|
|
{
|
|
if(strcmp(dir, curDir) == 0)
|
|
return 0;
|
|
if(dir[0] != '/' || strlen(dir) > MboxNameLen)
|
|
return -1;
|
|
strcpy(curDir, dir);
|
|
if(chdir(dir) < 0){
|
|
werrstr("mychdir failed: %r");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cdCreate(char *dir, char *file, int mode, ulong perm)
|
|
{
|
|
if(myChdir(dir) < 0)
|
|
return -1;
|
|
return create(file, mode, perm);
|
|
}
|
|
|
|
Dir*
|
|
cdDirstat(char *dir, char *file)
|
|
{
|
|
if(myChdir(dir) < 0)
|
|
return nil;
|
|
return dirstat(file);
|
|
}
|
|
|
|
int
|
|
cdExists(char *dir, char *file)
|
|
{
|
|
Dir *d;
|
|
|
|
d = cdDirstat(dir, file);
|
|
if(d == nil)
|
|
return 0;
|
|
free(d);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
cdDirwstat(char *dir, char *file, Dir *d)
|
|
{
|
|
if(myChdir(dir) < 0)
|
|
return -1;
|
|
return dirwstat(file, d);
|
|
}
|
|
|
|
int
|
|
cdOpen(char *dir, char *file, int mode)
|
|
{
|
|
if(myChdir(dir) < 0)
|
|
return -1;
|
|
return open(file, mode);
|
|
}
|
|
|
|
int
|
|
cdRemove(char *dir, char *file)
|
|
{
|
|
if(myChdir(dir) < 0)
|
|
return -1;
|
|
return remove(file);
|
|
}
|
|
|
|
/*
|
|
* open the one true mail lock file
|
|
*/
|
|
MbLock*
|
|
mbLock(void)
|
|
{
|
|
int i;
|
|
|
|
if(mLock.fd >= 0)
|
|
bye("mail lock deadlock");
|
|
for(i = 0; i < 5; i++){
|
|
mLock.fd = openLocked(mboxDir, "L.mbox", OREAD);
|
|
if(mLock.fd >= 0)
|
|
return &mLock;
|
|
sleep(1000);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
void
|
|
mbUnlock(MbLock *ml)
|
|
{
|
|
if(ml != &mLock)
|
|
bye("bad mail unlock");
|
|
if(ml->fd < 0)
|
|
bye("mail unlock when not locked");
|
|
close(ml->fd);
|
|
ml->fd = -1;
|
|
}
|
|
|
|
void
|
|
mbLockRefresh(MbLock *ml)
|
|
{
|
|
char buf[1];
|
|
|
|
seek(ml->fd, 0, 0);
|
|
read(ml->fd, buf, 1);
|
|
}
|
|
|
|
int
|
|
mbLocked(void)
|
|
{
|
|
return mLock.fd >= 0;
|
|
}
|
|
|
|
char*
|
|
impName(char *name)
|
|
{
|
|
char *s;
|
|
int n;
|
|
|
|
if(cistrcmp(name, "inbox") == 0)
|
|
if(access("msgs", AEXIST) == 0)
|
|
name = "msgs";
|
|
else
|
|
name = "mbox";
|
|
n = strlen(name) + STRLEN(".imp") + 1;
|
|
s = binalloc(&parseBin, n, 0);
|
|
if(s == nil)
|
|
return nil;
|
|
snprint(s, n, "%s.imp", name);
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* massage the mailbox name into something valid
|
|
* eliminates all .', and ..',s, redundatant and trailing /'s.
|
|
*/
|
|
char *
|
|
mboxName(char *s)
|
|
{
|
|
char *ss;
|
|
|
|
ss = mutf7str(s);
|
|
if(ss == nil)
|
|
return nil;
|
|
cleanname(ss);
|
|
return ss;
|
|
}
|
|
|
|
char *
|
|
strmutf7(char *s)
|
|
{
|
|
char *m;
|
|
int n;
|
|
|
|
n = strlen(s) * MUtf7Max + 1;
|
|
m = binalloc(&parseBin, n, 0);
|
|
if(m == nil)
|
|
return nil;
|
|
if(encmutf7(m, n, s) < 0)
|
|
return nil;
|
|
return m;
|
|
}
|
|
|
|
char *
|
|
mutf7str(char *s)
|
|
{
|
|
char *m;
|
|
int n;
|
|
|
|
/*
|
|
* n = strlen(s) * UTFmax / (2.67) + 1
|
|
* UTFMax / 2.67 == 3 / (8/3) == 9 / 8
|
|
*/
|
|
n = strlen(s);
|
|
n = (n * 9 + 7) / 8 + 1;
|
|
m = binalloc(&parseBin, n, 0);
|
|
if(m == nil)
|
|
return nil;
|
|
if(decmutf7(m, n, s) < 0)
|
|
return nil;
|
|
return m;
|
|
}
|
|
|
|
void
|
|
splitr(char *s, int c, char **left, char **right)
|
|
{
|
|
char *d;
|
|
int n;
|
|
|
|
n = strlen(s);
|
|
d = binalloc(&parseBin, n + 1, 0);
|
|
if(d == nil)
|
|
parseErr("out of memory");
|
|
strcpy(d, s);
|
|
s = strrchr(d, c);
|
|
if(s != nil){
|
|
*left = d;
|
|
*s++ = '\0';
|
|
*right = s;
|
|
}else{
|
|
*right = d;
|
|
*left = d + n;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* create the mailbox and all intermediate components
|
|
* a trailing / implies the new mailbox is a directory;
|
|
* otherwise, it's a file.
|
|
*
|
|
* return with the file open for write, or directory open for read.
|
|
*/
|
|
int
|
|
createBox(char *mbox, int dir)
|
|
{
|
|
char *m;
|
|
int fd;
|
|
|
|
fd = -1;
|
|
for(m = mbox; *m; m++){
|
|
if(*m == '/'){
|
|
*m = '\0';
|
|
if(access(mbox, AEXIST) < 0){
|
|
if(fd >= 0)
|
|
close(fd);
|
|
fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775);
|
|
if(fd < 0)
|
|
return -1;
|
|
}
|
|
*m = '/';
|
|
}
|
|
}
|
|
if(dir)
|
|
fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775);
|
|
else
|
|
fd = cdCreate(mboxDir, mbox, OWRITE, 0664);
|
|
return fd;
|
|
}
|
|
|
|
/*
|
|
* move one mail folder to another
|
|
* destination mailbox doesn't exist.
|
|
* the source folder may be a directory or a mailbox,
|
|
* and may be in the same directory as the destination,
|
|
* or a completely different directory.
|
|
*/
|
|
int
|
|
moveBox(char *from, char *to)
|
|
{
|
|
Dir *d;
|
|
char *fd, *fe, *td, *te, *fimp;
|
|
|
|
splitr(from, '/', &fd, &fe);
|
|
splitr(to, '/', &td, &te);
|
|
|
|
/*
|
|
* in the same directory: try rename
|
|
*/
|
|
d = cdDirstat(mboxDir, from);
|
|
if(d == nil)
|
|
return 0;
|
|
if(strcmp(fd, td) == 0){
|
|
nulldir(d);
|
|
d->name = te;
|
|
if(cdDirwstat(mboxDir, from, d) >= 0){
|
|
fimp = impName(from);
|
|
d->name = impName(te);
|
|
cdDirwstat(mboxDir, fimp, d);
|
|
free(d);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* directory copy is too hard for now
|
|
*/
|
|
if(d->mode & DMDIR)
|
|
return 0;
|
|
free(d);
|
|
|
|
return copyBox(from, to, 1);
|
|
}
|
|
|
|
/*
|
|
* copy the contents of one mailbox to another
|
|
* either truncates or removes the source box if it succeeds.
|
|
*/
|
|
int
|
|
copyBox(char *from, char *to, int doremove)
|
|
{
|
|
MbLock *ml;
|
|
char *fimp, *timp;
|
|
int ffd, tfd, ok;
|
|
|
|
if(cistrcmp(from, "inbox") == 0)
|
|
if(access("msgs", AEXIST) == 0)
|
|
from = "msgs";
|
|
else
|
|
from = "mbox";
|
|
|
|
ml = mbLock();
|
|
if(ml == nil)
|
|
return 0;
|
|
ffd = openLocked(mboxDir, from, OREAD);
|
|
if(ffd < 0){
|
|
mbUnlock(ml);
|
|
return 0;
|
|
}
|
|
tfd = createBox(to, 0);
|
|
if(tfd < 0){
|
|
mbUnlock(ml);
|
|
close(ffd);
|
|
return 0;
|
|
}
|
|
|
|
ok = copyData(ffd, tfd, ml);
|
|
close(ffd);
|
|
close(tfd);
|
|
if(!ok){
|
|
mbUnlock(ml);
|
|
return 0;
|
|
}
|
|
|
|
fimp = impName(from);
|
|
timp = impName(to);
|
|
if(fimp != nil && timp != nil){
|
|
ffd = cdOpen(mboxDir, fimp, OREAD);
|
|
if(ffd >= 0){
|
|
tfd = cdCreate(mboxDir, timp, OWRITE, 0664);
|
|
if(tfd >= 0){
|
|
copyData(ffd, tfd, ml);
|
|
close(tfd);
|
|
}
|
|
close(ffd);
|
|
}
|
|
}
|
|
cdRemove(mboxDir, fimp);
|
|
if(doremove)
|
|
cdRemove(mboxDir, from);
|
|
else
|
|
close(cdOpen(mboxDir, from, OWRITE|OTRUNC));
|
|
mbUnlock(ml);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* copies while holding the mail lock,
|
|
* then tries to copy permissions and group ownership
|
|
*/
|
|
static int
|
|
copyData(int ffd, int tfd, MbLock *ml)
|
|
{
|
|
Dir *fd, td;
|
|
char buf[BufSize];
|
|
int n;
|
|
|
|
for(;;){
|
|
n = read(ffd, buf, BufSize);
|
|
if(n <= 0){
|
|
if(n < 0)
|
|
return 0;
|
|
break;
|
|
}
|
|
if(write(tfd, buf, n) != n)
|
|
return 0;
|
|
mbLockRefresh(ml);
|
|
}
|
|
fd = dirfstat(ffd);
|
|
if(fd != nil){
|
|
nulldir(&td);
|
|
td.mode = fd->mode;
|
|
if(dirfwstat(tfd, &td) >= 0){
|
|
nulldir(&td);
|
|
td.gid = fd->gid;
|
|
dirfwstat(tfd, &td);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|