shithub: Nail

ref: 2d076037d8dddd20eaeb72f6ed31c8a359ce978b
dir: /mesg.c/

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <thread.h>
#include <regexp.h>

#include "mail.h"

#define Datefmt		"?WWW, ?MMM ?DD hh:mm:ss ?Z YYYY"

typedef struct Fn	Fn;

struct Fn {
	char *name;
	void (*fn)(Mesg *, char **, int);
};

void
mesgclear(Mesg *m)
{
	free(m->name);
	free(m->from);
	free(m->to);
	free(m->cc);
	free(m->replyto);
	free(m->date);
	free(m->subject);
	free(m->type);
	free(m->disposition);
	free(m->messageid);
	free(m->filename);
	free(m->digest);
	free(m->mflags);
	free(m->fromcolon);
}

void
mesgfree(Mesg *m)
{
	if(m == nil)
		return;
	mesgclear(m);
	free(m);
}

static char*
line(char *data, char **pp)
{
	char *p, *q;

	for(p=data; *p!='\0' && *p!='\n'; p++)
		;
	if(*p == '\n')
		*pp = p+1;
	else
		*pp = p;
	if(p == data)
		return nil;
	q = emalloc(p-data + 1);
	memmove(q, data, p-data);
	return q;
}

static char*
fc(Mesg *m, char *s)
{
	char *r;

	if(s != nil && m->from != nil){
		r = smprint("%s <%s>", s, m->from);
		free(s);
		return r;
	}
	if(m->from != nil)
		return estrdup(m->from);
	if(s != nil)
		return s;
	return estrdup("??");
}

Mesg*
mesgload(char *name)
{
	char *info, *p;
	int ninfo;
	Mesg *m;
	Tm tm;

	m = emalloc(sizeof(Mesg));
	m->name = estrjoin(name, "/", nil);
	if((info = rslurp(m, "info", &ninfo)) == nil)
		return nil;

	p = info;
	m->from = line(p, &p);
	m->to = line(p, &p);
	m->cc = line(p, &p);
	m->replyto = line(p, &p);
	m->date = line(p, &p);
	m->subject = line(p, &p);
	m->type = line(p, &p);
	m->disposition = line(p, &p);
	m->filename = line(p, &p);
	m->digest = line(p, &p);
	/* m->bcc = */ free(line(p, &p));
	m->inreplyto = line(p, &p);
	/* m->date = */ free(line(p, &p));
	/* m->sender = */ free(line(p, &p));
	m->messageid = line(p, &p);
	/* m->lines = */ free(line(p, &p));
	/* m->size = */ free(line(p, &p));
	m->mflags = line(p, &p);
	/* m->fileid = */ free(line(p, &p));
	m->fromcolon = fc(m, line(p, &p));
	free(info);

	m->flags = Funseen;
	if(strchr(m->mflags, 'd')) m->flags |= Fdel;
	if(strchr(m->mflags, 's')) m->flags &= ~Funseen;
	if(strchr(m->mflags, 'a')) m->flags |= Fresp;

	m->time = time(nil);
	if(tmparse(&tm, Datefmt, m->date, nil, nil) != nil)
		m->time = tmnorm(&tm);
	m->hash = 0;
	if(m->messageid != nil)
		m->hash = strhash(m->messageid);
	return m;
}

static int
mesgshow(Mesg *m)
{
	int rfd, wfd;
	char *buf, *path;
	int n;

	buf = emalloc(Bufsz);
	path = estrjoin(mbox.path, m->name, "body", nil);
	if((wfd = winopen(m, "body", OWRITE)) == -1)
		return -1;
	if((rfd = open(path, OREAD)) == -1)
		return -1;
	fprint(wfd, "From: %s\n", m->from);
	fprint(wfd, "Date: %s\n", m->to);
	fprint(wfd, "Subject: %s\n\n", m->subject);
	while(1){
		n = read(rfd, buf, Bufsz);
		if(n <= 0)
			break;
		if(write(wfd, buf, n) != n)
			break;
	}
	close(rfd);
	close(wfd);
	free(buf);
	free(path);
	return n;
}

