ref: cb2c30bfbf3f7d0ce1ecc28faf6891c98ab28273
dir: /muc.c/
#include <u.h>
#include <libc.h>
#include "xml.h"
#include "xmpp.h"
typedef struct Bookmark Bookmark;
struct Bookmark
{
char *jid;
};
char *affs[] = {
[Anone] "none",
[Aowner] "owner",
[Aadmin] "admin",
[Amember] "member",
[Aoutcast] "outcast",
};
static char *roles[] = {
[Rnone] "none",
[Rmoder] "moderator",
[Rpart] "participant",
[Rvisit] "visitor",
};
static Bookmark *bookmarks;
static int numbookmarks, mucwidth;
static int
addnick(Target *room, char *nick, char *jid, char *show, Target **ent)
{
Target *t;
int i, width;
for(i = 0; i < numtargets; i++){
int r;
t = targets[i];
if(t->type != Emucent || t->mucent.room != room)
continue;
r = strcmp(t->name, nick);
if(r == 0){
*ent = t;
return 0;
}
if(r > 0)
break;
}
t = addtarget(Emucent, nick);
t->jid = smprint("%s/%s", room->jid, nick);
t->mucent.jid = (jid == nil) ? nil : strdup(jid);
t->mucent.show = (show == nil) ? nil : strdup(show);
t->mucent.room = room;
if((width = utflen(nick)) > room->muc.width)
room->muc.width = width;
*ent = t;
room->muc.numents++;
return 1;
}
static int
rmnick(Target *room, char *nick)
{
Target *t;
int i, width, removed;
removed = 0;
room->muc.width = 0;
for(i = 0; i < numtargets; i++){
t = targets[i];
if(t->type != Emucent || t->mucent.room != room)
continue;
if(strcmp(t->name, nick) == 0){
rmtarget(t);
removed = 1;
}else if((width = utflen(t->name)) > room->muc.width)
room->muc.width = width;
}
if(removed)
room->muc.numents--;
return removed;
}
static int
setaffrole(Target *t, Xelem *item)
{
Xattr *aff, *role;
int changed, n, i;
if(item == nil)
return 0;
changed = 0;
role = xmlgetattr(item->a, "role");
aff = xmlgetattr(item->a, "affiliation");
if(role != nil){
n = Rnone;
for(i = 0; i < nelem(roles); i++){
if(strcmp(roles[i], role->v) == 0){
n = i;
break;
}
}
changed |= (t->mucent.role != n);
t->mucent.role = n;
}
if(aff != nil){
n = Anone;
for(i = 0; i < nelem(affs); i++){
if(strcmp(affs[i], aff->v) == 0){
n = i;
break;
}
}
changed |= (t->mucent.aff != n);
t->mucent.aff = n;
}
return changed;
}
static int
addbookmark(char *jid)
{
Bookmark *b;
int i;
for(i = 0; i < numbookmarks; i++)
if(strcmp(bookmarks[i].jid, jid) == 0)
return 0;
numbookmarks++;
bookmarks = realloc(bookmarks, sizeof(*b)*numbookmarks);
b = &bookmarks[numbookmarks-1];
b->jid = strdup(jid);
return 1;
}
static int
rmbookmark(char *jid)
{
int i;
for(i = 0; i < numbookmarks; i++){
if(strcmp(bookmarks[i].jid, jid) == 0){
numbookmarks--;
memcpy(&bookmarks[i], &bookmarks[i+1], sizeof(bookmarks[0])*(numbookmarks-i));
return 1;
}
}
return 0;
}
void
mucpresence(Xelem *xml, Target *room, Xattr *from)
{
char *s, didwhat[32];
Xattr *type;
Xelem *msg, *item, *x, *ch, *show;
int changed;
if((s = strchr(from->v, '/')) == nil)
return;
s++;
type = xmlgetattr(xml->a, "type");
msg = xmlget(xml->ch, "status");
x = item = nil;
show = xmlget(xml->ch, "show");
for(ch = xml->ch; ch != nil; ch = ch->next){
if(item == nil && (x = xmlget(ch, "x")) != nil)
item = xmlget(x->ch, "item");
}
if(x == nil)
return;
if(type != nil && strcmp(type->v, "unavailable") == 0){
Xelem *xstatus;
Xattr *code;
strcpy(didwhat, "left");
changed = rmnick(room, s);
xstatus = xmlget(x->ch, "status");
code = (xstatus != nil) ? xmlgetattr(xstatus->a, "code") : nil;
if(code != nil && strcmp(code->v, "303") == 0){
Xattr *nick;
nick = xmlgetattr(item->a, "nick");
if(nick != nil){
print("[%s] (%s) %s changed nick to %s\n",
strtime(), room->name, s, nick->v);
}
return;
}
}else{
Target *t;
Xattr *jid;
char *j, *sh;
jid = xmlgetattr(item->a, "jid");
j = (jid != nil && jid->v != nil) ? jid->v : nil;
sh = (show != nil && show->v != nil) ? show->v : nil;
strcpy(didwhat, "joined");
changed = addnick(room, s, j, sh, &t);
if(setaffrole(t, item) && !changed)
snprint(didwhat, sizeof(didwhat), "role=%s affiliation=%s",
affs[t->mucent.aff], roles[t->mucent.role]);
}
if(!changed || nopresence)
return;
print("[%s] (%s) %s %s", strtime(), room->name, s, didwhat);
if(msg != nil)
print(" (%s)", msg->v);
print("\n");
}
int
mucbookmarks(Xelem *e, int fd)
{
Xelem *x;
for(x = e->ch; x != nil; x = x->next){
char *argv[2];
Xattr *a;
if(strcmp(x->n, "conference") != 0)
continue;
if((a = xmlgetattr(x->a, "autojoin")) == nil)
continue;
if(strcmp(a->v, "1") != 0 && strcmp(a->v, "true") != 0)
continue;
argv[0] = "j";
a = xmlgetattr(x->a, "jid");
argv[1] = a->v;
if(cmdjoin(fd, 2, argv) < 0)
return -1;
addbookmark(a->v);
}
return 0;
}
int
cmdaff(int fd, int argc, char **argv)
{
Target *t, *room;
char *targ, *aff, *slash, *jid;
int i, nlen, res;
room = nil;
res = 0;
if(argc < 3){
if(argc < 2 && curr >= 0 && targets[curr]->type == Emuc)
room = targets[curr];
else{
targ = argv[1];
nlen = strlen(targ);
for(i = 0; i < numtargets; i++){
t = targets[i];
if(t->type == Emuc && targmatches(t, targ, nlen)){
room = t;
break;
}
}
}
for(i = Anone+1; room != nil && i < nelem(affs) && res >= 0; i++){
res = fprint(fd,
"<iq to='%Ӽ' type='get' id='afflist'>"
"<query xmlns='http://jabber.org/protocol/muc#admin'>"
"<item affiliation='%Ӽ'/>"
"</query></iq>",
room->jid,
affs[i]);
}
return res;
}
targ = argv[1];
aff = argv[2];
slash = strchr(targ, '/');
jid = nil;
if(curr >= 0 && targets[curr]->type == Emuc && slash == nil){
room = targets[curr];
if(strchr(targ, '@') != nil)
jid = targ;
else{
nlen = strlen(targ);
for(i = 0; i < numtargets; i++){
t = targets[i];
if(t->type == Emucent && t->mucent.room == room && targmatches(t, targ, nlen)){
jid = t->mucent.jid;
break;
}
}
}
}else if(slash != nil){
int rlen;
rlen = slash - targ;
slash++;
nlen = strlen(slash);
jid = (strchr(slash, '@') != nil) ? slash : nil;
for(i = 0; i < numtargets; i++){
t = targets[i];
if(t->type == Emucent && targmatches(t->mucent.room, targ, rlen) && targmatches(t, slash, nlen)){
room = t->mucent.room;
jid = t->mucent.jid;
break;
}
if(t->type == Emuc && targmatches(t, targ, rlen) && jid != nil){
room = t;
break;
}
}
}
if(room != nil && jid != nil){
res = fprint(fd,
"<iq to='%Ӽ' type='set'>"
"<query xmlns='http://jabber.org/protocol/muc#admin'>"
"<item affiliation='%Ӽ' jid='%Ӽ'/>"
"</query></iq>",
room->jid,
aff,
jid);
}else
print("no such target: %q\n", targ);
return res;
}
int
cmdbookmark(int fd, int /*argc*/, char **argv)
{
int (*f)(char *jid);
int i, res;
if(argv[0][1] == 0){
int i;
for(i = 0; i < numbookmarks; i++)
print(" %s\n", bookmarks[i].jid);
print("%d bookmark(s)\n", numbookmarks);
return 0;
}
if(argv[0][1] == '+')
f = addbookmark;
else if(argv[0][1] == '-')
f = rmbookmark;
else
return 0;
if(targets[curr]->type != Emuc || !f(targets[curr]->jid))
return 0;
res = fprint(fd,
"<iq type='set' id='takethat'>"
"<query xmlns='jabber:iq:private'>"
"<storage xmlns='storage:bookmarks'>");
for(i = 0; i < numbookmarks && res >= 0; i++)
res = fprint(fd, "<conference autojoin='1' jid='%Ӽ'/>", bookmarks[i].jid);
return res < 0 ? res : fprint(fd, "</storage></query></iq>");
}
int
cmdjoin(int fd, int argc, char **argv)
{
Target *t;
char *room, *rnick, *s;
int i, width, num;
if(argc < 2){
for(i = num = 0; i < numtargets; i++){
t = targets[i];
if(t->type == Emuc){
print(" %*s %d\n", -mucwidth, t->jid, t->muc.numents);
num++;
}
}
print("%d muc(s)\n", num);
return 0;
}
room = argv[1];
if(rnick = strchr(room, '/'))
*rnick++ = 0;
else
rnick = mynick;
if(fprint(fd,
"<presence to='%Ӽ/%Ӽ'>"
"<x xmlns='http://jabber.org/protocol/muc'>",
room, rnick) < 0)
return -1;
if(argc > 2 && fprint(fd, "<password>%Ӽ</password>", argv[2]) < 0)
return -1;
if(nohistory && fprint(fd, "<history maxchars='0'/>") < 0)
return -1;
if(fprint(fd, "</x></presence>") < 0)
return -1;
for(i = 0; i < numtargets; i++)
if(strcmp(targets[i]->name, room) == 0)
return 0;
t = addtarget(Emuc, room);
t->jid = strdup(room);
if(s = strchr(t->name, '@'))
*s = 0;
mucwidth = 0;
for(i = 0; i < numtargets; i++){
t = targets[i];
if(t->type == Emuc && (width = utflen(t->jid)) > mucwidth)
mucwidth = width;
}
return 0;
}
static Target *
findmuc(char *name)
{
int tlen, i;
Target *t;
t = nil;
tlen = strlen(name);
for(i = 0; i < numtargets; i++, t = nil){
t = targets[i];
if(t->type == Emuc && targmatches(t, name, tlen))
break;
}
if(t == nil)
print("no such muc: %q\n", name);
return t;
}
int
cmdpart(int fd, int argc, char **argv)
{
Target *t;
int i, width;
t = nil;
if(argc >= 2)
t = findmuc(argv[1]);
else if(curr >= 0 && targets[curr]->type == Emuc)
t = targets[curr];
if(t == nil)
return 0;
/* free private chats */
if(fprint(fd,
"<presence from='%Ӽ' to='%Ӽ' type='unavailable'></presence>",
myjid, t->jid) < 0)
return -1;
mucwidth = 0;
for(i = 0; i < numtargets; i++){
if(targets[i]->type == Emucent && targets[i]->mucent.room == t)
rmtarget(targets[i]);
if(t->type == Emuc && (width = utflen(t->jid)) > mucwidth)
mucwidth = width;
}
print("left %s\n", t->name);
rmtarget(t);
return 0;
}
int
cmdsubj(int fd, int argc, char **argv)
{
Target *t;
char *subj;
int res;
t = nil;
if(argc >= 2)
t = findmuc(argv[1]);
else if(curr >= 0 && targets[curr]->type == Emuc)
t = targets[curr];
if(t == nil)
return 0;
if(argv[0][0] == 's'){
print("subject for %s:\n%s\n", t->name, (t->muc.subj == nil) ? "" : t->muc.subj);
return 0;
}
subj = readlines();
res = fprint(fd,
"<message to='%Ӽ' type='%Ӽ'><subject>%Ӽ</subject></message>",
t->jid,
enttypes[t->type],
subj);
free(subj);
return res;
}
int
cmdwho(int, int argc, char **argv)
{
Target *room, *t;
char *show;
int i, num;
room = nil;
if(argc < 2){
if(curr < 0 || targets[curr]->type != Emuc)
return 0;
room = targets[curr];
}else{
int tlen;
tlen = strlen(argv[1]);
for(i = 0; i < numtargets; i++){
t = targets[i];
if(t->type == Emuc && targmatches(t, argv[1], tlen)){
room = t;
break;
}
}
if(room == nil){
print("no such target: %q\n", argv[1]);
return 0;
}
}
num = 0;
print("(%s):\n", room->jid);
for(i = 0; i < numtargets; i++){
t = targets[i];
if(t->type == Emucent && t->mucent.room == room){
show = t->mucent.show;
if(argv[0][0] == 'w' && show != nil){
if(strcmp(show, "away") == 0 || strcmp(show, "xa") == 0)
continue;
}
print(" %*s ", -room->muc.width, t->name);
if(argv[0][0] == 'W')
print(" %-7s ", show != nil ? show : "");
print("%-11s %-7s %s\n",
roles[t->mucent.role],
affs[t->mucent.aff],
t->mucent.jid != nil ? t->mucent.jid : "");
num++;
}
}
print("%d user(s)\n", num);
return 0;
}
int
cmdnick(int fd, int argc, char **argv)
{
char *nick, *p;
int i, res;
if(argc < 2 || curr < 0 || targets[curr]->type != Emuc)
return 0;
nick = strdup(argv[1]);
for(i = 2; i < argc; i++){
p = nick;
nick = smprint("%s %s", nick, argv[i]);
free(p);
}
res = fprint(fd,
"<presence from='%Ӽ' to='%Ӽ/%Ӽ'></presence>",
myjid, targets[curr]->jid, nick);
free(nick);
return res;
}