shithub: xmpp

ref: ed2752fe715680692023bbcaf85ddf03c2444c2c
dir: xmpp/muc.c

View raw version
#include <u.h>
#include <libc.h>
#include "xml.h"
#include "xmpp.h"

typedef struct Bookmark Bookmark;
struct Bookmark
{
	char *jid;
};

char *affs[] = {
	[Anone]    "none",
	[Aowner]   "owner",
	[Aadmin]   "admin",
	[Amember]  "member",
	[Aoutcast] "outcast",
};

static char *roles[] = {
	[Rnone]  "none",
	[Rmoder] "moderator",
	[Rpart]  "participant",
	[Rvisit] "visitor",
};

static Bookmark *bookmarks;
static int numbookmarks, mucwidth;

static int
addnick(Target *room, char *nick, char *jid, char *show, Target **ent)
{
	Target *t;
	int i, width;

	for(i = 0; i < numtargets; i++){
		int r;
		t = targets[i];
		if(t->type != Emucent || t->mucent.room != room)
			continue;
		r = strcmp(t->name, nick);
		if(r == 0){
			*ent = t;
			return 0;
		}
		if(r > 0)
			break;
	}
	t = addtarget(Emucent, nick);
	t->jid = smprint("%s/%s", room->jid, nick);
	t->mucent.jid = (jid == nil) ? nil : strdup(jid);
	t->mucent.show = (show == nil) ? nil : strdup(show);
	t->mucent.room = room;
	if((width = utflen(nick)) > room->muc.width)
		room->muc.width = width;
	*ent = t;
	room->muc.numents++;
	return 1;
}

static int
rmnick(Target *room, char *nick)
{
	Target *t;
	int i, width, removed;

	removed = 0;
	room->muc.width = 0;
	for(i = 0; i < numtargets; i++){
		t = targets[i];
		if(t->type != Emucent || t->mucent.room != room)
			continue;
		if(strcmp(t->name, nick) == 0){
			rmtarget(t);
			removed = 1;
		}else if((width = utflen(t->name)) > room->muc.width)
			room->muc.width = width;
	}
	if(removed)
		room->muc.numents--;
	return removed;
}

static int
setaffrole(Target *t, Xelem *item)
{
	Xattr *aff, *role;
	int changed, n, i;

	if(item == nil)
		return 0;
	changed = 0;
	role = xmlgetattr(item->a, "role");
	aff = xmlgetattr(item->a, "affiliation");
	if(role != nil){
		n = Rnone;
		for(i = 0; i < nelem(roles); i++){
			if(strcmp(roles[i], role->v) == 0){
				n = i;
				break;
			}
		}
		changed |= (t->mucent.role != n);
		t->mucent.role = n;
	}
	if(aff != nil){
		n = Anone;
		for(i = 0; i < nelem(affs); i++){
			if(strcmp(affs[i], aff->v) == 0){
				n = i;
				break;
			}
		}
		changed |= (t->mucent.aff != n);
		t->mucent.aff = n;
	}

	return changed;
}

static int
addbookmark(char *jid)
{
	Bookmark *b;
	int i;

	for(i = 0; i < numbookmarks; i++)
		if(strcmp(bookmarks[i].jid, jid) == 0)
			return 0;

	numbookmarks++;
	bookmarks = realloc(bookmarks, sizeof(*b)*numbookmarks);
	b = &bookmarks[numbookmarks-1];
	b->jid = strdup(jid);
	return 1;
}

static int
rmbookmark(char *jid)
{
	int i;

	for(i = 0; i < numbookmarks; i++){
		if(strcmp(bookmarks[i].jid, jid) == 0){
			numbookmarks--;
			memcpy(&bookmarks[i], &bookmarks[i+1], sizeof(bookmarks[0])*(numbookmarks-i));
			return 1;
		}
	}
	return 0;
}

