Plan 9 from Bell Labs’s /usr/web/sources/contrib/stassats/jacc.c

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


/*
 * Copy me if you can.
 * by 20h
 */

#include <u.h>
#include <libc.h>
#include <auth.h>
#include <mp.h>
#include <libsec.h>
#include "xmlpull.h"
#include "jacc.h"
#include "dat.h"
#include "roster.h"
#include "recv.h"

#define NAME "jacc - Jabber Client for Plan9"
#define VERSION "3rd ed"
#define OS "Plan 9 4th ed"

extern int doignore;

int
xmljacc(int sock)
{
	return fprint(sock, "<?xml version=\"1.0\"?>\n");
}

int
loginjacc(int sock, char *serv)
{
	return fprint(sock, "<stream:stream xmlns:stream=\"http://etherx.jabber.org/streams\""
						" xmlns=\"jabber:client\" to=\"%s\">\n", serv);
}

int
userjacc(int sock, char *user, char *pass, char *res)
{
	return fprint(sock, "<iq type=\"set\" id=\"auth_1\">\n"
						"<query xmlns=\"jabber:iq:auth\">\n"
						"<username>%s</username>\n"
						"<password>%s</password>\n"
						"<resource>%s</resource>\n"
						"</query>\n"
						"</iq>\n", user, pass, res);
}

int
versionjacc(int sock, char *from, char *to, char *id)
{
	return fprint(sock, "<iq from=\"%s\" type=\"result\" id=\"%s\" to=\"%s\">\n"
						"<query xmlns=\"jabber:iq:version\">\n"
						"<name>" NAME "</name>\n"
						"<version>" VERSION "</version>\n"
						"<os>" OS "</os>\n"
						"</query>\n"
						"</iq>\n", from, (id == nil) ? "" : id, to);
}

int
featuresjacc(int sock, char *from, char *to, char *id)
{
	return fprint(sock, "<iq from=\"%s\" type=\"result\" to=\"%s\" id=\"%s\">\n"
						"<query xmlns=\"http://jabber.org/protocol/disco#info\">\n"
						"<identity category=\"client\" type=\"pc\"/>\n"
						"<feature var=\"jabber:iq:time\"/>\n"
						"<feature var=\"jabber:iq:version\"/>\n"
						"<feature var=\"http://jabber.org/protocol/muc\"/>\n"
						"</query>\n"
						"</iq>\n", from, to, (id == nil) ? "" : id);
}

int
timejacc(int sock, char *from, char *to, char *id)
{
	Tm *lo, *gm;

	lo = localtime(time(0));
	gm = gmtime(time(0));
	
	return fprint(sock, "<iq from=\"%s\" type=\"result\" to=\"%s\" id=\"%s\">\n"
						"<query xmlns=\"jabber:iq:time\">\n"
						"<utc>%.4d%.2d%.2dT%.2d:%.2d:%.2d</utc>\n"
						"<display>%s %s %.2d %.2d:%.2d:%.2d %.4d</display>\n"
						"<tz>%s</tz>\n"
						"</query>\n"
						"</iq>\n", from, to, (id == nil) ? "" : id, gm->year + 1900, 
						gm->mon + 1, gm->mday, gm->hour, gm->min, 
						gm->sec, getday(lo->wday), getmonth(lo->mon), 
						lo->mday, lo->hour, lo->min, lo->sec, 
						lo->year + 1900, lo->zone);
}

int
lastjacc(int sock, char *from, char *to, char *id, int d)
{
	return fprint(sock, "<iq from=\"%s\" type=\"result\" to=\"%s\" id=\"%s\">\n"
						"<query xmlns=\"jabber:iq:last\" seconds=\"%d\"/>\n"
						"</iq>\n", from, to, (id == nil) ? "" : id, d);
}

int
registerjacc(int sock, char *serv, char *user, char *pass)
{
	return fprint(sock, "<iq type=\"set\" id=\"req\" to=\"%s\">\n"
						"<query xmlns=\"jabber:iq:register\">\n"
						"<username>%s</username>\n"
						"<password>%s</password>\n"
						"</query>\n"
						"</iq>\n", serv, user, pass);
}

int
vcardgetjacc(int sock, char *from, char *type)
{
	return fprint(sock, "<iq %s=\"%s\" type=\"get\" id=\"v1\">\n"
						"<vCard xmlns=\"vcard-temp\"/>\n"
						"</iq>\n", type, from);
}

