jacc

Jabber/XMPP client for Plan 9
git clone git://r-36.net/jacc
Log | Files | Refs | LICENSE

jacc.c (14431B)


      1 /*
      2  * Copy me if you can.
      3  * by 20h
      4  */
      5 
      6 #include <u.h>
      7 #include <libc.h>
      8 #include <auth.h>
      9 #include <mp.h>
     10 #include <libsec.h>
     11 #include "xmlpull.h"
     12 #include "jacc.h"
     13 #include "dat.h"
     14 #include "roster.h"
     15 #include "recv.h"
     16 
     17 #define NAME "jacc - Jabber Client for Plan9"
     18 #define VERSION "2nd ed"
     19 #define OS "Plan 9 4th ed"
     20 
     21 extern int doignore;
     22 
     23 int
     24 xmljacc(int sock)
     25 {
     26 	return fprint(sock, "<?xml version=\"1.0\"?>\n");
     27 }
     28 
     29 int
     30 loginjacc(int sock, char *serv)
     31 {
     32 	return fprint(sock, "<stream:stream xmlns:stream=\"http://etherx.jabber.org/streams\""
     33 						" xmlns=\"jabber:client\" to=\"%s\">\n", serv);
     34 }
     35 
     36 int
     37 userjacc(int sock, char *user, char *pass, char *res)
     38 {
     39 	return fprint(sock, "<iq type=\"set\" id=\"auth_1\">\n"
     40 						"<query xmlns=\"jabber:iq:auth\">\n"
     41 						"<username>%s</username>\n"
     42 						"<password>%s</password>\n"
     43 						"<resource>%s</resource>\n"
     44 						"</query>\n"
     45 						"</iq>\n", user, pass, res);
     46 }
     47 
     48 int
     49 versionjacc(int sock, char *from, char *to, char *id)
     50 {
     51 	return fprint(sock, "<iq from=\"%s\" type=\"result\" id=\"%s\" to=\"%s\">\n"
     52 						"<query xmlns=\"jabber:iq:version\">\n"
     53 						"<name>" NAME "</name>\n"
     54 						"<version>" VERSION "</version>\n"
     55 						"<os>" OS "</os>\n"
     56 						"</query>\n"
     57 						"</iq>\n", from, (id == nil) ? "" : id, to);
     58 }
     59 
     60 int
     61 featuresjacc(int sock, char *from, char *to, char *id)
     62 {
     63 	return fprint(sock, "<iq from=\"%s\" type=\"result\" to=\"%s\" id=\"%s\">\n"
     64 						"<query xmlns=\"http://jabber.org/protocol/disco#info\">\n"
     65 						"<identity category=\"client\" type=\"pc\"/>\n"
     66 						"<feature var=\"jabber:iq:time\"/>\n"
     67 						"<feature var=\"jabber:iq:version\"/>\n"
     68 						"<feature var=\"http://jabber.org/protocol/muc\"/>\n"
     69 						"</query>\n"
     70 						"</iq>\n", from, to, (id == nil) ? "" : id);
     71 }
     72 
     73 int
     74 timejacc(int sock, char *from, char *to, char *id)
     75 {
     76 	Tm *lo, *gm;
     77 
     78 	lo = localtime(time(0));
     79 	gm = gmtime(time(0));
     80 	
     81 	return fprint(sock, "<iq from=\"%s\" type=\"result\" to=\"%s\" id=\"%s\">\n"
     82 						"<query xmlns=\"jabber:iq:time\">\n"
     83 						"<utc>%.4d%.2d%.2dT%.2d:%.2d:%.2d</utc>\n"
     84 						"<display>%s %s %.2d %.2d:%.2d:%.2d %.4d</display>\n"
     85 						"<tz>%s</tz>\n"
     86 						"</query>\n"
     87 						"</iq>\n", from, to, (id == nil) ? "" : id, gm->year + 1900, 
     88 						gm->mon + 1, gm->mday, gm->hour, gm->min, 
     89 						gm->sec, getday(lo->wday), getmonth(lo->mon), 
     90 						lo->mday, lo->hour, lo->min, lo->sec, 
     91 						lo->year + 1900, lo->zone);
     92 }
     93 
     94 int
     95 lastjacc(int sock, char *from, char *to, char *id, int d)
     96 {
     97 	return fprint(sock, "<iq from=\"%s\" type=\"result\" to=\"%s\" id=\"%s\">\n"
     98 						"<query xmlns=\"jabber:iq:last\" seconds=\"%d\"/>\n"
     99 						"</iq>\n", from, to, (id == nil) ? "" : id, d);
    100 }
    101 
    102 int
    103 registerjacc(int sock, char *serv, char *user, char *pass)
    104 {
    105 	return fprint(sock, "<iq type=\"set\" id=\"req\" to=\"%s\">\n"
    106 						"<query xmlns=\"jabber:iq:register\">\n"
    107 						"<username>%s</username>\n"
    108 						"<password>%s</password>\n"
    109 						"</query>\n"
    110 						"</iq>\n", serv, user, pass);
    111 }
    112 
    113 int
    114 vcardgetjacc(int sock, char *from, char *type)
    115 {
    116 	return fprint(sock, "<iq %s=\"%s\" type=\"get\" id=\"v1\">\n"
    117 						"<vCard xmlns=\"vcard-temp\"/>\n"
    118 						"</iq>\n", type, from);
    119 }
    120 
    121 int
    122 vcardsetjacc(int sock, char *from, int fd)
    123 {
    124 	fprint(sock, "<iq from=\"%s\" type=\"set\" id=\"v2\">\n"
    125 				 "<vCard xmlns=\"vcard-temp\">\n", from);
    126 	readwrite(sock, fd);
    127 
    128 	return fprint(sock, "</vCard>\n"
    129 						"</iq>\n");
    130 }
    131 
    132 int
    133 presencejacc(int sock, char *stat, char *show, char *from, char *to)
    134 {
    135 	return fprint(sock, "<presence%s%s%s%s%s%s>\n"
    136 						"<show>%s</show>\n"
    137 						"<status>%s</status>\n"
    138 						"<priority>9</priority>\n"
    139 						"</presence>\n", (from != nil) ? " from=\"" : "",
    140 										 (from != nil) ? from : "",
    141 										 (from != nil) ? "\"" : "",
    142 										 (to != nil) ? " to=\"" : "",
    143 										 (to != nil) ? to : "",
    144 										 (to != nil) ? "\"" : "", 
    145 										 (show != nil) ? show : "",
    146 										 (stat != nil) ? stat : "");
    147 }
    148 
    149 int
    150 presencetypejacc(int sock, char *from, char *to, char *type)
    151 {
    152 	return fprint(sock, "<presence type=\"%s\" from=\"%s\" to=\"%s\"/>\n",
    153 						type, from, to);
    154 }
    155 
    156 int
    157 rosterjacc(int sock)
    158 {
    159 	return fprint(sock, "<iq type=\"get\" id=\"auth_2\">\n"
    160 						"<query xmlns=\"jabber:iq:roster\"/>\n"
    161 						"</iq>\n");
    162 }
    163 
    164 int
    165 messagejacc(int sock, char *from, char *to, char *msg, char *type)
    166 {
    167 	return fprint(sock, "<message from=\"%s\" to=\"%s\" type=\"%s\">\n"
    168 						"<body>%s</body>\n"
    169 						"</message>\n", from, to, type, msg);
    170 }
    171 
    172 int
    173 addbuddyjacc(int sock, char *jid, char *na, char *group)
    174 {
    175 	if(na == nil){
    176 		na = jid;
    177 		jid = strchr(na, '@');
    178 	
    179 		if(jid == nil)
    180 			return -1;
    181 		*jid++ = '\0';
    182 	
    183 		return fprint(sock, "<iq type=\"set\">\n"
    184 		 			 "<query xmlns=\"jabber:iq:roster\">\n"
    185 					 "<item jid=\"%s@%s\" name=\"%s\"/>\n"
    186 					 "%s%s%s"
    187 					 "</query>\n"
    188 					 "</iq>\n", na, jid, na,
    189 						(group != nil) ? "<group>" : "",
    190 						(group != nil) ? group : "",
    191 						(group != nil) ? "</group>\n" : "");
    192 	}
    193 		
    194 	return fprint(sock, "<iq type=\"set\">\n"
    195 		 		 "<query xmlns=\"jabber:iq:roster\">\n"
    196 				 "<item jid=\"%s\" name=\"%s\"/>\n"
    197 				 "%s%s%s"
    198 				 "</query>\n"
    199 				 "</iq>\n", jid, na,
    200 						(group != nil) ? "<group>" : "",
    201 						(group != nil) ? group : "",
    202 						(group != nil) ? "</group>\n" : "");
    203 }
    204 
    205 int
    206 delbuddyjacc(int sock, char *jid)
    207 {
    208 	return fprint(sock, "<iq type=\"set\">\n"
    209 						"<query xmlns=\"jabber:iq:roster\">\n"
    210 						"<item jid=\"%s\" subscription=\"remove\"/>\n"
    211 						"</query>\n"
    212 						"</iq>\n", jid);
    213 }
    214 
    215 int
    216 xmlnsjacc(int sock, char *who, char *t, char *id)
    217 {
    218 	return fprint(sock, "<iq type=\"get\" to=\"%s\" id=\"%s\">\n"
    219 						"<query xmlns=\"%s\"/>\n"
    220 						"</iq>\n", who, id, t);
    221 }
    222 
    223 void
    224 printrostern(rostern *r, char *w)
    225 {
    226 	char *tmstmp;
    227 
    228 	tmstmp = mktmstmp('(', ')');
    229 	while(r != nil){
    230 		if(w != nil){
    231 			if(r->status != nil)
    232 				if(!strcmp(r->status, w))
    233 					goto got_one;
    234 			if(r->name != nil)
    235 				if(!strcmp(r->name, w))
    236 					goto got_one;
    237 			if(r->jid != nil)
    238 				if(!strcmp(r->jid, w))
    239 					goto got_one;
    240 			if(r->show != nil)
    241 				if(!strcmp(r->show, w))
    242 					goto got_one;
    243 			if(r->group != nil)
    244 				if(!strcmp(r->group, w))
    245 					goto got_one;
    246 		} else {
    247 got_one:
    248 			print("%s%s/%s on %s -> %s/%s\n", tmstmp, r->name, r->jid, r->group, r->show, r->status);	
    249 		}
    250 
    251 		r = r->n;
    252 	}
    253 
    254 	return;
    255 }
    256 
    257 void
    258 usage(void)
    259 {
    260 	print("usage: [-dgit] [-r res] [-s tosrv] [net!]server[!port]\n");
    261 	exits(0);
    262 }
    263 
    264 int
    265 main(int argc, char *argv[])
    266 {
    267 	char *server, *user, *lbl, *b, *tmstmp, *buf, *toserver;
    268 	int sock, ts, reg, debug, tls;
    269 	UserPasswd *i;
    270 	TLSconn conn;
    271 	jabberc *me;
    272 
    273 	tls = 0;
    274 	b = nil;
    275 	reg = 0;
    276 	debug = 0;
    277 	toserver = nil;
    278 
    279 	ARGBEGIN {
    280 	case 't':
    281 		tls = 1;
    282 		break;
    283 	case 'r':
    284 		b = EARGF(usage());
    285 		break;
    286 	case 'g':
    287 		reg = 1;
    288 		break;
    289 	case 'd':
    290 		debug = 1;
    291 		break;
    292 	case 'i':
    293 		doignore = 1;
    294 		break;
    295 	case 's':
    296 		toserver = EARGF(usage());
    297 		break;
    298 	default:
    299 		usage();
    300 	} ARGEND;
    301 
    302 	if(argc < 1)
    303 		usage();
    304 	server = strdup(argv[0]);
    305 
    306 	lbl = getwindowlbl();
    307 	user = reallocj(nil, strlen(server) + 9, 2);
    308 	snprint(user, strlen(server) + 8, "jacc - %s", server);
    309 	setwindowlbl(user);
    310 	free(user);
    311 
    312 	i = auth_getuserpasswd(auth_getkey, "proto=pass server=%s service=jabber", server);
    313 	if(i == nil)
    314 		sysfatal("auth_getuserpasswd: %r");
    315 
    316 	sock = dial(netmkaddr(server, "tcp", tls ? "5223" : "5222"), 0, 0, 0);
    317 	if(sock < 0)
    318 		sysfatal("dial: %r");
    319 
    320 	if(tls){
    321 		ts = tlsClient(sock, &conn);
    322 		if(ts < 0)
    323 			sysfatal("tlsClient: %r");
    324 		sock = ts;
    325 
    326 		if(conn.cert != nil)
    327 			free(conn.cert);
    328 	}
    329 
    330 	buf = strchr(server, '!');
    331 	if(buf != nil) {
    332 		*buf++ = '\0';
    333 		user = strchr(buf, '!');
    334 		if(user != nil)
    335 			*user = '\0';
    336 		user = strdup(buf);
    337 		free(server);
    338 		server = user;
    339 	}
    340 
    341 	if(toserver == nil)
    342 		toserver = server;
    343 
    344 	me = mkjabberc();
    345 	me->show = strdup("Online");
    346 	me->stat = strdup("Online");
    347 	me->name = strdup(i->user);
    348 	me->serv = strdup(toserver);
    349 
    350 	if(b != nil)
    351 		me->reso = strdup(b);
    352 	else
    353 		me->reso = strdup("Plan9");
    354 	me->jid = printjid(me->name, me->serv, me->reso);
    355 	me->debug = debug;
    356 	me->reg = reg;
    357 	me->last = time(0);
    358 
    359 	free(server);
    360 
    361 	ts = getpid();
    362 
    363 #ifdef PLAN9PORT
    364 	switch(fork()) {
    365 #endif
    366 #ifndef PLAN9PORT
    367 	switch(rfork(RFPROC|RFFDG|RFMEM)) {
    368 #endif
    369 	case -1:
    370 		sysfatal("fork: %r");
    371 	case 0:
    372 		if(recvjacc(sock, me, i->passwd) < 0)
    373 			perror("recvjacc");
    374 
    375 		if(lbl != nil){
    376 			setwindowlbl(lbl);
    377 			lbl = nil;
    378 			free(lbl);
    379 		}
    380 		killproc(ts);
    381 		exits(0);
    382 	default:
    383 		user = reallocj(nil, 1025, 2);
    384 		buf = nil;
    385 		while(sock > 0 && user != nil){
    386 			ts = -1;
    387 			memset(user, 0, 1025);
    388 
    389 			while(read(0, &user[++ts], 1) && ts < 1024 && sock > 0)
    390 				if(user[ts] == '\n')
    391 					break;
    392 			user[ts] = '\0';
    393 			me->last = time(0);
    394 
    395 			tmstmp = mktmstmp('(', ')');
    396 			if(user[0] != '/'){
    397 				if(buf != nil){
    398 					b = filterhin(user, 0);
    399 					messagejacc(sock, me->jid, buf, b, "chat");
    400 					print("%s\n", tmstmp);
    401 					free(b);
    402 				}
    403 				free(tmstmp);
    404 				continue;
    405 			}
    406 
    407 			switch(user[1]){
    408 			case 'h':
    409 			case 'H':
    410 				print("%sHelp for jacc:\n", tmstmp);
    411 				print("%s  /a [+|-|*]jid - authenticate jid\n", tmstmp);
    412 				print("%s  /b - turn debugging on or off\n", tmstmp);
    413 				print("%s  /c file - set vcard on server\n", tmstmp);
    414 				print("%s  /d jid [feat] - do a discovery request\n", tmstmp);
    415 				print("%s  /e jid - get time from jid\n", tmstmp);
    416 				print("%s  /g jid - get agents information from jid\n", tmstmp);
    417 				print("%s  /h - print out this help\n", tmstmp);
    418 				print("%s  /i jid - get version of jid\n", tmstmp);
    419 				print("%s  /l [status|jid|user] - list the roster\n", tmstmp);
    420 				print("%s  /m jid - send a message to jid\n", tmstmp);
    421 				print("%s  /p [show] [status] - set status and show\n", tmstmp);
    422 				print("%s  /q - quit jacc\n", tmstmp);
    423 				print("%s  /s [jid] - set active jid\n", tmstmp);
    424 				print("%s  /t jid - get idle time of jid\n",tmstmp);
    425 				print("%s  /u [+|-]jid [alias] - manage roster\n", tmstmp);
    426 				print("%s  /v [jid] - get vcard from jid\n", tmstmp);
    427 				print("%s\n", tmstmp);
    428 				break;
    429 			case 'q':
    430 			case 'Q':
    431 				fprint(sock, "<presence from=\"%s\" type=\"unavailable\"/>",
    432 								me->jid);
    433 				fprint(sock, "</stream:stream>");
    434 				free(user);
    435 				user = nil;
    436 				break;
    437 			case 's':
    438 			case 'S':
    439 				server = getarg(user, 1, 0);
    440 				if(server == nil){
    441 					print("%s%s\n", tmstmp, (buf != nil) ? buf : "<nil>");
    442 					break;
    443 				}
    444 
    445 				buf = setchan(buf, namerostern(me->rost, nil, server));
    446 				free(server);
    447 					break;
    448 			case 'l':
    449 			case 'L':
    450 				server = getarg(user, 1, 0);
    451 
    452 				printrostern(me->rost, server);
    453 
    454 				if(server != nil)
    455 					free(server);
    456 				break;
    457 			case 'm':
    458 			case 'M':
    459 				server = getarg(user, 1, 0);
    460 				if(server == nil)
    461 					break;
    462 
    463 				b = getarg(user, 2, 2);
    464 				if(b == nil){
    465 					free(server);
    466 					break;
    467 				}
    468 
    469 				messagejacc(sock, me->jid, namerostern(me->rost, nil, server), b, "normal");
    470 				free(server);
    471 				free(b);
    472 				break;
    473 			case 'p':
    474 			case 'P':
    475 				server = getarg(user, 1, 0);
    476 				if(server == nil){
    477 					print("%s%s\n", tmstmp, me->stat);
    478 					break;
    479 				}
    480 
    481 				b = getarg(user, 2, 2);
    482 				if(b != nil){
    483 					presencejacc(sock, b, server, nil, nil);
    484 					free(me->stat);
    485 					me->stat = strdup(b);
    486 				} else
    487 					presencejacc(sock, nil, server, nil, nil);
    488 				free(me->show);
    489 				me->show = strdup(server);
    490 				statusrostern(me->rost, me->jid, me->jid, server, b);
    491 				free(server);
    492 				break;
    493 			case 'c':
    494 			case 'C':
    495 				server = getarg(user, 1, 0);
    496 				if(server != nil){
    497 					ts = open(server, OREAD);
    498 					if(ts >= 0){
    499 						vcardsetjacc(sock, me->jid, ts);
    500 						close(ts);
    501 					}
    502 					free(server);
    503 				}
    504 				break;
    505 			case 'v':
    506 			case 'V':
    507 				server = getarg(user, 1, 0);
    508 				if(server == nil){
    509 					vcardgetjacc(sock, me->jid, "from");
    510 					break;
    511 				}
    512 
    513 				vcardgetjacc(sock, namerostern(me->rost, nil, server), "to");
    514 				print("Vcard of: %s\n", namerostern(me->rost, nil, server));
    515 				free(server);
    516 				break;
    517 			case 'u':
    518 			case 'U':
    519 				server = getarg(user, 1, 0);
    520 				if(server != nil){
    521 					if(server[0] == '-')
    522 						delbuddyjacc(sock, namerostern(me->rost, server + 1, server + 1));
    523 					else {
    524 						b = getarg(user, 2, 0);
    525 						if(server[0] == '+')
    526 							addbuddyjacc(sock, server + 1, b, nil);
    527 						else
    528 							addbuddyjacc(sock, server, b, nil);
    529 					}
    530 					free(server);
    531 				}
    532 				break;
    533 			case 'a':
    534 			case 'A':
    535 				server = getarg(user, 1, 0);
    536 				if(server != nil){
    537 					switch(server[0]){
    538 					case '+':
    539 						presencetypejacc(sock, me->jid, namerostern(me->rost, nil, server + 1), "subscribed");
    540 						break;
    541 					case '-':
    542 						presencetypejacc(sock, me->jid, namerostern(me->rost, nil, server + 1), "unsubscribe");
    543 						break;
    544 					case '*':
    545 						presencetypejacc(sock, me->jid, namerostern(me->rost, nil, server + 1), "subscribe");
    546 						break;
    547 					default:
    548 						presencetypejacc(sock, me->jid, namerostern(me->rost, nil, server), "subscribed");
    549 						break;
    550 					}
    551 					free(server);
    552 				}
    553 				break;
    554 			case 'd':
    555 			case 'D':
    556 				server = getarg(user, 1, 0);
    557 				if(server != nil){
    558 					b = getarg(user, 2, 2);
    559 					if(b == nil)
    560 						b = strdup("info");
    561 
    562 					free(tmstmp);
    563 					tmstmp = reallocj(nil, 35 + strlen(b), 2);
    564 					sprint(tmstmp, "http://jabber.org/protocol/disco#%s", b);
    565 
    566 					xmlnsjacc(sock, server, tmstmp, "disco0");
    567 					free(b);
    568 					free(server);
    569 				}
    570 				break;
    571 			case 'b':
    572 			case 'B':
    573 				if(me->debug == 0)
    574 					me->debug = 1;
    575 				else
    576 					me->debug = 0;
    577 				print("%sDebug: %c\n", tmstmp, me->debug);
    578 				break;
    579 			case 't':
    580 			case 'T':
    581 				server = getarg(user, 1, 0);
    582 				if(server != nil){
    583 					xmlnsjacc(sock, namerostern(me->rost, nil, server), "jabber:iq:last", "last0");
    584 					free(server);
    585 				}
    586 				break;
    587 			case 'i':
    588 			case 'I':
    589 				server = getarg(user, 1, 0);
    590 				if(server != nil){
    591 					xmlnsjacc(sock, namerostern(me->rost, nil, server), "jabber:iq:version", "version0");
    592 					free(server);
    593 				}
    594 				break;
    595 			case 'e':
    596 			case 'E':
    597 				server = getarg(user, 1, 0);
    598 				if(server != nil){
    599 					xmlnsjacc(sock, namerostern(me->rost, nil, server), "jabber:iq:time", "time0");
    600 					free(server);
    601 				}
    602 				break;
    603 			case 'g':
    604 			case 'G':
    605 				server = getarg(user, 1, 0);
    606 				if(server != nil){
    607 					xmlnsjacc(sock, namerostern(me->rost, nil, server), "jabber:iq:agents", "agents0");
    608 					free(server);
    609 				}
    610 				break;
    611 			default:
    612 				break;
    613 			}
    614 			free(tmstmp);
    615 		}
    616 
    617 		wait();
    618 		if(lbl != nil){
    619 			setwindowlbl(lbl);
    620 			lbl = nil;
    621 			free(lbl);
    622 		}
    623 		break;
    624 	}
    625 
    626 	freejabberc(me);
    627 	exits(0);
    628 	return 0;
    629 }