void
mucpresence(Xelem *xml, Target *room, Xattr *from)
{
	char *s, didwhat[32];
	Xattr *type;
	Xelem *msg, *item, *x, *ch, *show;
	int changed;

	if((s = strchr(from->v, '/')) == nil)
		return;
	s++;

	type = xmlgetattr(xml->a, "type");
	msg = xmlget(xml->ch, "status");
	x = item = nil;
	show = xmlget(xml->ch, "show");
	for(ch = xml->ch; ch != nil; ch = ch->next){
		if(item == nil && (x = xmlget(ch, "x")) != nil)
			item = xmlget(x->ch, "item");
	}
	if(x == nil)
		return;

	if(type != nil && strcmp(type->v, "unavailable") == 0){
		Xelem *xstatus;
		Xattr *code;

		strcpy(didwhat, "left");
		changed = rmnick(room, s);
		xstatus = xmlget(x->ch, "status");
		code = (xstatus != nil) ? xmlgetattr(xstatus->a, "code") : nil;
		if(code != nil && strcmp(code->v, "303") == 0){
			Xattr *nick;
			nick = xmlgetattr(item->a, "nick");
			if(nick != nil){
				print("[%s] (%s) %s changed nick to %s\n",
					strtime(), room->name, s, nick->v);
			}
			return;
		}
	}else{
		Target *t;
		Xattr *jid;
		char *j, *sh;

		jid = xmlgetattr(item->a, "jid");
		j = (jid != nil && jid->v != nil) ? jid->v : nil;
		sh = (show != nil && show->v != nil) ? show->v : nil;

		strcpy(didwhat, "joined");
		changed = addnick(room, s, j, sh, &t);
		if(setaffrole(t, item) && !changed)
			snprint(didwhat, sizeof(didwhat), "role=%s affiliation=%s",
				affs[t->mucent.aff], roles[t->mucent.role]);
	}
	if(!changed || nopresence)
		return;
	print("[%s] (%s) %s %s", strtime(), room->name, s, didwhat);
	if(msg != nil)
		print(" (%s)", msg->v);
	print("\n");
}

int
mucbookmarks(Xelem *e, int fd)
{
	Xelem *x;

	for(x = e->ch; x != nil; x = x->next){
		char *argv[2];
		Xattr *a;

		if(strcmp(x->n, "conference") != 0)
			continue;
		if((a = xmlgetattr(x->a, "autojoin")) == nil)
			continue;
		if(strcmp(a->v, "1") != 0 && strcmp(a->v, "true") != 0)
			continue;
		argv[0] = "j";
		a = xmlgetattr(x->a, "jid");
		argv[1] = a->v;
		if(cmdjoin(fd, 2, argv) < 0)
			return -1;
		addbookmark(a->v);
	}

	return 0;
}

int
cmdaff(int fd, int argc, char **argv)
{
	Target *t, *room;
	char *targ, *aff, *slash, *jid;
	int i, nlen, res;

	room = nil;
	res = 0;
	if(argc < 3){
		if(argc < 2 && curr >= 0 && targets[curr]->type == Emuc)
			room = targets[curr];
		else{
			targ = argv[1];
			nlen = strlen(targ);
			for(i = 0; i < numtargets; i++){
				t = targets[i];
				if(t->type == Emuc && targmatches(t, targ, nlen)){
					room = t;
					break;
				}
			}
		}
		for(i = Anone+1; room != nil && i < nelem(affs) && res >= 0; i++){
			res = fprint(fd,
				"<iq to='%Ӽ' type='get' id='afflist'>"
				"<query xmlns='http://jabber.org/protocol/muc#admin'>"
				"<item affiliation='%Ӽ'/>"
				"</query></iq>",
				room->jid,
				affs[i]);
		}
		return res;
	}

	targ = argv[1];
	aff = argv[2];
	slash = strchr(targ, '/');
	jid = nil;

	if(curr >= 0 && targets[curr]->type == Emuc && slash == nil){
		room = targets[curr];
		if(strchr(targ, '@') != nil)
			jid = targ;
		else{
			nlen = strlen(targ);
			for(i = 0; i < numtargets; i++){
				t = targets[i];
				if(t->type == Emucent && t->mucent.room == room && targmatches(t, targ, nlen)){
					jid = t->mucent.jid;
					break;
				}
			}
		}
	}else if(slash != nil){
		int rlen;
		rlen = slash - targ;
		slash++;
		nlen = strlen(slash);
		jid = (strchr(slash, '@') != nil) ? slash : nil;
		for(i = 0; i < numtargets; i++){
			t = targets[i];
			if(t->type == Emucent && targmatches(t->mucent.room, targ, rlen) && targmatches(t, slash, nlen)){
				room = t->mucent.room;
				jid = t->mucent.jid;
				break;
			}
			if(t->type == Emuc && targmatches(t, targ, rlen) && jid != nil){
				room = t;
				break;
			}
		}
	}

	if(room != nil && jid != nil){
		res = fprint(fd,
			"<iq to='%Ӽ' type='set'>"
			"<query xmlns='http://jabber.org/protocol/muc#admin'>"
			"<item affiliation='%Ӽ' jid='%Ӽ'/>"
			"</query></iq>",
			room->jid,
			aff,
			jid);
	}else
		print("no such target: %q\n", targ);

	return res;
}

