Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/root/sys/src/cmd/mail2fs/mailplumb.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include "common.h"
#include <plumb.h>
#include <ctype.h>
#include "util.h"

/*
 * Generate plumber messages as a plan b mail box
 * changes. We run upas/fs and mail2fs in a cpu server
 * and not at the user terminal. Users get their msgs
 * already processed for reading.
 */

enum {
	// for plumbmsg
	Old,
	New
};

typedef struct Msg Msg;
struct Msg{
	char*	path;
	char*	from;
	char*	date;
	Msg*	next;
	int	visited;
};

int	hopt;	// send plumb msgs for old ones
int	debug;
char*	mboxdir;	// only used by plumbmsg()
int	octopus;	// plumb using /mnt/ports/post

void
printmsgs(Msg* m)
{
	if(m == nil)
		fprint(2, "no msgs\n");
	for(; m != nil; m = m->next)
		fprint(2, "%s\tfrom %s\n", m->path, m->from);
}

static int
cmpent(void* a1, void* a2)
{
	Dir*	d1 = a1;
	Dir*	d2 = a2;
	int	n1, n2;

	n1 = atoi(mailnamenb(d1->name));
	n2 = atoi(mailnamenb(d2->name));
	return n2 - n1;
}

Msg*
newmsg(char* dir)
{
	static char buf[512];
	Msg*	m;
	char*	fname;
	int	fd;
	char*	s;
	int	nr;

	fname = smprint("%s/raw", dir);
	m = mallocz(sizeof(Msg), 1);
	if(m == nil || fname == nil)
		sysfatal("not enough memory");
	fd = open(fname, OREAD);
	if(fd < 0){
		// maybe just a new one, give it some time.
		sleep(1000);
		fd = open(fname, OREAD);
	}
	free(fname);
	if(fd < 0)
		goto fail;
	nr = read(fd, buf, sizeof(buf)-1);
	close(fd);
	if(nr <= 5 || strncmp(buf, "From ", 5))
		goto fail;
	buf[nr] = 0;
	s = strchr(buf+5, ' ');
	if(s == nil)
		goto fail;
	*s = 0;
	m->from = strdup(buf+5);
	m->path = strdup(dir);
	if(m->from == nil || m->path == nil)
		goto fail;
	return m;
fail:
	/* Message may have been archived in the
	 * mean while. In any case, this is not to
	 * be considered an error.
	 */
	if(debug)
		fprint(2, "%s: %r\n", dir);
	free(m->path);
	free(m->from);
	free(m);
	return nil;
}

void
freemsg(Msg* m)
{
	if(m != nil){
		free(m->path);
		free(m->from);
		free(m->date);
	}
	free(m);
}

int
isnew(Msg* m, Msg* msgs)
{
	for(; msgs != nil; msgs = msgs->next)
		if(strcmp(msgs->path, m->path) == 0){
			msgs->visited = 1;
			return 0;
		}
	return 1;
}
 
void
oplumbmsg(Msg* m, int what)
{
	char*	str[2] = {"gone", "new"};
	char	msg[80];
	char*	e;
	int	fd;

	e = msg+sizeof(msg);
	e = seprint(msg, e, "/msgs: %s %s %s\n", m->path, m->from, str[what]);
	fd = open("/mnt/ports/post", OWRITE);
	if(fd < 0)
		sysfatal("ports open:  %r");
	if(write(fd, msg, e-msg) != e - msg)
		sysfatal("pors write: %r");
	close(fd);
}

void
plumbmsg(Msg* m, int what)
{
	static int fd = -1;
	char*	str[2] = { "delete", "new" };
	Plumbmsg p;
	Plumbattr a[10];
	int ai;

	assert(what == 0 || what == 1);
	if(debug)
		fprint(2, "%s msg %s from %s\n", str[what], m->path, m->from);
	if(octopus){
		oplumbmsg(m, what);
		return;
	}
	memset(&p, 0, sizeof(p));
	p.src = "mailplumb";
	p.dst = "seemail";
	p.wdir = mboxdir;
	p.type = "text";

	ai = 0;
	a[ai].name = "filetype";
	a[ai].value = "mail";

	a[++ai].name = "sender";
	a[ai].value = m->from;
	a[ai-1].next = &a[ai];

	a[++ai].name = "length";
	a[ai].value = "42";
	a[ai-1].next = &a[ai];

	a[++ai].name = "mailtype";
	a[ai].value = str[what];
	a[ai-1].next = &a[ai];

	a[++ai].name = "digest";
	a[ai].value = m->path;
	a[ai-1].next = &a[ai];

	a[ai].next = nil;
	p.attr = a;
	p.ndata = strlen(m->path);
	p.data = m->path;

	if(fd < 0)
		fd = plumbopen("send", OWRITE);
	if(fd < 0)
		sysfatal("plumb: %r\n");
	if (plumbsend(fd, &p) < 0){
		fprint(2, "plumbsend: %r\n");
		close(fd);
		fd = -1;
	}
}