int
vcardsetjacc(int sock, char *from, int fd)
{
	fprint(sock, "<iq from=\"%s\" type=\"set\" id=\"v2\">\n"
				 "<vCard xmlns=\"vcard-temp\">\n", from);
	readwrite(sock, fd);

	return fprint(sock, "</vCard>\n"
						"</iq>\n");
}

int
presencejacc(int sock, char *stat, char *show, char *from, char *to)
{
	return fprint(sock, "<presence%s%s%s%s%s%s>\n"
						"<show>%s</show>\n"
						"<status>%s</status>\n"
						"<priority>9</priority>\n"
						"</presence>\n", (from != nil) ? " from=\"" : "",
										 (from != nil) ? from : "",
										 (from != nil) ? "\"" : "",
										 (to != nil) ? " to=\"" : "",
										 (to != nil) ? to : "",
										 (to != nil) ? "\"" : "", 
										 (show != nil) ? show : "",
										 (stat != nil) ? stat : "");
}

int
presencetypejacc(int sock, char *from, char *to, char *type)
{
	return fprint(sock, "<presence type=\"%s\" from=\"%s\" to=\"%s\"/>\n",
						type, from, to);
}

int
rosterjacc(int sock)
{
	return fprint(sock, "<iq type=\"get\" id=\"auth_2\">\n"
						"<query xmlns=\"jabber:iq:roster\"/>\n"
						"</iq>\n");
}

int
messagejacc(int sock, char *from, char *to, char *msg, char *type)
{
	return fprint(sock, "<message from=\"%s\" to=\"%s\" type=\"%s\">\n"
						"<body>%s</body>\n"
						"</message>\n", from, to, type, msg);
}

int
addbuddyjacc(int sock, char *jid, char *na, char *group)
{
	if(na == nil){
		na = jid;
		jid = strchr(na, '@');
	
		if(jid == nil)
			return -1;
		*jid++ = '\0';
	
		return fprint(sock, "<iq type=\"set\">\n"
		 			 "<query xmlns=\"jabber:iq:roster\">\n"
					 "<item jid=\"%s@%s\" name=\"%s\"/>\n"
					 "%s%s%s"
					 "</query>\n"
					 "</iq>\n", na, jid, na,
						(group != nil) ? "<group>" : "",
						(group != nil) ? group : "",
						(group != nil) ? "</group>\n" : "");
	}
		
	return fprint(sock, "<iq type=\"set\">\n"
		 		 "<query xmlns=\"jabber:iq:roster\">\n"
				 "<item jid=\"%s\" name=\"%s\"/>\n"
				 "%s%s%s"
				 "</query>\n"
				 "</iq>\n", jid, na,
						(group != nil) ? "<group>" : "",
						(group != nil) ? group : "",
						(group != nil) ? "</group>\n" : "");
}

int
delbuddyjacc(int sock, char *jid)
{
	return fprint(sock, "<iq type=\"set\">\n"
						"<query xmlns=\"jabber:iq:roster\">\n"
						"<item jid=\"%s\" subscription=\"remove\"/>\n"
						"</query>\n"
						"</iq>\n", jid);
}

int
xmlnsjacc(int sock, char *who, char *t, char *id)
{
	return fprint(sock, "<iq type=\"get\" to=\"%s\" id=\"%s\">\n"
						"<query xmlns=\"%s\"/>\n"
						"</iq>\n", who, id, t);
}

void
printrostern(rostern *r, char *w)
{
	char *tmstmp;

	tmstmp = mktmstmp('(', ')');
	while(r != nil){
		if(w != nil){
			if(r->status != nil)
				if(!strcmp(r->status, w))
					goto got_one;
			if(r->name != nil)
				if(!strcmp(r->name, w))
					goto got_one;
			if(r->jid != nil)
				if(!strcmp(r->jid, w))
					goto got_one;
			if(r->show != nil)
				if(!strcmp(r->show, w))
					goto got_one;
			if(r->group != nil)
				if(!strcmp(r->group, w))
					goto got_one;
		} else {
got_one:
			print("%s%s/%s on %s -> %s/%s\n", tmstmp, r->name, r->jid, r->group, r->show, r->status);	
		}

		r = r->n;
	}

	return;
}

void
usage(void)
{
	print("usage: [-dgit] [-r res] [-s tosrv] [net!]server[!port]\n");
	exits(0);
}

void admin (int sock, char *id, char *room, char *item)
{
	fprint(sock, "<iq id=\"%s\" to=\"%s\" type=\"set\"><query xmlns=\"http://jabber.org/protocol/muc#admin\"><item %s /></query></iq>",  id, room, item);
}