int
cmdbookmark(int fd, int /*argc*/, char **argv)
{
	int (*f)(char *jid);
	int i, res;

	if(argv[0][1] == 0){
		int i;
		for(i = 0; i < numbookmarks; i++)
			print("  %s\n", bookmarks[i].jid);
		print("%d bookmark(s)\n", numbookmarks);
		return 0;
	}

	if(argv[0][1] == '+')
		f = addbookmark;
	else if(argv[0][1] == '-')
		f = rmbookmark;
	else
		return 0;
	if(targets[curr]->type != Emuc || !f(targets[curr]->jid))
		return 0;

	res = fprint(fd,
		"<iq type='set' id='takethat'>"
		"<query xmlns='jabber:iq:private'>"
		"<storage xmlns='storage:bookmarks'>");
	for(i = 0; i < numbookmarks && res >= 0; i++)
		res = fprint(fd, "<conference autojoin='1' jid='%Ӽ'/>", bookmarks[i].jid);
	return res < 0 ? res : fprint(fd, "</storage></query></iq>");
}

int
cmdjoin(int fd, int argc, char **argv)
{
	Target *t;
	char *room, *rnick, *s;
	int i, width, num;

	if(argc < 2){
		for(i = num = 0; i < numtargets; i++){
			t = targets[i];
			if(t->type == Emuc){
				print("  %*s  %d\n", -mucwidth, t->jid, t->muc.numents);
				num++;
			}
		}
		print("%d muc(s)\n", num);
		return 0;
	}

	room = argv[1];
	if(rnick = strchr(room, '/'))
		*rnick++ = 0;
	else
		rnick = mynick;

	if(fprint(fd,
		"<presence to='%Ӽ/%Ӽ'>"
		"<x xmlns='http://jabber.org/protocol/muc'>",
		room, rnick) < 0)
		return -1;
	if(argc > 2 && fprint(fd, "<password>%Ӽ</password>", argv[2]) < 0)
		return -1;
	if(nohistory && fprint(fd, "<history maxchars='0'/>") < 0)
		return -1;
	if(fprint(fd, "</x></presence>") < 0)
		return -1;

	for(i = 0; i < numtargets; i++)
		if(strcmp(targets[i]->name, room) == 0)
			return 0;

	t = addtarget(Emuc, room);
	t->jid = strdup(room);
	if(s = strchr(t->name, '@'))
		*s = 0;
	mucwidth = 0;
	for(i = 0; i < numtargets; i++){
		t = targets[i];
		if(t->type == Emuc && (width = utflen(t->jid)) > mucwidth)
			mucwidth = width;
	}
	return 0;
}

