ref: d88eb3a20c49c9d1678847c79a2cc6025ad830da
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; }