int
main(int argc, char *argv[])
{
	char *server, *user, *lbl, *b, *tmstmp, *buf, *toserver, *role, *tmp;
	int sock, ts, reg, debug, tls;
	UserPasswd *i;
	TLSconn conn;
	jabberc *me;

	tls = 0;
	b = nil;
	reg = 0;
	debug = 0;
	toserver = nil;

	ARGBEGIN {
	case 't':
		tls = 1;
		break;
	case 'r':
		b = EARGF(usage());
		break;
	case 'g':
		reg = 1;
		break;
	case 'd':
		debug = 1;
		break;
	case 'i':
		doignore = 1;
		break;
	case 's':
		toserver = EARGF(usage());
		break;
	default:
		usage();
	} ARGEND;

	if(argc < 1)
		usage();
	server = strdup(argv[0]);

	lbl = getwindowlbl();
	user = reallocj(nil, strlen(server) + 9, 2);
	snprint(user, strlen(server) + 8, "jacc - %s", server);
	setwindowlbl(user);
	free(user);

	i = auth_getuserpasswd(auth_getkey, "proto=pass server=%s service=jabber", server);
	if(i == nil)
		sysfatal("auth_getuserpasswd: %r");

	sock = dial(netmkaddr(server, "tcp", tls ? "5223" : "5222"), 0, 0, 0);
	if(sock < 0)
		sysfatal("dial: %r");

	if(tls){
		ts = tlsClient(sock, &conn);
		if(ts < 0)
			sysfatal("tlsClient: %r");
		sock = ts;

		if(conn.cert != nil)
			free(conn.cert);
	}

	buf = strchr(server, '!');
	if(buf != nil) {
		*buf++ = '\0';
		user = strchr(buf, '!');
		if(user != nil)
			*user = '\0';
		user = strdup(buf);
		free(server);
		server = user;
	}

	if(toserver == nil)
		toserver = server;

	me = mkjabberc();
	me->show = strdup("Online");
	me->stat = strdup("Online");
	me->name = strdup(i->user);
	me->serv = strdup(toserver);

	if(b != nil)
		me->reso = strdup(b);
	else
		me->reso = strdup("Plan9");
	me->jid = printjid(me->name, me->serv, me->reso);
	me->debug = debug;
	me->reg = reg;
	me->last = time(0);

	free(server);

	ts = getpid();

#ifdef PLAN9PORT
	switch(fork()) {
#endif
#ifndef PLAN9PORT
	switch(rfork(RFPROC|RFFDG|RFMEM)) {
#endif
	case -1:
		sysfatal("fork: %r");
	case 0:
		if(recvjacc(sock, me, i->passwd) < 0)
			perror("recvjacc");

		if(lbl != nil){
			setwindowlbl(lbl);
			lbl = nil;
			free(lbl);
		}
		killproc(ts);
		exits(0);
	default:
		user = reallocj(nil, 1025, 2);
		buf = nil;
		while(sock > 0 && user != nil){
			ts = -1;
			memset(user, 0, 1025);

			while(read(0, &user[++ts], 1) && ts < 1024 && sock > 0)
				if(user[ts] == '\n')
					break;
			user[ts] = '\0';
			me->last = time(0);

			tmstmp = mktmstmp('(', ')');
			if(user[0] != '/'){
				if(buf != nil){
					b = filterhin(user, 0);
					messagejacc(sock, me->jid, buf, b, "chat");
					print("%s\n", tmstmp);
					free(b);
				}
				free(tmstmp);
				continue;
			}
			if (user[1] == 'x'){
				b = getarg(user, 1, 0);
				if(b != nil){
					if (strcmp(b, "join") == 0){
						free(b);

						server = getarg(user, 2, 0);
                 
						if(server != nil){
							b = getarg(user, 3, 0);                         
							if(b == nil)
								b = strdup(me->name);

							fprint(sock, "<presence to=\"%s/%s\"/>",  server, b);
							free (server);
							free(b);
						}
					} 
					else
					if (strcmp(b, "leave") == 0){
						free(b);				
						server = getarg(user, 2, 0); 
						if(server != nil) {
							fprint(sock,
								"<presence to='%s' type='unavailable'/>",  server);
							free (server);
						}
											
					}
					else
					if (strcmp(b, "say") == 0){
						free(b);
						server = getarg(user, 2, 0);                    
						if(server != nil){                               
							b = getarg(user, 3, 2);                         
							if(b != nil){
								messagejacc(sock,
									 	me->jid, server, b, "groupchat");
								free(b);		
							}
							free(server);
						}
					}
					else
					if (strcmp(b, "priv") == 0){
						free(b);
						server = getarg(user, 2, 0);                    
						if(server != nil){
							role = getarg(user, 3, 0);
							if(role != nil){
								b = getarg(user, 4, 2);
								if(b != nil){
									tmp = server;
									server = smprint("%s/%s", server, role);
									free(tmp);

									messagejacc(sock,
									 		me->jid, server, b, "chat");
									free(b);		
								}
								free(role);
							}
							free(server);
						}
					}
					else
					if (strcmp(b, "role") == 0){
						free(b);
						server = getarg(user, 2, 0);                
						if(server != nil){    
                           				role = getarg(user, 3, 0);
							if(role != nil){
								b = getarg(user, 4, 2);
								if(b != nil){
									tmp = b;								
									b = smprint("nick='%s' role='%s'", b, role);
									free(tmp);

									admin(sock, role, server, b);
									free(b);					
								}
								free(role);
							}                    
							free(server);
						}
					}
					else
					if (strcmp(b, "affil") == 0){
						free(b);
						server = getarg(user, 2, 0);                    
						if(server != nil){
							role = getarg(user, 3, 0);
							if(role != nil){
								b = getarg(user, 4, 2);                         
								if(b != nil){
									tmp = b;
									b = smprint("jid='%s' affiliation='%s'", b, role);
									free(tmp);

									admin(sock, role, server, b);
									free(b);					
								}
								free(role);
							}                    
							free(server);
						}
					}
					else
						free(b);
				}
			}
			else
			switch(user[1]){
			case 'h':
			case 'H':
				print("%sHelp for jacc:\n", tmstmp);
				print("%s  /a [+|-|*]jid - authenticate jid\n", tmstmp);
				print("%s  /b - turn debugging on or off\n", tmstmp);
				print("%s  /c file - set vcard on server\n", tmstmp);
				print("%s  /d jid [feat] - do a discovery request\n", tmstmp);
				print("%s  /e jid - get time from jid\n", tmstmp);
				print("%s  /g jid - get agents information from jid\n", tmstmp);
				print("%s  /h - print out this help\n", tmstmp);
				print("%s  /i jid - get version of jid\n", tmstmp);
				print("%s  /l [status|jid|user] - list the roster\n", tmstmp);
				print("%s  /m jid - send a message to jid\n", tmstmp);
				print("%s  /p [show] [status] - set status and show\n", tmstmp);
				print("%s  /q - quit jacc\n", tmstmp);
				print("%s  /s [jid] - set active jid\n", tmstmp);
				print("%s  /t jid - get idle time of jid\n",tmstmp);
				print("%s  /u [+|-]jid [alias] - manage roster\n", tmstmp);
				print("%s  /v [jid] - get vcard from jid\n", tmstmp);

				print("%s  /x command - operate with groupchat\n", tmstmp);
				print("%s    commands:\n", tmstmp);
				print("%s  groupchat - leave groupchat\n", tmstmp);
				print("%s  join groupchat [nick] - join to the groupchat\n", tmstmp);
//				print("%s  names groupchat - occupants' list\n", tmstmp);
				print("%s  say groupchat - send a message to groupchat\n", tmstmp);
				print("%s  priv groupchat nick - send a private message to occupant\n", tmstmp);
				print("%s  leave groupchat - leave groupchat\n", tmstmp);
				print("%s  affil groupchat affilation jid - set affilation\n", tmstmp);
				print("%s  role groupchat role nick - set role\n", tmstmp);
				break;

			case 'q':
			case 'Q':
				fprint(sock, "<presence from=\"%s\" type=\"unavailable\"/>",
								me->jid);
				fprint(sock, "</stream:stream>");
				free(user);
				user = nil;
				break;
			case 's':
			case 'S':
				server = getarg(user, 1, 0);
				if(server == nil){
					print("%s%s\n", tmstmp, (buf != nil) ? buf : "<nil>");
					break;
				}

				buf = setchan(buf, namerostern(me->rost, nil, server));
				free(server);
					break;
			case 'l':
			case 'L':
				server = getarg(user, 1, 0);

				printrostern(me->rost, server);

				if(server != nil)
					free(server);
				break;
			case 'm':
			case 'M':
				server = getarg(user, 1, 0);
				if(server != nil){

					b = getarg(user, 2, 2);
					if(b != nil){
						messagejacc(sock, me->jid, namerostern(me->rost, nil, server), b, "normal");
						free(b);
					}

					free(server);
				}
				break;
			case 'p':
			case 'P':
				server = getarg(user, 1, 0);
				if(server == nil){
					print("%s%s\n", tmstmp, me->stat);
					break;
				}

				b = getarg(user, 2, 2);
				if(b != nil){
					presencejacc(sock, b, server, nil, nil);
					free(me->stat);
					me->stat = strdup(b);
				} else
					presencejacc(sock, nil, server, nil, nil);
				free(me->show);
				me->show = strdup(server);
				statusrostern(me->rost, me->jid, me->jid, server, b);
				free(server);
				break;
			case 'c':
			case 'C':
				server = getarg(user, 1, 0);
				if(server != nil){
					ts = open(server, OREAD);
					if(ts >= 0){
						vcardsetjacc(sock, me->jid, ts);
						close(ts);
					}
					free(server);
				}
				break;
			case 'v':
			case 'V':
				server = getarg(user, 1, 0);
				if(server == nil){
					vcardgetjacc(sock, me->jid, "from");
					break;
				}

				vcardgetjacc(sock, namerostern(me->rost, nil, server), "to");
				print("Vcard of: %s\n", namerostern(me->rost, nil, server));
				free(server);
				break;
			case 'u':
			case 'U':
				server = getarg(user, 1, 0);
				if(server != nil){
					if(server[0] == '-')
						delbuddyjacc(sock, namerostern(me->rost, server + 1, server + 1));
					else {
						b = getarg(user, 2, 0);
						if(server[0] == '+')
							addbuddyjacc(sock, server + 1, b, nil);
						else
							addbuddyjacc(sock, server, b, nil);
					}
					free(server);
				}
				break;
			case 'a':
			case 'A':
				server = getarg(user, 1, 0);
				if(server != nil){
					switch(server[0]){
					case '+':
						presencetypejacc(sock, me->jid, namerostern(me->rost, nil, server + 1), "subscribed");
						break;
					case '-':
						presencetypejacc(sock, me->jid, namerostern(me->rost, nil, server + 1), "unsubscribe");
						break;
					case '*':
						presencetypejacc(sock, me->jid, namerostern(me->rost, nil, server + 1), "subscribe");
						break;
					default:
						presencetypejacc(sock, me->jid, namerostern(me->rost, nil, server), "subscribed");
						break;
					}
					free(server);
				}
				break;
			case 'd':
			case 'D':
				server = getarg(user, 1, 0);
				if(server != nil){
					b = getarg(user, 2, 2);
					if(b == nil)
						b = strdup("info");

					free(tmstmp);
					tmstmp = reallocj(nil, 35 + strlen(b), 2);
					sprint(tmstmp, "http://jabber.org/protocol/disco#%s", b);

					xmlnsjacc(sock, server, tmstmp, "disco0");
					free(b);
					free(server);
				}
				break;
			case 'b':
			case 'B':
				if(me->debug == 0)
					me->debug = 1;
				else
					me->debug = 0;
				print("%sDebug: %c\n", tmstmp, me->debug);
				break;
			case 't':
			case 'T':
				server = getarg(user, 1, 0);
				if(server != nil){
					xmlnsjacc(sock, namerostern(me->rost, nil, server), "jabber:iq:last", "last0");
					free(server);
				}
				break;
			case 'i':
			case 'I':
				server = getarg(user, 1, 0);
				if(server != nil){
					xmlnsjacc(sock, namerostern(me->rost, nil, server), "jabber:iq:version", "version0");
					free(server);
				}
				break;
			case 'e':
			case 'E':
				server = getarg(user, 1, 0);
				if(server != nil){
					xmlnsjacc(sock, namerostern(me->rost, nil, server), "jabber:iq:time", "time0");
					free(server);
				}
				break;
			case 'g':
			case 'G':
				server = getarg(user, 1, 0);
				if(server != nil){
					xmlnsjacc(sock, namerostern(me->rost, nil, server), "jabber:iq:agents", "agents0");
					free(server);
				}
				break;

			default:
				break;
			}
			free(tmstmp);
		}

		wait();
		if(lbl != nil){
			setwindowlbl(lbl);
			lbl = nil;
			free(lbl);
		}
		break;
	}

	freejabberc(me);
	exits(0);
	return 0;
}

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.