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

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


#include <u.h>
#include <libc.h>
#include <thread.h>
#include <omero.h>
#include <error.h>
#include <plumb.h>
#include <regexp.h>
#include <ctype.h>
#include <b.h>

typedef struct Face Face;

struct Face {
	char*	key;
	char*	addr;
	char*	file;
	Panel*	col;
	Panel*	tag;
	Panel*	img;
	int	reclaim;	// for scandir
};

#define dprint if(debug)fprint

int	debug;
Channel*plumbc;
Panel*	grow;
Face**	faces;
int	nfaces;
int	maxfaces = 32;
char*	voiceinmsg;
char*	voiceoutmsg;

static char*
translatedomain(char *dom)
{
	static char buf[200];
	char *p, *ep, *q, *nextp, *file;
	char *bbuf, *ebuf;
	Reprog *exp;

	file = readfstr("/lib/face/.machinelist");
	if (file == nil)
		return dom;

	for(p=file; p; p=nextp) {
		if(nextp = strchr(p, '\n'))
			*nextp++ = '\0';

		if(*p == '#' || (q = strpbrk(p, " \t")) == nil || q-p > sizeof(buf)-2)
			continue;

		bbuf = buf+1;
		ebuf = buf+(1+(q-p));
		strncpy(bbuf, p, ebuf-bbuf);
		*ebuf = 0;
		if(*bbuf != '^')
			*--bbuf = '^';
		if(ebuf[-1] != '$') {
			*ebuf++ = '$';
			*ebuf = 0;
		}

		if((exp = regcomp(bbuf)) == nil){
			fprint(2, "bad regexp in machinelist: %s\n", bbuf);
			sysfatal("regexp");
		}

		if(regexec(exp, dom, 0, 0)){
			free(exp);
			ep = p+strlen(p);
			q += strspn(q, " \t");
			if(ep-q+2 > sizeof buf) {
				fprint(2, "huge replacement in machinelist: %.*s\n", utfnlen(q, ep-q), q);
				sysfatal("bad big replacement");
			}
			strncpy(buf, q, ep-q);
			ebuf = buf+(ep-q);
			*ebuf = 0;
			while(ebuf > buf && (ebuf[-1] == ' ' || ebuf[-1] == '\t'))
				*--ebuf = 0;
			free(file);
			return buf;
		}
		free(exp);
	}
	free(file);

	return dom;

}

static char*
tryfindpicture_user(char *dom, char *user, int depth)
{
	static char buf[200];
	char *p, *q, *nextp, *file, *usr;
	usr = getuser();

	sprint(buf, "/usr/%s/lib/face/48x48x%d/.dict", usr, depth);
	if((file = readfstr(buf)) == nil)
		return nil;

	snprint(buf, sizeof buf, "%s/%s", dom, user);

	for(p=file; p; p=nextp) {
		if(nextp = strchr(p, '\n'))
			*nextp++ = '\0';

		if(*p == '#' || (q = strpbrk(p, " \t")) == nil)
			continue;
		*q++ = 0;

		if(strcmp(buf, p) == 0) {
			q += strspn(q, " \t");
			q = buf+snprint(buf, sizeof buf, "/usr/%s/lib/face/48x48x%d/%s", usr, depth, q);
			while(q > buf && (q[-1] == ' ' || q[-1] == '\t'))
				*--q = 0;
			free(file);
			return buf;
		}
	}
	free(file);
	return nil;			
}

static char*
tryfindpicture_global(char *dom, char *user, int depth)
{
	static char buf[200];
	char *p, *q, *nextp, *file;

	sprint(buf, "/lib/face/48x48x%d/.dict", depth);
	if((file = readfstr(buf)) == nil)
		return nil;

	snprint(buf, sizeof buf, "%s/%s", dom, user);

	for(p=file; p; p=nextp) {
		if(nextp = strchr(p, '\n'))
			*nextp++ = '\0';

		if(*p == '#' || (q = strpbrk(p, " \t")) == nil)
			continue;
		*q++ = 0;

		if(strcmp(buf, p) == 0) {
			q += strspn(q, " \t");
			q = buf+snprint(buf, sizeof buf, "/lib/face/48x48x%d/%s", depth, q);
			while(q > buf && (q[-1] == ' ' || q[-1] == '\t'))
				*--q = 0;
			free(file);
			return buf;
		}
	}
	free(file);
	return nil;			
}

