shithub: xmpp

ref: 7b3151f0a13823fccc39b2609155ffd1e5fd806f
dir: /rost.c/

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

enum
{
	Snone = 0,
	Sto,
	Sfrom,
	Sboth,
};

char *subscr2str[] =
{
	[Snone] "none",
	[Sto]   "to",
	[Sfrom] "from",
	[Sboth] "both",
};

char *subscr2fmt[] =
{
	[Snone] "[  ]",
	[Sto]   "[← ]",
	[Sfrom] "[ →]",
	[Sboth] "[←→]",
};

static char **asked;
static int numasked, rostwidth;

int
rostupdate(Xelem *x, int fd)
{
	Xattr *n, *s, *j;
	Target *t;
	int i, width;

	if(fprint(fd, "<presence/>") < 0)
		return -1;

	for(x = x->ch->ch; x != nil; x = x->next){
		n = xmlgetattr(x->a, "name");
		s = xmlgetattr(x->a, "subscription");
		j = xmlgetattr(x->a, "jid");
		if(j == nil || s == nil)
			continue;
		if(n == nil)
			n = j;

		for(i = 0, t = nil; i < numtargets; i++, t = nil){
			t = targets[i];
			if(t->type == Erost && strcmp(t->jid, j->v) == 0)
				break;
		}
		if(t == nil){
			t = addtarget(Erost, n->v);
			t->jid = strdup(j->v);
		}else{
			if(strcmp(s->v, "remove") == 0){
				print("%t removed from roster\n", t);
				rmtarget(t);
				continue;
			}
			free(t->name);
			t->name = strdup(n->v);
		}
		if((width = utflen(t->name)) > rostwidth)
			rostwidth = width;
		for(i = 0; i < nelem(subscr2str); i++){
			if(strcmp(s->v, subscr2str[i]) == 0){
				t->rost.subscr = i;
				break;
			}
		}
	}

	return 0;
}

int
rostsubscr(char *from, char *type, int fd)
{
	Target *t;
	int i;

	if(strcmp(type, "subscribe") == 0){
		for(i = 0; i < numtargets; i++){
			t = targets[i];
			if(t->type == Erost && (t->rost.flags & Fasked) && strncmp(t->jid, from, strlen(t->jid)) == 0){
				fprint(fd, "<presence to='%Ӽ' type='subscribed'/>", from);
				return 1;
			}
		}
		for(i = 0; i < numasked; i++)
			if(strcmp(asked[i], from) == 0)
				return 1;
		print("%s asks for a subscription\n", from);
		numasked++;
		asked = realloc(asked, numasked*sizeof(asked[0]));
		asked[numasked-1] = strdup(from);
		return 1;
	}else if(strcmp(type, "subscribed") == 0){
		for(i = 0; i < numtargets; i++){
			t = targets[i];
			if(t->type == Erost && strncmp(t->jid, from, strlen(t->jid)) == 0){
				t->rost.flags |= Fasked;
				print("%s has approved subscription\n", from);
				fprint(fd, "<presence to='%Ӽ' type='subscribe'/>", from); /* ack */
				return 1;
			}
		}
	}

	return 0;
}

void
rostpresence(Xelem *x, Target *t)
{
	Xelem *show;
	Xattr *type;
	char *didwhat, *s;
	int online;

	didwhat = nil;
	type = xmlgetattr(x->a, "type");
	show = xmlget(x->ch, "show");
	online = 1;
	if(type != nil){
		if(strcmp(type->v, "unavailable") == 0)
			online = 0;
		else{
			if(strcmp(type->v, "unsubscribed") == 0)
				print("%t cancelled subscription\n", t);
			else if(strcmp(type->v, "unsubscribe") == 0)
				print("%t unsubscribed\n", t);
			return;
		}
	}

	if(!online && (t->rost.flags & Fonline)){
		didwhat = "went offline";
		t->rost.flags &= ~Fonline;
	}else if(online && (t->rost.flags & Fonline) == 0){
		didwhat = "is online";
		t->rost.flags |= Fonline;
	}

	if(show == nil)
		s = "";
	else
		s = show->v;
	if(t->rost.show != nil && strcmp(s, t->rost.show) == 0)
		s = nil;
	else{
		free(t->rost.show);
		t->rost.show = strdup(s);
	}

	if(nopresence || didwhat == nil)
		return;
	print("[%s] %s %s", strtime(), t->name, didwhat);
	if(s != nil && s[0] != 0)
		print(" (%s)", s);
	print("\n");
}