static void
reply(Mesg *m, char **f, int nf)
{
	if(nf >= 2
	|| nf >= 1 &&  strcmp(f[0], "All") != 0){
		fprint(2, "Q: invaid args\n");
		return;
	}

	/* FIXME: get all recievers of the message */
	compose1(m->from, m, 0);
}

static void
qreply(Mesg *m, char **f, int nf)
{
	if(nf >= 3
	|| nf >= 2 && strcmp(f[1], "All") != 0
	|| nf >= 1 && strcmp(f[0], "Reply") != 0
	|| nf == 0){
		fprint(2, "Q: invaid args\n");
		return;
	}

	/* FIXME: get all recievers of the message */
	compose1(m->from, m, 1);
}

static void
mesgquit(Mesg *m, char **, int)
{
	m->quitting = 1;
}

static Fn mesgfn[] = {
	{"Q",		qreply},
	{"Reply",	reply},
//	{"Delmesg",	delmesg},
	{"Save",	nil},
	{"Del", 	mesgquit},
	{nil}
};

static void
mesgmain(void *mp)
{
	char *a, *path, *f[32];
	Event ev;
	Mesg *m;
	Fn *p;
	int nf;

	m = mp;
	m->quitting = 0;

	path = estrjoin(mbox.path, m->name, nil);
	wininit(m, path);
	free(path);

	wintagwrite(m, "Q Reply all Delmesg Save  ");
	mesgshow(m);
	fprint(m->ctl, "clean\n");
	mbox.nopen++;
	while(!m->quitting){
		if(winevent(m, &ev) != 'M')
			continue;
		fprint(2, "%s\n", ev.text);
		if(strcmp(ev.text, "Del") == 0)
			break;
		switch(ev.type){
		case 'l':
		case 'L':
			if((a = matchaddr(m, &ev)) != nil)
				compose1(a, nil, 0);
			else if(matchmesg(m, ev.text))
				mesgopen(ev.text, nil);
			else
				winsendevent(m, &ev);
			free(a);
			break;
		case 'x':
		case 'X':
			if((nf = tokenize(ev.text, f, nelem(f))) == 0)
				continue;
			for(p = mesgfn; p->name != nil; p++){
				if(strcmp(p->name, f[0]) == 0 && p->fn != nil){
					p->fn(m, &f[1], nf - 1);
					break;
				}
			}
			if(p->fn == nil)
				winsendevent(m, &ev);
			break;
		}
	}
	mbox.nopen--;
	m->flags &= ~Fopen;
	winclose(m);
	threadexits(nil);
}

void
mesgpath2name(char *buf, int nbuf, char *name)
{
	char *e;
	int n;

	n = strlen(mbox.path);
	if(strncmp(name, mbox.path, n) == 0){
		e = strecpy(buf, buf + nbuf - 2, name + n);
		e[0] = '/';
		e[1] = 0;
	}else
		strecpy(buf, buf+nbuf, name);
}

int
mesgmatch(Mesg *m, char *name, char *digest)
{
	if(m->flags & Fdummy)
		return 0;
	if(strcmp(m->name, name) == 0)
		return digest == nil || strcmp(m->digest, digest) == 0;
	return 0;
}

Mesg*
mesglookup(char *name, char *digest)
{
	char buf[32];
	int i;

	mesgpath2name(buf, sizeof(buf), name);
	for(i = 0; i < mbox.nmesg; i++)
		if(mesgmatch(mbox.mesg[i], buf, digest))
			return mbox.mesg[i];
	return nil;
}

void
mesgopen(char *name, char *digest)
{
	Mesg *m;
	char *path;
	int fd;

	m = mesglookup(name, digest);
	if(m == nil || (m->flags & Fopen))
		return;
	assert(!(m->flags & Fdummy));
	m->flags |= Fopen;
	if(m->flags & Funseen){
		m->flags &= ~Funseen;
		path = estrjoin(mbox.path, "/", m->name, "/flags", nil);
		if((fd = open(path, OWRITE)) != -1){
			fprint(fd, "+s");
			close(fd);
		}
		mbredraw(m, 0, 0);
		free(path);
	}
	threadcreate(mesgmain, m, Stack);
}