static char*
tryfindpicture(char *dom, char *user, int depth)
{
	char* result;

	if((result = tryfindpicture_user(dom, user, depth)) != nil)
		return result;

	return tryfindpicture_global(dom, user, depth);
}

static char*
tryfindfile(char *dom, char *user, int depth)
{
	char *p, *q;

	for(;;){
		for(p=dom; p; (p=strchr(p, '.')) && p++)
			if(q = tryfindpicture(p, user, depth))
				return q;
		depth >>= 1;
		if(depth == 0)
			break;
	}
	return nil;
}

void
parseaddr(char* sender, char** dp, char** up)
{
	char*	dom;
	char*	user;
	char *at, *bang;
	char *p;


	/* works with UTF-8, although it's written as ASCII */
	for(p=sender; *p!='\0'; p++)
		*p = tolower(*p);
	user = sender;
	dom = nil;
	at = strchr(sender, '@');
	if(at){
		*at++ = '\0';
		dom = at; // estrdup(at);
		goto done;
	}
	bang = strchr(sender, '!');
	if(bang){
		*bang++ = '\0';
		user = bang; // estrdup(bang);
		dom = sender;
	}
done:
	*dp = dom;
	*up = user;
}

char*
dblookup(char* a)
{
	static char	*facedom;
	char*	dom;
	char*	user;
	char*	p;
	static char addr[80];

	strecpy(addr, addr+80, a);
	parseaddr(addr, &dom, &user);

	if(facedom == nil){
		facedom = getenv("facedom");
		if(facedom == nil)
			facedom = "astro";
	}
	if(dom == nil)
		dom = facedom;
	dom = translatedomain(dom);
	dprint(2,"dom %s\n", dom);
	if(p = tryfindfile(dom, user, 8))
		return p;
	p = tryfindfile(dom, "unknown", 8);
	if(p != nil || strcmp(dom, facedom)==0)
		return p;
	p =  tryfindfile("unknown", "unknown", 8);
	if (!p)
		return "/dev/null";
	else
		return p;
}

void
omerogone(void)
{
	fprint(2, "%s: terminated\n", argv0);
	threadexitsall(nil);
}

void
freeface(Face* f)
{
	free(f->key);
	free(f->addr);
	free(f->file);
	openpanelctl(grow);
	panelctl(grow, "hold");
	if (f->img)
		removepanel(f->img);
	if (f->tag)
		removepanel(f->tag);
	if (f->col)
		removepanel(f->col);
	closepanelctl(grow);
	free(f);
}

Face*
newface(char* addr, char* key)
{
	int	i;
	int	pos = -1;
	Face*	f;

	for (i = 0; i < nfaces; i++){
		if (faces[i] == nil)
			pos = i;
		else if (key && !strcmp(faces[i]->key, key))
			return nil;	// already there
	}
	if (pos < 0){
		if ((nfaces%32) == 0)
			faces = erealloc(faces, (nfaces+32)*sizeof(Face*));
		pos = nfaces++;
	}
	f = faces[pos] = emalloc(sizeof(Face));
	f->key = key ? estrdup(key) : nil;
	f->addr = estrdup(addr);
	f->file = estrdup(dblookup(addr));
	f->reclaim = 0; // for scandir
	dprint(2,"new face addr %s key %s file %s\n", f->addr, f->key, f->file);
	if (f->file == nil){
		faces[pos] = nil;
		freeface(f);
		f = nil;
	}
	return f;
}

