shithub: fork

ref: 59fe51f5cbb81a135734c693ab51a5b243534c92
dir: /sys/src/cmd/upas/fs/chkidx.c/

View raw version
#include "common.h"
#include <auth.h>
#include <libsec.h>
#include <bin.h>
#include "dat.h"

#define idprint(...)	if(1) fprint(2, __VA_ARGS__); else {}
enum{
	Maxver	= 10,
};
static char *magictab[Maxver] = {
[4]	"idx magic v4\n",
[7]	"idx magic v7\n",
};
static int fieldstab[Maxver] = {
[4]	19,
[7]	21,
};

static	char	*magic;
static	int	Idxfields;
static	int	lineno;
static	int	idxver;

int
newid(void)
{
	static int id;

	return ++id;
}

void*
emalloc(ulong n)
{
	void *p;

	p = mallocz(n, 1);
	if(!p)
		sysfatal("malloc %lud: %r", n);
	setmalloctag(p, getcallerpc(&n));
	return p;
}
	
static int
Afmt(Fmt *f)
{
	char buf[SHA1dlen*2 + 1];
	uchar *u, i;

	u = va_arg(f->args, uchar*);
	if(u == 0 && f->flags & FmtSharp)
		return fmtstrcpy(f, "-");
	if(u == 0)
		return fmtstrcpy(f, "<nildigest>");
	for(i = 0; i < SHA1dlen; i++)
		sprint(buf + 2*i, "%2.2ux", u[i]);
	return fmtstrcpy(f, buf);
}

static int
Dfmt(Fmt *f)
{
	char buf[32];
	int seq;
	uvlong v;

	v = va_arg(f->args, uvlong);
	seq = v & 0xff;
	if(seq > 99)
		seq = 99;
	snprint(buf, sizeof buf, "%llud.%.2d", v>>8, seq);
	return fmtstrcpy(f, buf);
}

static Mailbox*
shellmailbox(char *path)
{
	Mailbox *mb;

	mb = malloc(sizeof *mb);
	if(mb == 0)
		sysfatal("malloc");
	memset(mb, 0, sizeof *mb);
	snprint(mb->path, sizeof mb->path, "%s", path);
	mb->id = newid();
	mb->root = newmessage(nil);
	mb->mtree = mkavltree(mtreecmp);
	return mb;
}

void
shellmailboxfree(Mailbox*)
{
}

Message*
newmessage(Message *parent)
{
	static int id;
	Message *m;

//	msgallocd++;

	m = mallocz(sizeof *m, 1);
	if(m == 0)
		sysfatal("malloc");
	m->disposition = Dnone;
//	m->type = newrefs("text/plain");
//	m->charset = newrefs("iso-8859-1");
	m->cstate = Cidxstale;
	m->flags = Frecent;
	m->id = newid();
	if(parent)
		snprint(m->name, sizeof m->name, "%d", ++(parent->subname));
	if(parent == nil)
		parent = m;
	m->whole = parent;
	m->hlen = -1;
	return m;
}

void
unnewmessage(Mailbox *mb, Message *parent, Message *m)
{
	assert(parent->subname > 0);
//	delmessage(mb, m);
	USED(mb, m);
	parent->subname -= 1;
}


static int
validmessage(Mailbox *mb, Message *m, int level)
{
	if(level){
		if(m->digest != 0)
			goto lose;
		if(m->fileid <= 1000000ull<<8)
		if(m->fileid != 0)
			goto lose;
	}else{
		if(m->digest == 0)
			goto lose;
		if(m->size == 0)
			goto lose;
		if(m->fileid <= 1000000ull<<8)
			goto lose;
		if(mtreefind(mb, m->digest))
			goto lose;
	}
	return 1;
lose:
	fprint(2, "invalid cache[%d] %#A size %ld %D\n", level, m->digest, m->size, m->fileid);
	return 0;
}

static char*
∫(char *x)
{
	if(x && *x)
		return x;
	return nil;
}

static char*
brdstr(Biobuf *b, int c, int eat)
{
	char *s;

	s = Brdstr(b, c, eat);
	if(s)
		lineno++;
	return s;
}

static int
nibble(int c)
{
	if(c >= '0' && c <= '9')
		return c - '0';
	if(c < 0x20)
		c += 0x20;
	if(c >= 'a' && c <= 'f')
		return c - 'a'+10;
	return 0xff;
}

static uchar*
hackdigest(char *s)
{
	uchar t[SHA1dlen];
	int i;

	if(strcmp(s, "-") == 0)
		return 0;
	if(strlen(s) != 2*SHA1dlen){
		fprint(2, "bad digest %s\n", s);
		return 0;
	}
	for(i = 0; i < SHA1dlen; i++)
		t[i] = nibble(s[2*i])<<4 | nibble(s[2*i + 1]);
	memmove(s, t, SHA1dlen);
	return (uchar*)s;
}

static Message*
findmessage(Mailbox *, Message *parent, int n)
{
	Message *m;

	for(m = parent->part; m; m = m->next)
		if(!m->digest && n-- == 0)
			return m;
	return 0;
}

static uvlong
rdfileid(char *s, int level)
{
	char *p;
	uvlong uv;

	uv = strtoul(s, &p, 0);
	if((level == 0 && uv < 1000000) || *p != '.')
		return 0;
	return uv<<8 | strtoul(p + 1, 0, 10);
}

