574 lines · 13.4 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <auth.h>#include <bio.h>#include <thread.h>#include "xml.h"#include "xmpp.h" /* commands are at xmpp.c:/^handle */ char *enttypes[] ={	[Emuc]    "groupchat",	[Emucent] "chat",	[Erost]   "chat",}; static int pfd;static Biobuf pb;static char lastid[32]; int debug, nopresence, nohistory, plainallow;Biobuf kbin;char *server, *mydomain, *myjid, *mynick, *myresource, *myplainjid;static QLock prlock; static intismyjid(char *jid){	int n; 	n = strlen(myplainjid);	if(strncmp(jid, myplainjid, n) == 0 && (jid[n] == 0 || jid[n] == '/'))		return 1;	n = strlen(myjid);	if(strncmp(jid, myjid, n) == 0 && (jid[n] == 0 || jid[n] == '/'))		return 1; 	return 0;} static voidinerror(Xelem *x){	Xattr *from, *type;	Xelem *err; 	from = xmlgetattr(x->a, "from");	err = xmlget(x->ch, "error");	type = err == nil ? nil : xmlgetattr(err->a, "type");	print("[%s] (%s, error) %s %s",		strtime(), (from == nil) ? mydomain : from->v, x->n, type == nil ? "" : type->v);	err = err == nil ? nil : err->ch;	if(err != nil)		print(": %s", (err->v == nil) ? err->n : err->v);	print("\n");} static voidinmsg(Xelem *x){	Xattr *type, *from, *stamp, *to, *ns;	Xelem *body, *delay, *subj, *e;	char *s, *nick, *bodyv, tmp[64];	Target *t, *room;	int i; 	if((from = xmlgetattr(x->a, "from")) == nil)		return; 	/* message carbons https://xmpp.org/extensions/xep-0280.html */	if((e = xmlget(x->ch, "received")) != nil &&	   ismyjid(from->v) &&	   (ns = xmlgetattr(e->a, "xmlns")) != nil &&	   strcmp(ns->v, "urn:xmpp:carbons:2") == 0 &&	   (e = xmlget(e->ch, "forwarded")) != nil &&	   (e = xmlget(e->ch, "message")) != nil){		x = e;		if((from = xmlgetattr(x->a, "from")) == nil)			return;	} 	type = xmlgetattr(x->a, "type");	body = xmlget(x->ch, "body");	subj = xmlget(x->ch, "subject");	/* ignore "composing..." messages */	if(body == nil && subj == nil)		return; 	to = xmlgetattr(x->a, "to");	if(to != nil && !ismyjid(to->v))		return; 	if((delay = xmlget(x->ch, "delay")) == nil)		delay = xmlget(x->ch, "x");	if((stamp = delay ? xmlgetattr(delay->a, "stamp") : nil) == nil)		stamp = xmlgetattr(x->a, "ts");	bodyv = (body == nil) ? nil : ((body->v == nil) ? "" : body->v); 	/*	 * there is no difference between mucpriv and raw jid	 * try to find the target	 */	t = room = nil;	for(i = 0; i < numtargets; i++, t = nil){		t = targets[i];		if(t->type == Emuc && strncmp(t->jid, from->v, strlen(t->jid)) == 0)			room = t;		else if((t->type == Emucent && strcmp(t->jid, from->v) == 0) ||				(t->type == Erost && strncmp(t->jid, from->v, strlen(t->jid)) == 0)){			break;		}	} 	if(subj != nil && room != nil){		free(room->muc.subj);		room->muc.subj = strdup((subj->v == nil) ? "" : subj->v);		return;	}	if(bodyv == nil)		return; 	print("[%s] ", (stamp != nil) ? strstamp(stamp->v) : strtime());	if(t == nil && room == nil)		nick = from->v;	else if(t != nil && t->type == Erost){		snprint(tmp, sizeof(tmp), "%t", t);		nick = tmp;	}else{		/* extract nick and muc */		if(nick = strchr(from->v, '/'))			nick++;		if(s = strchr(from->v, '@'))			*s = 0;		print("(%s", from->v);		if(type != nil && strcmp(type->v, enttypes[Emucent]) == 0)			print(", private) ");		else			print(") ");	} 	if(nick == nil)		print("%s\n", bodyv);	else if(strncmp(bodyv, "/me ", 4) == 0)		print("→ %s %s\n", nick, bodyv+4);	else		print("%s → %s\n", nick, bodyv);} static intiniq(Xelem *x, int fd){	Xelem *e;	Xattr *a, *from, *id, *var;	int isget, isset; 	a = xmlgetattr(x->a, "type");	if(a == nil || x->ch == nil)		return 0;	id = xmlgetattr(x->a, "id"); 	if(strcmp(a->v, "result") == 0){		if(x->ch != nil && (e = xmlget(x->ch->ch, "storage")) != nil){			/* autojoin bookmarked MUCs http://xmpp.org/extensions/xep-0048.html */			a = xmlgetattr(e->a, "xmlns");			if(strcmp(a->v, "storage:bookmarks") != 0)				return 0;			return mucbookmarks(e, fd);		}		if(x->ch != nil && x->ch->ch != nil && strcmp(x->ch->n, "bind") == 0){			if(strcmp(x->ch->ch->n, "jid") == 0){				free(myjid);				myjid = strdup(x->ch->ch->v);			}		}		if(id != nil && strcmp(id->v, "afflist") == 0){			int width, len, num;			Xattr *aff, *jid;			for(e = x->ch->ch, width = 0; e != nil; e = e->next){				if((jid = xmlgetattr(e->a, "jid")) != nil && (len = strlen(jid->v)) > width)					width = len;			}			for(e = x->ch->ch, num = 0; e != nil; e = e->next, num++){				if((jid = xmlgetattr(e->a, "jid")) != nil && (aff = xmlgetattr(e->a, "affiliation")) != nil)					print("  %*s  %-8s\n", -width, jid->v, aff->v);			}			print("%d jid(s)\n", num);			return 0;		}else if(id != nil && strcmp(id->v, "gimme0") == 0){			/* bookmarks http://xmpp.org/extensions/xep-0048.html */			if(fprint(pfd,				"<iq type='get' from='%Ӽ' id='gimme1'>"				"<query xmlns='jabber:iq:private'>"				"<storage xmlns='storage:bookmarks'/>"				"</query></iq>",				myjid) < 0)				return -1; 			/* ask for roster */			if(fprint(pfd,				"<iq type='get' from='%Ӽ' id='gimme2'>"				"<query xmlns='jabber:iq:roster'/></iq>",				myjid) < 0)				return -1; 			/* enable message carbons https://xmpp.org/extensions/xep-0280.html */			if(x->ch != nil)			for(e = x->ch->ch; e != nil; e = e->next){				if(strcmp(e->n, "feature") != 0)					continue;				if((var = xmlgetattr(e->a, "var")) == nil)					continue;				if(strcmp(var->v, "urn:xmpp:carbons:2") != 0)					continue;								if(fprint(pfd,					"<iq type='set' from='%Ӽ' id='lotsofcarbs' xmlns='jabber:client'>"					"<enable xmlns='urn:xmpp:carbons:2'/>"					"</iq>",					myjid) < 0)					return -1;				break;			}		}	} 	/* incoming queries */	isget = strcmp(a->v, "get") == 0;	isset = strcmp(a->v, "set") == 0;	if(!isget && !isset && strcmp(a->v, "result") != 0)		return 0;	from = xmlgetattr(x->a, "from");	if(isget && (from == nil || id == nil))		return 0; 	if(e = xmlget(x->ch, "query")){		a = xmlgetattr(e->a, "xmlns");		if(a != nil && isget &&strcmp(a->v, "jabber:iq:version") == 0){			/* software version http://xmpp.org/extensions/xep-0092.html */			return fprint(fd,				"<iq type='result' to='%Ӽ' id='%Ӽ'>"				"<query xmlns='jabber:iq:version'>"				"<name>xmpp</name>"				"<version>9front edition</version>"				"<os>Plan 9</os>"				"</query></iq>",				from->v, id->v);		}		if(a != nil && isget && strcmp(a->v, "http://jabber.org/protocol/disco#info") == 0){			/* service discovery http://xmpp.org/extensions/xep-0030.html */			return fprint(fd,				"<iq type='result' to='%Ӽ' id='%Ӽ'>"				"<query xmlns='http://jabber.org/protocol/disco#info'>"				"<feature var='http://jabber.org/protocol/disco#info'/>"				"<feature var='jabber:iq:version'/>"				"<feature var='urn:xmpp:time'/>"				"<feature var='urn:xmpp:ping'/>"				"<feature var='http://jabber.org/protocol/muc'/>"				"</query></iq>",				from->v, id->v);		}		if(a != nil && !isget && strcmp(a->v, "jabber:iq:roster") == 0)			return rostupdate(x, fd);	}else if(isget && (e = xmlget(x->ch, "time")) != nil){		a = xmlgetattr(e->a, "xmlns");		if(a != nil && strcmp(a->v, "urn:xmpp:time") == 0){			/* entity time http://xmpp.org/extensions/xep-0202.html */			char *utc, *tzo; 			utc = strenttime(&tzo);			return fprint(fd,				"<iq type='result' to='%Ӽ' id='%Ӽ'>"				"<time xmlns='urn:xmpp:time'>"				"<utc>%Ӽ</utc><tzo>%Ӽ</tzo>"				"</time></iq>",				from->v, id->v, utc, tzo);		}	}else if(isget && (e = xmlget(x->ch, "ping")) != nil){		a = xmlgetattr(e->a, "xmlns");		if(a != nil && strcmp(a->v, "urn:xmpp:ping") == 0){			/* ping http://xmpp.org/extensions/xep-0199.html */			return fprint(fd,				"<iq type='result' to='%Ӽ' id='%Ӽ'/>",				from->v, id->v);		}	} 	if(isget)		return fprint(fd,			"<iq type='error' to='%Ӽ' id='%Ӽ'>"			"<error type='cancel'>"			"<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"			"</error></iq>",			from->v, id->v);	return 0;} static voidinpresence(Xelem *x, int fd){	int i, found;	Target *t;	Xattr *from, *type; 	from = xmlgetattr(x->a, "from");	type = xmlgetattr(x->a, "type");	if(type != nil && rostsubscr(from->v, type->v, fd))		return;	found = 0;	for(i = 0; i < numtargets; i++){		t = targets[i];		if(t->type == Erost && strncmp(t->jid, from->v, strlen(t->jid)) == 0){			rostpresence(x, t);			found = 1;			break;		}		if(t->type == Emucent && strcmp(t->jid, from->v) == 0){			mucpresence(x, t->mucent.room, from);			found = 1;		}		if(t->type == Emuc && strncmp(t->jid, from->v, strlen(t->jid)) == 0){			mucpresence(x, t, from);			found = 1;		}	} 	if(!found && debug > 0)		fprint(2, "presence from unknown target: %q\n", from->v);} static voidreader(void *pd){	Xelem *x;	Xattr *type, *id;	int err; 	USED(pd);	threadsetname("reader"); 	fprint(pfd,		"<iq type='get' from='%Ӽ' to='%Ӽ' id='gimme0'>"		"<query xmlns='http://jabber.org/protocol/disco#info'/>"		"</iq>",		myjid, server); 	for(err = 0; err == 0;){		if((x = xmlread(&pb, 0, &err)) == nil)			continue; 		qlock(&prlock);		if((id = xmlgetattr(x->a, "id")) != nil && strcmp(id->v, lastid) == 0){			xmlprint(x, 2);			lastid[0] = 0;		}else if(debug > 1){			if(strcmp(x->n, "presence") != 0 || debug > 2)				xmlprint(x, 2);		} 		type = xmlgetattr(x->a, "type");		if(type != nil && strcmp(type->v, "error") == 0)			inerror(x);		else if(strcmp(x->n, "message") == 0)			inmsg(x);		else if(strcmp(x->n, "presence") == 0)			inpresence(x, pfd);		else if(strcmp(x->n, "iq") == 0)			iniq(x, pfd);		xmlfree(x);		qunlock(&prlock);	}	fprint(2, "%r\n");	threadexitsall(nil);} static intcmdmsg(int fd, int, char **){	Target *t;	char *s;	int res; 	if(curr < 0)		return 0; 	s = readlines();	t = targets[curr];	if(t->type == Emucent || t->type == Erost)		print("[%s] (%s) me → %s\n", strtime(), t->name, s);	res = fprint(fd,		"<message to='%Ӽ' type='%Ӽ'><body>%Ӽ</body></message>",		t->jid,		enttypes[t->type],		s);	free(s);	return res;} static inthandle(int fd, char *s){	typedef int (*cmdf)(int, int, char **);	char *ps, *pe, *argv[3];	static cmdf cmds[256] = {		['a'] cmdaff,      /*  muc.c:/^cmdaff      */		['b'] cmdbookmark, /*  muc.c:/^cmdbookmark */		['j'] cmdjoin,     /*  muc.c:/^cmdjoin     */		['m'] cmdmsg,      /* xmpp.c:/^cmdmsg      */		['n'] cmdnick,     /*  muc.c:/^cmdnick     */		['p'] cmdpart,     /*  muc.c:/^cmdpart     */		['r'] cmdroster,   /* rost.c:/^cmdroster   */		['R'] cmdroster,   /* rost.c:/^cmdroster   */		['s'] cmdsubj,     /*  muc.c:/^cmdsubj     */		['S'] cmdsubj,     /*  muc.c:/^cmdsubj     */		['t'] cmdtarget,   /* targ.c:/^cmdtarget   */		['w'] cmdwho,      /*  muc.c:/^cmdwho      */		['W'] cmdwho,      /*  muc.c:/^cmdwho      */	};	int argc;	Target *t; 	cleaninput(utflen(s)+1);	if(*s == '/' && *(++s) != '/'){		if(*s == 'q'){			for(s++; (*s == ' ' || *s == '\t'); s++);			lastid[0] = 0;			if((ps = utfutf(s, "id='")) != nil && (pe = utfrune(ps+4, '\'')) != nil){				ps += 4;				if(sizeof(lastid) > pe-ps)					strncpy(lastid, ps, pe-ps);			}			return fprint(fd, "%s", s);		}else if(*s == 'm' && s[1] == 'e'){			s--;		}else if(cmds[*s] != nil){			argc = tokenize(s, argv, nelem(argv));			return cmds[*s](fd, argc, argv);		}else{			s--;			print("unknown cmd %q\n", s);			return 0;		}	} 	if(curr < 0)		return 0;	t = targets[curr];	if(t->type == Emucent || t->type == Erost)		print("[%s] (%s) me → %s\n", strtime(), t->name, s);	return fprint(fd,		"<message to='%Ӽ' type='%Ӽ'><body>%Ӽ</body></message>",		t->jid, enttypes[t->type], s);} static voidwriter(void *pd){	char *s;	int err; 	USED(pd);	threadsetname("writer"); 	Binit(&kbin, 0, OREAD);	for(err = 0; (s = Brdstr(&kbin, '\n', 1)) != nil && err >= 0;){		qlock(&prlock);		if(s[0] != 0)			err = handle(pfd, s);		free(s);		qunlock(&prlock);	}} static voidusage(void){	fprint(2, "usage: xmpp [-n nick] [-r resource] [-p] [-y] jid\n");	threadexits("usage");} static intdie(void *, char *){	setlabel(nil, nil);	return 0;} static voidpblethal(char *m){	threadexitsall(m);} voidthreadmain(int argc, char **argv){	UserPasswd *up;	char *user; 	debug = 0;

Lines 1–500 of 574.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t