static Target *
findmuc(char *name)
{
	int tlen, i;
	Target *t;

	t = nil;
	tlen = strlen(name);
	for(i = 0; i < numtargets; i++, t = nil){
		t = targets[i];
		if(t->type == Emuc && targmatches(t, name, tlen))
			break;
	}
	if(t == nil)
		print("no such muc: %q\n", name);
	return t;
}

int
cmdpart(int fd, int argc, char **argv)
{
	Target *t;
	int i, width;

	t = nil;
	if(argc >= 2)
		t = findmuc(argv[1]);
	else if(curr >= 0 && targets[curr]->type == Emuc)
		t = targets[curr];
	if(t == nil)
		return 0;

	/* free private chats */
	if(fprint(fd,
		"<presence from='%Ӽ' to='%Ӽ' type='unavailable'></presence>",
		myjid, t->jid) < 0)
		return -1;

	mucwidth = 0;
	for(i = 0; i < numtargets; i++){
		if(targets[i]->type == Emucent && targets[i]->mucent.room == t)
			rmtarget(targets[i]);
		if(t->type == Emuc && (width = utflen(t->jid)) > mucwidth)
			mucwidth = width;
	}

	print("left %s\n", t->name);
	rmtarget(t);
	return 0;
}

int
cmdsubj(int fd, int argc, char **argv)
{
	Target *t;
	char *subj;
	int res;

	t = nil;
	if(argc >= 2)
		t = findmuc(argv[1]);
	else if(curr >= 0 && targets[curr]->type == Emuc)
		t = targets[curr];
	if(t == nil)
		return 0;

	if(argv[0][0] == 's'){
		print("subject for %s:\n%s\n", t->name, (t->muc.subj == nil) ? "" : t->muc.subj);
		return 0;
	}

	subj = readlines();
	res = fprint(fd,
		"<message to='%Ӽ' type='%Ӽ'><subject>%Ӽ</subject></message>",
		t->jid,
		enttypes[t->type],
		subj);
	free(subj);
	return res;
}

int
cmdwho(int, int argc, char **argv)
{
	Target *room, *t;
	char *show;
	int i, num;

	room = nil;
	if(argc < 2){
		if(curr < 0 || targets[curr]->type != Emuc)
			return 0;
		room = targets[curr];
	}else{
		int tlen;
		tlen = strlen(argv[1]);
		for(i = 0; i < numtargets; i++){
			t = targets[i];
			if(t->type == Emuc && targmatches(t, argv[1], tlen)){
				room = t;
				break;
			}
		}
		if(room == nil){
			print("no such target: %q\n", argv[1]);
			return 0;
		}
	}

	num = 0;
	print("(%s):\n", room->jid);
	for(i = 0; i < numtargets; i++){
		t = targets[i];
		if(t->type == Emucent && t->mucent.room == room){
			show = t->mucent.show;
			if(argv[0][0] == 'w' && show != nil){
				if(strcmp(show, "away") == 0 || strcmp(show, "xa") == 0)
					continue;
			}

			print("  %*s  ", -room->muc.width, t->name);
			if(argv[0][0] == 'W')
				print(" %-7s ", show != nil ? show : "");
			print("%-11s %-7s  %s\n",
				roles[t->mucent.role],
				affs[t->mucent.aff],
				t->mucent.jid != nil ? t->mucent.jid : "");
			num++;
		}
	}
	print("%d user(s)\n", num);
	return 0;
}

int
cmdnick(int fd, int argc, char **argv)
{
	char *nick, *p;
	int i, res;

	if(argc < 2 || curr < 0 || targets[curr]->type != Emuc)
		return 0;
	nick = strdup(argv[1]);
	for(i = 2; i < argc; i++){
		p = nick;
		nick = smprint("%s %s", nick, argv[i]);
		free(p);
	}
	res = fprint(fd,
		"<presence from='%Ӽ' to='%Ӽ/%Ӽ'></presence>",
		myjid, targets[curr]->jid, nick);
	free(nick);
	return res;
}