static int
cmdrostadd(int fd, char *jid, char *name)
{
	int i;

	for(i = 0; i < numasked; i++){
		if(strncmp(asked[i], jid, strlen(jid)) == 0){
			if(fprint(fd, "<presence to='%Ӽ' type='subscribed'/>", asked[i]) < 0)
				return -1;
			jid = asked[i];
			break;
		}
	}

	if(fprint(fd, "<iq type='set'><query xmlns='jabber:iq:roster'><item jid='%Ӽ'", jid) < 0)
		return -1;
	if(name != nil && fprint(fd, " name='%Ӽ'", name) < 0)
		return -1;
	if(fprint(fd, "/></query></iq><presence to='%Ӽ' type='subscribe'/>", jid) < 0)
		return -1;
	print("asking %s for a subscription\n", jid);

	if(i < numasked){
		free(asked[i]);
		numasked--;
		memcpy(&asked[i], &asked[i+1], sizeof(asked[0])*(numasked-i));
	}
	return 0;
}

static int
cmdrostrm(int fd, char *jid)
{
	Target *t;
	char *a, *b;
	int i, alen, blen, res;

	for(i = 0; i < numasked; i++){
		if(strncmp(asked[i], jid, strlen(jid)) == 0){
			if(fprint(fd, "<presence to='%Ӽ' type='unsubscribed'/>", asked[i]) < 0)
				return -1;
			free(asked[i]);
			numasked--;
			memcpy(&asked[i], &asked[i+1], sizeof(asked[0])*(numasked-i));
			break;
		}
	}

	a = jid;
	b = strchr(a, '/');
	if(b == nil){
		blen = 0;
		alen = strlen(a);
	}else{
		b++;
		blen = strlen(b);
		alen = b-1 - a;
	}

	res = 0;
	for(i = 0; i < numtargets; i++){
		t = targets[i];
		if(t->type == Erost && targmatches(t, a, alen) && (b == nil || strncmp(t->jid, b, blen) == 0)){
			res = fprint(fd,
				"<iq type='set'>"
				"<query xmlns='jabber:iq:roster'>"
				"<item jid='%Ӽ' subscription='remove'/>"
				"</query></iq>",
				t->jid);
			if(res > 0)
				res = fprint(fd, "<presence to='%Ӽ' type='unsubscribe'/>", t->jid);
			break;
		}
	}
	return res;
}

static int
cmdrostshow(int extra)
{
	int i, num;

	for(i = num = 0; i < numtargets; i++){
		Target *t;
		t = targets[i];
		if(t->type != Erost)
			continue;
		if((t->rost.flags & Fonline) == 0 && !extra)
			continue;
		print("  %s %*s", subscr2fmt[t->rost.subscr], -rostwidth, t->name);
		if(t->rost.show != nil && t->rost.show[0] != 0)
			print("  [%s]", t->rost.show);
		else
		if((t->rost.flags & Fonline) == 0)
			print("  [offline]");
		print("\n");
		if(extra && strcmp(t->name, t->jid) != 0)
			print("    %s\n", t->jid);
		num++;
	}
	print("%d user(s) online\n", num);
	return 0;
}

int
cmdroster(int fd, int argc, char **argv)
{
	int op;

	op = argv[0][1];
	if(op == 0)
		return cmdrostshow(argv[0][0] == 'R');
	else if(op == '+' && argc > 1)
		return cmdrostadd(fd, argv[1], (argc > 2 ? argv[2] : nil));
	else if(op == '-' && argc > 1)
		return cmdrostrm(fd, argv[1]);
	return 0;
}