Msg**
readmdir(char* dir, Msg* msgs, Msg** mp, int initial)
{
	Msg*	m;
	Dir*	d;
	int	n;
	int	fd;
	int	i; 
	char*	tf;

	if(dir == nil)
		return mp;
	fd = open(dir, OREAD);
	if(fd < 0){
		fprint(2, "%s: %r\n", dir);
		return mp;
	}
	n = dirreadall(fd, &d);
	close(fd);
	if(n < 0)
		fprint(2, "%s: %r\n", dir);
	if(n <= 0)
		return mp;
	qsort(d, n, sizeof(Dir), cmpent);
	for(i = 0; i < n; i++)
		if(isdigit(d[i].name[0])){
			tf = smprint("%s/%s", dir, d[i].name);
			m = newmsg(tf);
			free(tf);
			if(m != nil){
				if(isnew(m, msgs)){
					*mp = m;
					mp = &m->next;
					m->visited = 1;
					m->date = strdup(ctime(d[i].mtime));
					m->date[strlen(m->date)-1] = 0;
					if(!initial || hopt)
						plumbmsg(m, New);
				}else
					freemsg(m);
			}
		}
	free(d);
	return mp;
}

Msg*
readmbox(char* mbox, Qid* q, Msg* old, int initial)
{
	Msg**	mp;
	Dir*	d;
	int	fd;
	int	i;
	int	n;
	char*	dir;
	Msg*	msgs;
	Msg*	m;

	msgs = old;
	for(mp = &msgs; *mp != nil; mp = &(*mp)->next)
		(*mp)->visited = 0;
	fd = open(mbox, OREAD);
	if(fd < 0)
		sysfatal("%s: %r\n", mbox);
	d = dirfstat(fd);
	if(d == nil)
		sysfatal("%s: %r\n", mbox);
	*q = d->qid;
	free(d);
	n = dirreadall(fd, &d);
	close(fd);
	if(n <= 0)
		return nil;
	qsort(d, n, sizeof(Dir), cmpent);
	for(i = 0; i < n; i++)
		if(d[i].qid.type&QTDIR){
			dir = smprint("%s/%s", mbox, d[i].name);
			mp =  readmdir(dir, msgs, mp, initial);
			free(dir);
		}
	for(mp = &msgs; (m = *mp) != nil; )
		if(!m->visited){
			*mp = m->next;
			plumbmsg(m, Old);
			freemsg(m);
		}else
			mp = &m->next;
	free(d);
	return msgs;
}

void
watchmbox(char* dir, Qid q, Msg* msgs)
{
	Dir*	d;
	Qid	nqid;

	for(;;){
		sleep(octopus ? 5000 : 20000);
		d = dirstat(dir);
		if(d == nil)
			sysfatal("%s: %r\n", dir);
		nqid = d->qid;
		free(d);
		if(octopus && nqid.path == q.path && nqid.vers == q.vers)
			continue;
		msgs = readmbox(dir, &q, msgs, 0);
	}
}

void
usage(void)
{
	fprint(2, "usage: %s [-dho] [dir]\n", argv0);
	exits("usage");
}

void
main(int argc, char*argv[])
{
	char*	dir;
	char*	top;
	Msg*	m;
	Qid	mqid;
	Msg*	msgs;


	top = smprint("/mail/box/%s", getuser());
	ARGBEGIN{
	case 'd':
		debug++;
		break;
	case 'h':
		hopt++;
		break;
	case 'o':
		octopus++;
		break;
	default:
		usage();
	}ARGEND;
	if(argc == 1)
		dir = cleanpath(argv[0], top);
	else {
		if(argc != 0)
			usage();
		dir = smprint("%s/msgs", top);
	}
	mboxdir = dir;
	msgs = readmbox(dir, &mqid, nil, 1);
	if(debug){
		fprint(2, "initial msgs:\n");
		printmsgs(msgs);
	}
	switch(rfork(RFPROC|RFNOTEG|RFFDG|RFNOWAIT)){
	case -1:
		sysfatal("fork: %r");
	case 0:
		if(hopt)
			for(m = msgs; m != nil; m = m->next)
				plumbmsg(m, New);
		watchmbox(dir, mqid, msgs);
		exits(nil);
	}
	exits(nil);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.