#include "common.h" enum{ Mbox = 1, Mdir,}; typedef struct Folder Folder;struct Folder{ int open; int ofd; int type; Biobuf *out; Mlock *l;};static Folder ftab[5]; static Folder*getfolder(Biobuf *out){ int i; Folder *f; for(i = 0; i < nelem(ftab); i++){ f = ftab+i; if(f->open == 0){ f->open = 1; f->ofd = -1; f->type = 0; return f; } if(f->out == out) return f; } sysfatal("folder.c:ftab too small"); return 0;} static intputfolder(Folder *f){ int r; r = 0; if(f->l) sysunlock(f->l); if(f->out) r |= Bterm(f->out); if(f->ofd > 0){ close(f->ofd); free(f->out); } memset(f, 0, sizeof *f); return r;} static Biobuf*mboxopen(char *s){ Folder *f; f = getfolder(0); f->l = syslock(s); /* traditional botch: ignore failure */ if((f->ofd = open(s, OWRITE)) == -1) if((f->ofd = create(s, OWRITE|OEXCL, DMAPPEND|0600)) == -1){ putfolder(f); return nil; } seek(f->ofd, 0, 2); f->out = malloc(sizeof *f->out); Binit(f->out, f->ofd, OWRITE); f->type = Mbox; return f->out;} /* * sync with send/cat_mail.c:/^mdir */static Biobuf*mdiropen(char *s, long t){ char buf[64]; long i; Folder *f; f = getfolder(0); for(i = 0; i < 100; i++){ snprint(buf, sizeof buf, "%s/%lud.%.2ld", s, t, i); if((f->ofd = create(buf, OWRITE|OEXCL, DMAPPEND|0660)) != -1) goto found; } putfolder(f); return nil;found: werrstr(""); f->out = malloc(sizeof *f->out); Binit(f->out, f->ofd, OWRITE); f->type = Mdir; return f->out;} Biobuf*openfolder(char *s, long t){ int isdir; Dir *d; if(d = dirstat(s)){ isdir = d->mode&DMDIR; free(d); }else{ isdir = create(s, OREAD, DMDIR|0777); if(isdir == -1) return nil; close(isdir); isdir = 1; } if(isdir) return mdiropen(s, t); else return mboxopen(s);} intrenamefolder(Biobuf *b, long t){ char buf[32]; int i; Dir d; Folder *f; f = getfolder(b); if(f->type != Mdir) return 0; for(i = 0; i < 100; i++){ nulldir(&d); snprint(buf, sizeof buf, "%lud.%.2d", t, i); d.name = buf; if(dirfwstat(Bfildes(b), &d) > 0) return 0; } return -1;} intclosefolder(Biobuf *b){ return putfolder(getfolder(b));} /* * escape "From " at the beginning of a line; * translate \r\n to \n for imap */static intmboxesc(Biobuf *in, Biobuf *out, int type){ char *s; int n; for(; s = Brdstr(in, '\n', 0); free(s)){ if(!strncmp(s, "From ", 5)) Bputc(out, ' '); n = strlen(s); if(n > 1 && s[n-2] == '\r'){ s[n-2] = '\n'; n--; } if(Bwrite(out, s, n) == Beof){ free(s); return -1; } if(s[n-1] != '\n') Bputc(out, '\n'); } if(type == Mbox) Bputc(out, '\n'); if(Bflush(out) == Beof) return -1; return 0;} intappendfolder(Biobuf *b, char *addr, long *t, int fd){ char *s; int r; Biobuf bin; Folder *f; Tm tm; f = getfolder(b); Bseek(f->out, 0, 2); Binit(&bin, fd, OREAD); s = Brdstr(&bin, '\n', 0); if(!s || strncmp(s, "From ", 5)) Bprint(f->out, "From %s %.28s\n", addr, ctime(*t)); else if(fromtotm(s, &tm) >= 0) *t = tm2sec(&tm); if(s) Bwrite(f->out, s, strlen(s)); free(s); r = mboxesc(&bin, f->out, f->type); return r | Bterm(&bin);} intfappendfolder(char *addr, long t, char *s, int fd){ long t0, r; Biobuf *b; b = openfolder(s, t); if(b == nil) return -1; t0 = t; r = appendfolder(b, addr, &t, fd); if(t != t0) renamefolder(b, t); r |= closefolder(b); return r;} /* * BOTCH sync with ../imap4d/mbox.c:/^okmbox */ static char *specialfile[] ={ "L.mbox", "forward", "headers", "imap.subscribed", "names", "pipefrom", "pipeto",}; static intspecial(char *s){ char *p; int i; p = strrchr(s, '/'); if(p == nil) p = s; else p++; for(i = 0; i < nelem(specialfile); i++) if(strcmp(p, specialfile[i]) == 0) return 1; return 0;} static char*mkmbpath(char *s, int n, char *user, char *mb, char *path){ char *p, *e, *r, buf[Pathlen]; if(!mb) return mboxpathbuf(s, n, user, path); e = buf+sizeof buf; p = seprint(buf, e, "%s", mb); if(r = strrchr(buf, '/')) p = r; seprint(p, e, "/%s", path); return mboxpathbuf(s, n, user, buf);} /* * fancy processing for ned: * we default to storing in $mail/f then just in $mail. */char*ffoldername(char *mb, char *user, char *rcvr){ char *p; int c, n; Dir *d; static char buf[Pathlen]; d = dirstat(mkmbpath(buf, sizeof buf, user, mb, "f/")); n = strlen(buf); if(!d || d->qid.type != QTDIR) buf[n -= 2] = 0; free(d); if(p = strrchr(rcvr, '!')) rcvr = p+1; while(n < sizeof buf-1 && (c = *rcvr++)){ if(c== '@') break; if(c == '/') c = '_'; buf[n++] = c; } buf[n] = 0; if(special(buf)){ fprint(2, "!won't overwrite %s\n", buf); return nil; } return buf;} char*foldername(char *mb, char *user, char *path){ static char buf[Pathlen]; mkmbpath(buf, sizeof buf, user, mb, path); if(special(buf)){ fprint(2, "!won't overwrite %s\n", buf); return nil; } return buf;} static intappend(Biobuf *in, Biobuf *out){ char *buf; int n, m; buf = malloc(8192); for(;;){ m = 0; n = Bread(in, buf, 8192); if(n <= 0) break; m = Bwrite(out, buf, n); if(m != n) break; } if(m != n) n = -1; else n = 1; free(buf); return n;} /* symmetry for nedmail; misnamed */intfappendfile(char*, char *target, int in){ int fd, r; Biobuf bin, out; if((fd = create(target, ORDWR|OEXCL, 0666)) == -1) return -1; Binit(&out, fd, OWRITE); Binit(&bin, in, OREAD); r = append(&bin, &out); Bterm(&bin); Bterm(&out); close(fd); return r;}
#include "common.h"
enum{
Mbox = 1,
Mdir,
};
typedef struct Folder Folder;
struct Folder{
int open;
int ofd;
int type;
Biobuf *out;
Mlock *l;
};
static Folder ftab[5];
static Folder*
getfolder(Biobuf *out)
{
int i;
Folder *f;
for(i = 0; i < nelem(ftab); i++){
f = ftab+i;
if(f->open == 0){
f->open = 1;
f->ofd = -1;
f->type = 0;
return f;
}
if(f->out == out)
return f;
}
sysfatal("folder.c:ftab too small");
return 0;
}
static int
putfolder(Folder *f)
{
int r;
r = 0;
if(f->l)
sysunlock(f->l);
if(f->out)
r |= Bterm(f->out);
if(f->ofd > 0){
close(f->ofd);
free(f->out);
}
memset(f, 0, sizeof *f);
return r;
}
static Biobuf*
mboxopen(char *s)
{
Folder *f;
f = getfolder(0);
f->l = syslock(s); /* traditional botch: ignore failure */
if((f->ofd = open(s, OWRITE)) == -1)
if((f->ofd = create(s, OWRITE|OEXCL, DMAPPEND|0600)) == -1){
putfolder(f);
return nil;
}
seek(f->ofd, 0, 2);
f->out = malloc(sizeof *f->out);
Binit(f->out, f->ofd, OWRITE);
f->type = Mbox;
return f->out;
}
/*
* sync with send/cat_mail.c:/^mdir
*/
static Biobuf*
mdiropen(char *s, long t)
{
char buf[64];
long i;
Folder *f;
f = getfolder(0);
for(i = 0; i < 100; i++){
snprint(buf, sizeof buf, "%s/%lud.%.2ld", s, t, i);
if((f->ofd = create(buf, OWRITE|OEXCL, DMAPPEND|0660)) != -1)
goto found;
}
putfolder(f);
return nil;
found:
werrstr("");
f->out = malloc(sizeof *f->out);
Binit(f->out, f->ofd, OWRITE);
f->type = Mdir;
return f->out;
}
Biobuf*
openfolder(char *s, long t)
{
int isdir;
Dir *d;
if(d = dirstat(s)){
isdir = d->mode&DMDIR;
free(d);
}else{
isdir = create(s, OREAD, DMDIR|0777);
if(isdir == -1)
return nil;
close(isdir);
isdir = 1;
}
if(isdir)
return mdiropen(s, t);
else
return mboxopen(s);
}
int
renamefolder(Biobuf *b, long t)
{
char buf[32];
int i;
Dir d;
Folder *f;
f = getfolder(b);
if(f->type != Mdir)
return 0;
for(i = 0; i < 100; i++){
nulldir(&d);
snprint(buf, sizeof buf, "%lud.%.2d", t, i);
d.name = buf;
if(dirfwstat(Bfildes(b), &d) > 0)
return 0;
}
return -1;
}
int
closefolder(Biobuf *b)
{
return putfolder(getfolder(b));
}
/*
* escape "From " at the beginning of a line;
* translate \r\n to \n for imap
*/
static int
mboxesc(Biobuf *in, Biobuf *out, int type)
{
char *s;
int n;
for(; s = Brdstr(in, '\n', 0); free(s)){
if(!strncmp(s, "From ", 5))
Bputc(out, ' ');
n = strlen(s);
if(n > 1 && s[n-2] == '\r'){
s[n-2] = '\n';
n--;
}
if(Bwrite(out, s, n) == Beof){
free(s);
return -1;
}
if(s[n-1] != '\n')
Bputc(out, '\n');
}
if(type == Mbox)
Bputc(out, '\n');
if(Bflush(out) == Beof)
return -1;
return 0;
}
int
appendfolder(Biobuf *b, char *addr, long *t, int fd)
{
char *s;
int r;
Biobuf bin;
Folder *f;
Tm tm;
f = getfolder(b);
Bseek(f->out, 0, 2);
Binit(&bin, fd, OREAD);
s = Brdstr(&bin, '\n', 0);
if(!s || strncmp(s, "From ", 5))
Bprint(f->out, "From %s %.28s\n", addr, ctime(*t));
else if(fromtotm(s, &tm) >= 0)
*t = tm2sec(&tm);
if(s)
Bwrite(f->out, s, strlen(s));
free(s);
r = mboxesc(&bin, f->out, f->type);
return r | Bterm(&bin);
}
int
fappendfolder(char *addr, long t, char *s, int fd)
{
long t0, r;
Biobuf *b;
b = openfolder(s, t);
if(b == nil)
return -1;
t0 = t;
r = appendfolder(b, addr, &t, fd);
if(t != t0)
renamefolder(b, t);
r |= closefolder(b);
return r;
}
/*
* BOTCH sync with ../imap4d/mbox.c:/^okmbox
*/
static char *specialfile[] =
{
"L.mbox",
"forward",
"headers",
"imap.subscribed",
"names",
"pipefrom",
"pipeto",
};
static int
special(char *s)
{
char *p;
int i;
p = strrchr(s, '/');
if(p == nil)
p = s;
else
p++;
for(i = 0; i < nelem(specialfile); i++)
if(strcmp(p, specialfile[i]) == 0)
return 1;
return 0;
}
static char*
mkmbpath(char *s, int n, char *user, char *mb, char *path)
{
char *p, *e, *r, buf[Pathlen];
if(!mb)
return mboxpathbuf(s, n, user, path);
e = buf+sizeof buf;
p = seprint(buf, e, "%s", mb);
if(r = strrchr(buf, '/'))
p = r;
seprint(p, e, "/%s", path);
return mboxpathbuf(s, n, user, buf);
}
/*
* fancy processing for ned:
* we default to storing in $mail/f then just in $mail.
*/
char*
ffoldername(char *mb, char *user, char *rcvr)
{
char *p;
int c, n;
Dir *d;
static char buf[Pathlen];
d = dirstat(mkmbpath(buf, sizeof buf, user, mb, "f/"));
n = strlen(buf);
if(!d || d->qid.type != QTDIR)
buf[n -= 2] = 0;
free(d);
if(p = strrchr(rcvr, '!'))
rcvr = p+1;
while(n < sizeof buf-1 && (c = *rcvr++)){
if(c== '@')
break;
if(c == '/')
c = '_';
buf[n++] = c;
}
buf[n] = 0;
if(special(buf)){
fprint(2, "!won't overwrite %s\n", buf);
return nil;
}
return buf;
}
char*
foldername(char *mb, char *user, char *path)
{
static char buf[Pathlen];
mkmbpath(buf, sizeof buf, user, mb, path);
if(special(buf)){
fprint(2, "!won't overwrite %s\n", buf);
return nil;
}
return buf;
}
static int
append(Biobuf *in, Biobuf *out)
{
char *buf;
int n, m;
buf = malloc(8192);
for(;;){
m = 0;
n = Bread(in, buf, 8192);
if(n <= 0)
break;
m = Bwrite(out, buf, n);
if(m != n)
break;
}
if(m != n)
n = -1;
else
n = 1;
free(buf);
return n;
}
/* symmetry for nedmail; misnamed */
int
fappendfile(char*, char *target, int in)
{
int fd, r;
Biobuf bin, out;
if((fd = create(target, ORDWR|OEXCL, 0666)) == -1)
return -1;
Binit(&out, fd, OWRITE);
Binit(&bin, in, OREAD);
r = append(&bin, &out);
Bterm(&bin);
Bterm(&out);
close(fd);
return r;
}