/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <bio.h>#include <auth.h>#include <libsec.h>#include "imap4d.h" static int saveMsg(char *dst, char *digest, int flags, char *head, int nhead, Biobuf *b, long n);static int saveb(int fd, DigestState *dstate, char *buf, int nr, int nw);static long appSpool(Biobuf *bout, Biobuf *bin, long n); /* * check if the message exists */intcopyCheck(Box *box, Msg *m, int uids, void *v){ int fd; USED(box); USED(uids); USED(v); if(m->expunged) return 0; fd = msgFile(m, "raw"); if(fd < 0){ msgDead(m); return 0; } close(fd); return 1;} intcopySave(Box *box, Msg *m, int uids, void *vs){ Dir *d; Biobuf b; vlong length; char *head; int ok, hfd, bfd, nhead; USED(box); USED(uids); if(m->expunged) return 0; hfd = msgFile(m, "unixheader"); if(hfd < 0){ msgDead(m); return 0; } head = readFile(hfd); if(head == nil){ close(hfd); return 0; } /* * clear out the header if it doesn't end in a newline, * since it is a runt and the "header" will show up in the raw file. */ nhead = strlen(head); if(nhead > 0 && head[nhead-1] != '\n') nhead = 0; bfd = msgFile(m, "raw"); close(hfd); if(bfd < 0){ msgDead(m); return 0; } d = dirfstat(bfd); if(d == nil){ close(bfd); return 0; } length = d->length; free(d); Binit(&b, bfd, OREAD); ok = saveMsg(vs, m->info[IDigest], m->flags, head, nhead, &b, length); Bterm(&b); close(bfd); return ok;} /* * first spool the input into a temorary file, * and massage the input in the process. * then save to real box. */intappendSave(char *mbox, int flags, char *head, Biobuf *b, long n){ Biobuf btmp; int fd, ok; fd = imapTmp(); if(fd < 0) return 0; Bprint(&bout, "+ Ready for literal data\r\n"); if(Bflush(&bout) < 0) writeErr(); Binit(&btmp, fd, OWRITE); n = appSpool(&btmp, b, n); Bterm(&btmp); if(n < 0){ close(fd); return 0; } seek(fd, 0, 0); Binit(&btmp, fd, OREAD); ok = saveMsg(mbox, nil, flags, head, strlen(head), &btmp, n); Bterm(&btmp); close(fd); return ok;} /* * copy from bin to bout, * mapping "\r\n" to "\n" and "\nFrom " to "\n From " * return the number of bytes in the mapped file. * * exactly n bytes must be read from the input, * unless an input error occurs. */static longappSpool(Biobuf *bout, Biobuf *bin, long n){ int i, c; c = '\n'; while(n > 0){ if(c == '\n' && n >= STRLEN("From ")){ for(i = 0; i < STRLEN("From "); i++){ c = Bgetc(bin); if(c != "From "[i]){ if(c < 0) return -1; Bungetc(bin); break; } n--; } if(i == STRLEN("From ")) Bputc(bout, ' '); Bwrite(bout, "From ", i); } c = Bgetc(bin); n--; if(c == '\r' && n-- > 0){ c = Bgetc(bin); if(c != '\n') Bputc(bout, '\r'); } if(c < 0) return -1; if(Bputc(bout, c) < 0) return -1; } if(c != '\n') Bputc(bout, '\n'); if(Bflush(bout) < 0) return -1; return Boffset(bout);} static intsaveMsg(char *dst, char *digest, int flags, char *head, int nhead, Biobuf *b, long n){ DigestState *dstate; MbLock *ml; uchar shadig[SHA1dlen]; char buf[BufSize + 1], digbuf[NDigest + 1]; int i, fd, nr, nw, ok; ml = mbLock(); if(ml == nil) return 0; fd = openLocked(mboxDir, dst, OWRITE); if(fd < 0){ mbUnlock(ml); return 0; } seek(fd, 0, 2); dstate = nil; if(digest == nil) dstate = sha1(nil, 0, nil, nil); if(!saveb(fd, dstate, head, nhead, nhead)){ if(dstate != nil) sha1(nil, 0, shadig, dstate); mbUnlock(ml); close(fd); return 0; } ok = 1; if(n == 0) ok = saveb(fd, dstate, "\n", 0, 1); while(n > 0){ nr = n; if(nr > BufSize) nr = BufSize; nr = Bread(b, buf, nr); if(nr <= 0){ saveb(fd, dstate, "\n\n", 0, 2); ok = 0; break; } n -= nr; nw = nr; if(n == 0){ if(buf[nw - 1] != '\n') buf[nw++] = '\n'; buf[nw++] = '\n'; } if(!saveb(fd, dstate, buf, nr, nw)){ ok = 0; break; } mbLockRefresh(ml); } close(fd); if(dstate != nil){ digest = digbuf; sha1(nil, 0, shadig, dstate); for(i = 0; i < SHA1dlen; i++) snprint(digest+2*i, NDigest+1-2*i, "%2.2ux", shadig[i]); } if(ok){ fd = cdOpen(mboxDir, impName(dst), OWRITE); if(fd < 0) fd = emptyImp(dst); if(fd >= 0){ seek(fd, 0, 2); wrImpFlags(buf, flags, 1); fprint(fd, "%.*s %.*lud %s\n", NDigest, digest, NUid, 0UL, buf); close(fd); } } mbUnlock(ml); return 1;} static intsaveb(int fd, DigestState *dstate, char *buf, int nr, int nw){ if(dstate != nil) sha1((uchar*)buf, nr, nil, dstate); if(write(fd, buf, nw) != nw) return 0; return 1;}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include <libsec.h>
#include "imap4d.h"
static int saveMsg(char *dst, char *digest, int flags, char *head, int nhead, Biobuf *b, long n);
static int saveb(int fd, DigestState *dstate, char *buf, int nr, int nw);
static long appSpool(Biobuf *bout, Biobuf *bin, long n);
/*
* check if the message exists
*/
int
copyCheck(Box *box, Msg *m, int uids, void *v)
{
int fd;
USED(box);
USED(uids);
USED(v);
if(m->expunged)
return 0;
fd = msgFile(m, "raw");
if(fd < 0){
msgDead(m);
return 0;
}
close(fd);
return 1;
}
int
copySave(Box *box, Msg *m, int uids, void *vs)
{
Dir *d;
Biobuf b;
vlong length;
char *head;
int ok, hfd, bfd, nhead;
USED(box);
USED(uids);
if(m->expunged)
return 0;
hfd = msgFile(m, "unixheader");
if(hfd < 0){
msgDead(m);
return 0;
}
head = readFile(hfd);
if(head == nil){
close(hfd);
return 0;
}
/*
* clear out the header if it doesn't end in a newline,
* since it is a runt and the "header" will show up in the raw file.
*/
nhead = strlen(head);
if(nhead > 0 && head[nhead-1] != '\n')
nhead = 0;
bfd = msgFile(m, "raw");
close(hfd);
if(bfd < 0){
msgDead(m);
return 0;
}
d = dirfstat(bfd);
if(d == nil){
close(bfd);
return 0;
}
length = d->length;
free(d);
Binit(&b, bfd, OREAD);
ok = saveMsg(vs, m->info[IDigest], m->flags, head, nhead, &b, length);
Bterm(&b);
close(bfd);
return ok;
}
/*
* first spool the input into a temorary file,
* and massage the input in the process.
* then save to real box.
*/
int
appendSave(char *mbox, int flags, char *head, Biobuf *b, long n)
{
Biobuf btmp;
int fd, ok;
fd = imapTmp();
if(fd < 0)
return 0;
Bprint(&bout, "+ Ready for literal data\r\n");
if(Bflush(&bout) < 0)
writeErr();
Binit(&btmp, fd, OWRITE);
n = appSpool(&btmp, b, n);
Bterm(&btmp);
if(n < 0){
close(fd);
return 0;
}
seek(fd, 0, 0);
Binit(&btmp, fd, OREAD);
ok = saveMsg(mbox, nil, flags, head, strlen(head), &btmp, n);
Bterm(&btmp);
close(fd);
return ok;
}
/*
* copy from bin to bout,
* mapping "\r\n" to "\n" and "\nFrom " to "\n From "
* return the number of bytes in the mapped file.
*
* exactly n bytes must be read from the input,
* unless an input error occurs.
*/
static long
appSpool(Biobuf *bout, Biobuf *bin, long n)
{
int i, c;
c = '\n';
while(n > 0){
if(c == '\n' && n >= STRLEN("From ")){
for(i = 0; i < STRLEN("From "); i++){
c = Bgetc(bin);
if(c != "From "[i]){
if(c < 0)
return -1;
Bungetc(bin);
break;
}
n--;
}
if(i == STRLEN("From "))
Bputc(bout, ' ');
Bwrite(bout, "From ", i);
}
c = Bgetc(bin);
n--;
if(c == '\r' && n-- > 0){
c = Bgetc(bin);
if(c != '\n')
Bputc(bout, '\r');
}
if(c < 0)
return -1;
if(Bputc(bout, c) < 0)
return -1;
}
if(c != '\n')
Bputc(bout, '\n');
if(Bflush(bout) < 0)
return -1;
return Boffset(bout);
}
static int
saveMsg(char *dst, char *digest, int flags, char *head, int nhead, Biobuf *b, long n)
{
DigestState *dstate;
MbLock *ml;
uchar shadig[SHA1dlen];
char buf[BufSize + 1], digbuf[NDigest + 1];
int i, fd, nr, nw, ok;
ml = mbLock();
if(ml == nil)
return 0;
fd = openLocked(mboxDir, dst, OWRITE);
if(fd < 0){
mbUnlock(ml);
return 0;
}
seek(fd, 0, 2);
dstate = nil;
if(digest == nil)
dstate = sha1(nil, 0, nil, nil);
if(!saveb(fd, dstate, head, nhead, nhead)){
if(dstate != nil)
sha1(nil, 0, shadig, dstate);
mbUnlock(ml);
close(fd);
return 0;
}
ok = 1;
if(n == 0)
ok = saveb(fd, dstate, "\n", 0, 1);
while(n > 0){
nr = n;
if(nr > BufSize)
nr = BufSize;
nr = Bread(b, buf, nr);
if(nr <= 0){
saveb(fd, dstate, "\n\n", 0, 2);
ok = 0;
break;
}
n -= nr;
nw = nr;
if(n == 0){
if(buf[nw - 1] != '\n')
buf[nw++] = '\n';
buf[nw++] = '\n';
}
if(!saveb(fd, dstate, buf, nr, nw)){
ok = 0;
break;
}
mbLockRefresh(ml);
}
close(fd);
if(dstate != nil){
digest = digbuf;
sha1(nil, 0, shadig, dstate);
for(i = 0; i < SHA1dlen; i++)
snprint(digest+2*i, NDigest+1-2*i, "%2.2ux", shadig[i]);
}
if(ok){
fd = cdOpen(mboxDir, impName(dst), OWRITE);
if(fd < 0)
fd = emptyImp(dst);
if(fd >= 0){
seek(fd, 0, 2);
wrImpFlags(buf, flags, 1);
fprint(fd, "%.*s %.*lud %s\n", NDigest, digest, NUid, 0UL, buf);
close(fd);
}
}
mbUnlock(ml);
return 1;
}
static int
saveb(int fd, DigestState *dstate, char *buf, int nr, int nw)
{
if(dstate != nil)
sha1((uchar*)buf, nr, nil, dstate);
if(write(fd, buf, nw) != nw)
return 0;
return 1;
}