/*
* 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;
}