void
say(char* fmt, char* name)
{
	char	voice[50];
	char*	s;

	seprint(voice, voice+sizeof(voice), fmt, name);
	s = strchr(voice, '@');
	if (s)
		*s = 0;
	writefstr("/devs/voice/output", voice);

}

void
delfacei(int i)
{
	Face*	f;

	f = faces[i];
	dprint(2, "ofaces: del %s\n", f->addr);
	if (voiceoutmsg)
		say(voiceoutmsg, f->addr);
	faces[i] = nil;
	freeface(f);
}

void
delface(char* key)
{
	int	i;

	for (i = 0; i < nfaces; i++)
		if (faces[i] && !strcmp(faces[i]->key, key)){
			delfacei(i);
			break;
		}
}

int
hasface(char* key)
{
	int	i;

	for (i = 0; i < nfaces; i++)
		if (faces[i] && !strcmp(faces[i]->key, key)){
			faces[i]->reclaim = 0;	// for scandir
			return 1;
		}
	return 0;
}

void
addfaceimg(Face* f)
{
	long	l;
	void*	data;
	char	addr[8];
	char*	s;

	if (f == nil)
		return;
	openpanelctl(grow);
	panelctl(grow, "hold");
	f->col = createsubpanel(grow, "col:face");
	if (f->col == nil){
		fprint(2, "ofaces: %r\n");
		goto fail;
	}
	openpanelctl(f->col);
	panelctl(f->col, "notag");
	closepanelctl(f->col);
	f->img = createsubpanel(f->col, "image:face");
	if (f->img == nil){
		fprint(2, "ofaces: %r\n");
		goto fail;
	}
	closepanelctl(f->img);
	f->tag = createsubpanel(f->col, "label:sender");
	if (f->tag == nil){
		fprint(2, "ofaces: %r\n");
		goto fail;
	}
	openpanelctl(f->tag);
	panelctl(f->tag, "font S");
	closepanelctl(f->tag);
	seprint(addr, addr+sizeof(addr), f->addr);
	s = strchr(addr, '@');
	if (s)
		*s = 0;
	if (openpanel(f->tag, OWRITE) < 0)
		dprint(2, "openpanel %s %r\n", f->tag->name);
	if (writepanel(f->tag, addr, strlen(addr)) != strlen(addr))
		dprint(2, "writepanel %s %r\n", f->tag->name);
	closepanel(f->tag);
	data = readf(f->file, nil, 0, &l);
	if (data && l>0){
		openpanel(f->img, OWRITE);
		writepanel(f->img, data, l);
		closepanel(f->img);
	}
	free(data);
	dprint(2, "ofaces: add %s\n", f->addr);
	if (voiceinmsg)
		say(voiceinmsg, f->addr);
fail:
	closepanelctl(grow);
}

/* cmd output must be a list of mail addresses for users.
 * All white space is ignored.
 * Optionally, each address may be prefixed by a string and
 * an equal sign, to permit multiple addresses to be shown
 * multiple times.
 * Example:
 *  	nemo paurea esoriano
 * Another example:
 * 	/mail/box/nemo/mails/432/231/text=lscore@lsub.org
 */
void
getusers(char* cmd)
{
	static	char	buf[8*1024];
	static	char*	names[512];
	static	char*	keys[512];
	char*	s;
	int	nnames, i, n;
	char*	c;

	if (cmd[0] != '/' && cmd[0] != '.')
		c = smprint("/bin/%s", cmd);
	else
		c = strdup(cmd);
	n = tcmdoutput(c, buf, sizeof(buf)-1);
	free(c);
	if (n <= 0)
		return;
	buf[n] = 0;
	nnames = tokenize(buf, keys, nelem(keys));
	for (i = 0; i < nnames; i++){
		s = utfrune(keys[i], '=');
		if (s == nil){
			names[i] = keys[i];
		} else {
			*s++ = 0;
			names[i] = s;
		}
	}
	for (i = 0; i < nfaces; i++)
		if (faces[i])
			faces[i]->reclaim = 1;
	dprint(2, "ofaces: ");
	for (i = 0; i < nnames; i++){
		dprint(2, "%s ", names[i]);
		hasface(keys[i]);
	}
	dprint(2, "\n");
	for (i = 0; i < nfaces; i++)
		if (faces[i] && faces[i]->reclaim)
			delfacei(i);
	for (i = 0; i < nnames && nfaces < maxfaces; i++)
		if (!hasface(keys[i]))
			addfaceimg(newface(names[i], keys[i]));
}

