#include "imap4d.h"
#include <libsec.h>

int
copycheck(Box*, Msg *m, int, void *)
{
	int fd;

	if(m->expunged)
		return 0;
	fd = msgfile(m, "rawunix");
	if(fd < 0){
		msgdead(m);
		return 0;
	}
	close(fd);
	return 1;
}

static int
opendeliver(int *pip, char *folder, char *from, long t)
{
	char *av[7], buf[32];
	int i, pid, fd[2];

	if(pipe(fd) != 0)
		sysfatal("pipe: %r");
	pid = fork();
	switch(pid){
	case -1:
		return -1;
	case 0:
		av[0] = "mbappend";
		av[1] = folder;
		i = 2;
		if(from){
			av[i++] = "-f";
			av[i++] = from;
		}
		if(t != 0){
			snprint(buf, sizeof buf, "%ld", t);
			av[i++] = "-t";
			av[i++] = buf;
		}
		av[i] = 0;
		close(0);
		dup(fd[1], 0);
		if(fd[1] != 0)
			close(fd[1]);
		close(fd[0]);
		exec("/bin/upas/mbappend", av);
		ilog("exec: %r");
		_exits("b0rked");
	default:
		*pip = fd[0];
		close(fd[1]);
		return pid;
	}
}

static int
closedeliver(int pid, int fd)
{
	int nz, wpid;
	Waitmsg *w;

	close(fd);
	while(w = wait()){
		nz = !w->msg || !w->msg[0];
		wpid = w->pid;
		free(w);
		if(wpid == pid)
			return nz? 0: -1;
	}
	return -1;
}

/*
 * we're going to all this trouble of fiddling the .imp file for
 * the target mailbox because we wish to save the flags.  we
 * should be using upas/fs's flags instead.
 *
 * note.  appendmb for mbox fmt wants to lock the directory.  
 * since the locking is intentionally broken, we could get by
 * with aquiring the lock before we fire up appendmb and
 * trust that he doesn't worry if he does acquire the lock.
 * instead, we'll just do locking around the .imp file.
 */
static int
savemsg(char *dst, int flags, char *head, int nhead, Biobuf *b, long n, Uidplus *u)
{
	char *digest, buf[Bufsize + 1], digbuf[Ndigest + 1], folder[Pathlen];
	uchar shadig[SHA1dlen];
	int i, fd, pid, nr, ok;
	DigestState *dstate;
	Mblock *ml;

	snprint(folder, sizeof folder, "%s/%s", mboxdir, dst);
	pid = opendeliver(&fd, folder, 0, 0);
	if(pid == -1)
		return 0;
	ok = 1;
	dstate = sha1(nil, 0, nil, nil);
	if(nhead){
		sha1((uchar*)head, nhead, nil, dstate);
		if(write(fd, head, nhead) != nhead){
			ok = 0;
			goto loose;
		}
	}
	while(n > 0){
		nr = n;
		if(nr > Bufsize)
			nr = Bufsize;
		nr = Bread(b, buf, nr);
		if(nr <= 0){
			ok = 0;
			break;
		}
		n -= nr;
		sha1((uchar*)buf, nr, nil, dstate);
		if(write(fd, buf, nr) != nr){
			ok = 0;
			break;
		}
	}
loose:
	closedeliver(pid, fd);
	sha1(nil, 0, shadig, dstate);
	if(ok){
		digest = digbuf;
		for(i = 0; i < SHA1dlen; i++)
			sprint(digest + 2*i, "%2.2ux", shadig[i]);
		ml = mblock();
		if(ml == nil)
			return 0;
		ok = appendimp(dst, digest, flags, u) == 0;
		mbunlock(ml);
	}
	return ok;
}

static int
copysave(Box*, Msg *m, int, void *vs, Uidplus *u)
{
	int ok, fd;
	vlong length;
	Biobuf b;
	Dir *d;

	if(m->expunged)
		return 0;
	if((fd = msgfile(m, "rawunix")) == -1){
		msgdead(m);
		return 0;
	}
	if((d = dirfstat(fd)) == nil){
		close(fd);
		return 0;
	}
	length = d->length;
	free(d);

	Binit(&b, fd, OREAD);
	ok = savemsg(vs, m->flags, 0, 0, &b, length, u);
	Bterm(&b);
	close(fd);
	return ok;
}

int
copysaveu(Box *box, Msg *m, int i, void *vs)
{
	int ok;
	Uidplus *u;

	u = binalloc(&parsebin, sizeof *u, 1);
	ok = copysave(box, m, i, vs, u);
	*uidtl = u;
	uidtl = &u->next;
	return ok;
}


/*
 * first spool the input into a temorary file,
 * and massage the input in the process.
 * then save to real box.
 */
/*
 * copy from bin to bout,
 * map "\r\n" to "\n" and
 * 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
spool(Biobuf *bout, Biobuf *bin, long n)
{
	int c;

	while(n > 0){
		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(Bflush(bout) < 0)
		return -1;
	return Boffset(bout);
}

int
appendsave(char *mbox, int flags, char *head, Biobuf *b, long n, Uidplus *u)
{
	int fd, ok;
	Biobuf btmp;

	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 = spool(&btmp, b, n);
	Bterm(&btmp);
	if(n < 0){
		close(fd);
		return 0;
	}

	seek(fd, 0, 0);
	Binit(&btmp, fd, OREAD);
	ok = savemsg(mbox, flags, head, strlen(head), &btmp, n, u);
	Bterm(&btmp);
	close(fd);
	return ok;
}