static int
rdidx(Biobuf *b, Mailbox *mb, Message *parent, int npart, int level)
{
	char *f[50 + 1], *s;
	uchar *digest;
	int n, nparts, good, bad, redux;
	Message *m, **ll, *l;

	bad = good = redux = 0;
	ll = &parent->part;
	nparts = npart;
	for(; npart != 0 && (s = brdstr(b, '\n', 1)); npart--){
//if(lineno>18&&lineno<25)idprint("%d: %d [%s]\n", lineno, level, s);
		n = tokenize(s, f, nelem(f));
		if(n != Idxfields){
			print("%d: bad line\n", lineno);
			bad++;
			free(s);
			continue;
		}
		digest = hackdigest(f[0]);
		if(level == 0){
			if(digest == 0)
				idprint("%d: no digest\n", lineno);
			m = mtreefind(mb, digest);
		}else{
			m = findmessage(mb, parent, nparts - npart);
			if(m == 0){
			//	idprint("can't find message\n");
			}
		}
		if(m){
			/*
			 * read in mutable information.
			 * currently this is only flags
			 */
			idprint("%d seen before %d... %.2ux", level, m->id, m->cstate);
			redux++;
			m->flags |= strtoul(f[1], 0, 16);
			m->cstate &= ~Cidxstale;
			m->cstate |= Cidx;
			idprint("→%.2ux\n", m->cstate);
			free(s);

			if(m->nparts)
				rdidx(b, mb, m, m->nparts, level + 1);
			ll = &m->next;
			continue;
		}
		m = newmessage(parent);
//if(lineno>18&&lineno<25)idprint("%d: %d %d %A\n", lineno, level, m->id, digest);
//		idprint("%d new %d %#A \n", level, m->id, digest);
		m->digest = digest;
		m->flags = strtoul(f[1], 0, 16);
		m->fileid = rdfileid(f[2], level);
		m->lines = atoi(f[3]);
		m->ffrom = ∫(f[4]);
		m->from = ∫(f[5]);
		m->to = ∫(f[6]);
		m->cc = ∫(f[7]);
		m->bcc = ∫(f[8]);
		m->replyto = ∫(f[9]);
		m->messageid = ∫(f[10]);
		m->subject = ∫(f[11]);
		m->sender = ∫(f[12]);
		m->inreplyto = ∫(f[13]);
//		m->type = newrefs(f[14]);
		m->disposition = atoi(f[15]);
		m->size = strtoul(f[16], 0, 0);
		m->rawbsize = strtoul(f[17], 0, 0);
		switch(idxver){
		case 4:
			m->nparts = strtoul(f[18], 0, 0);
		case 7:
			m->ibadchars = strtoul(f[18], 0, 0);
			m->idxaux = ∫(f[19]);
			m->nparts = strtoul(f[20], 0, 0);
		}
		m->cstate &= ~Cidxstale;
		m->cstate |= Cidx;
		m->str = s;
//		free(s);
		if(!validmessage(mb, m, level)){
			/*
			 *  if this was an okay message, and somebody
			 * wrote garbage to the index file, we lose the msg.
			 */
			print("%d: !validmessage\n", lineno);
			bad++;
			unnewmessage(mb, parent, m);
			continue;
		}
		if(level == 0)
			m->inmbox = 1;
//		cachehash(mb, m);		/* hokey */
		l = *ll;
		*ll = m;
		ll = &m->next;
		*ll = l;
		good++;

		if(m->nparts){
//			fprint(2, "%d: %d parts [%s]\n", lineno, m->nparts, f[18]);
			rdidx(b, mb, m, m->nparts, level + 1);
		}
	}
	if(level == 0)
		print("idx: %d %d %d\n", good, bad, redux);
	return 0;
}

static int
verscmp(Biobuf *b)
{
	char *s;
	int i;

	if((s = brdstr(b, '\n', 0)) == 0)
		return -1;
	for(i = 0; i < Maxver; i++)
		if(magictab[i])
		if(strcmp(s, magictab[i]) == 0)
			break;
	free(s);
	if(i == Maxver)
		return -1;
	idxver = i;
	magic = magictab[i];
	Idxfields = fieldstab[i];
	fprint(2, "version %d\n", i);
	return 0;
}

int
mbvers(Biobuf *b)
{
	char *s;

	if(s = brdstr(b, '\n', 1)){
		free(s);
		return 0;
	}
	return -1;
}

int
ckidxfile(Mailbox *mb)
{
	char buf[Pathlen + 4];
	int r;
	Biobuf *b;

	snprint(buf, sizeof buf, "%s", mb->path);
	b = Bopen(buf, OREAD);
	if(b == nil)
		return -1;
	if(verscmp(b) == -1)
		return -1;
	if(idxver >= 7)
		mbvers(b);
	r = rdidx(b, mb, mb->root, -1, 0);
	Bterm(b);
	return r;
}

static char *bargv[] = {"/fd/0", 0};

void
main(int argc, char **argv)
{
	Mailbox *mb;

	fmtinstall('A', Afmt);
	fmtinstall('D', Dfmt);
	ARGBEGIN{
	}ARGEND
	if(*argv == 0)
		argv = bargv;
	for(; *argv; argv++){
		mb = shellmailbox(*argv);
		lineno = 0;
		if(ckidxfile(mb) == -1)
			fprint(2, "%s: %r\n", *argv);
		shellmailboxfree(mb);
	}
	exits("");
}