void
lookmail(Oev* e)
{
	static int showfd = -1;
	int	i;
	char*	s;

	for (i = 0; i < nfaces; i++)
		if (faces[i] && faces[i]->img == e->panel)
			break;
	if (i == nfaces){
		dprint(2, "no mail for look event?");
		return;
	}
	dprint(2, "look for mail from %s\n", faces[i]->key);
	if (showfd == -1)
		showfd = plumbopen("edit", OWRITE);
	s = nil;
	if (faces[i]->addr){
		s = smprint("ofaces\nedit\n/\ntext\naddr=\n%ld\n%s",
			strlen(faces[i]->key), faces[i]->key);
		if (showfd != -1){
			if (write(showfd, s, strlen(s)) < 0){
				close(showfd);
				showfd = -1;
			}
		}
	}
	free(s);
}

void
usage(void)
{
	fprint(2, "usage: %s [-d] [-n max] [-i inmsg] [-o outmsg] [-l label] cmd\n", argv0);
	sysfatal("usage");
}

void
timerproc(void* a)
{
	Channel* c = a;

	for(;;){
		sleep(15 * 1000);
		sendul(c, 0);
	}
}

void
threadmain(int argc, char* argv[])
{
	extern int omerodebug;
	char* label;
	char*	cmd;
	char*	s;
	char	str[40];
	Panel*	lg;
	Oev	e;
	long	l;
	int	lflag;
	Alt	a[] = {
		{ nil, &e, CHANRCV },
		{ nil, &l, CHANRCV },
		{ nil, nil, CHANEND }};

	lflag = 0;
	label = nil;
	ARGBEGIN{
	case 'i':
		voiceinmsg = EARGF(usage());
		break;
	case 'o':
		voiceoutmsg = EARGF(usage());
		break;
	case 'D':
		omerodebug++;
		break;
	case 'd':
		debug++;
		if (debug > 1)
			omerodebug++;
		break;
	case 'n':
		maxfaces = atoi(EARGF(usage()));
		break;
	case 'l':
		lflag++;
		label = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND;
	switch(argc){
	case 0:
		cmd = strdup("Mails -l");
		break;
	case 1:
		cmd = strdup(argv[0]);
		break;
	default:
		cmd = nil;
		usage();
	}
	if (label == nil){
		label = strdup(cmd);
		s = strchr(label, ' ');
		if (s)
			*s = 0;
		s = strrchr(label ,'/');
		if (s)
			label = s+1;
	}
	a[0].c = omeroeventchan(nil);
	a[1].c = chancreate(sizeof(ulong), 0);
	grow = createpanel((lflag ? label : "ofaces"), "row", nil);
	if (grow == nil)
		sysfatal("panelinit: %r\n");
	if (label){
		seprint(str, str+40, "label:%s", label);
		lg = createsubpanel(grow, str);
		if (lg == nil)
			sysfatal("createsubpanel: %r");
	}
	panelctl(grow, "nohold");	// BUG: back compat. remove.
	closepanelctl(grow);
	proccreate(timerproc, a[1].c, 8*1024);
	getusers(cmd);
	for(;;){
		switch(alt(a)){
		case 0:
			dprint(2, "event %s %s\n", e.ev, e.path);
			if (!cistrcmp(e.ev, "look"))
				lookmail(&e);
			clearoev(&e);
			break;
		case 1:
			getusers(cmd);
			break;
		default:
			goto done;
		}
	}
done:
	omeroterm();
	threadexitsall(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.