mirror of
git://git.psyced.org/git/psyced
synced 2024-08-15 03:25:10 +00:00
1301 lines
39 KiB
C
1301 lines
39 KiB
C
// $Id: user.c,v 1.303 2008/09/12 15:54:39 lynx Exp $ // vim:syntax=lpc:ts=8
|
|
#include "jabber.h"
|
|
#include "user.h"
|
|
#include "person.h"
|
|
#include <uniform.h>
|
|
#include <peers.h>
|
|
|
|
// important to #include user.h first
|
|
// then we also repatch _host_XMPP so disco.c does the right thing for us
|
|
#undef _host_XMPP
|
|
#define _host_XMPP SERVER_HOST
|
|
|
|
volatile string prefix; // used anywhere?
|
|
volatile string tag;
|
|
volatile string resource;
|
|
volatile string myjid, myjidresource;
|
|
|
|
volatile int isplacemsg;
|
|
|
|
volatile int hasroster = 0; // client has requested roster
|
|
|
|
volatile mapping affiliations = ([ ]); // hack to support affiliations
|
|
volatile mapping jabber2avail;
|
|
|
|
#include JABBER_PATH "disco.c"
|
|
// #include NET_PATH "members.i" // isn't this include redundant?
|
|
|
|
sTag(t) { tag = t; }
|
|
sResource(r) { resource = r; }
|
|
qResource() { return resource; }
|
|
|
|
qHasCurrentPlace() { return 0; }
|
|
|
|
void create() {
|
|
jabber2avail = shared_memory("jabber2avail");
|
|
::create();
|
|
}
|
|
|
|
/* it should be posible to do some things in a way that can be shared between
|
|
* user and active/gateway... mostly stuff like MUC-support, version requests
|
|
* and such -- TODO
|
|
* what should be strictly in user.c is mainly roster managment
|
|
*/
|
|
|
|
msg(source, mc, data, mapping vars, showingLog) {
|
|
int ret;
|
|
string jid, buddy;
|
|
string packet;
|
|
mixed t;
|
|
|
|
P2(("%s beim jabber:user:msg\n", mc))
|
|
// net/group/master says we should copy vars if we need to
|
|
// mess with it. yes we certainly do! major bugfix here :)
|
|
vars = copy(vars);
|
|
|
|
// looks quite similar to the stuff in active.c, but active
|
|
// is much more elaborated
|
|
if (vars["_place"]) vars["_place"] = mkjid(vars["_place"]);
|
|
|
|
if (abbrev("_message_echo_public", mc) || abbrev("_jabber", mc)) {
|
|
return render(mc, data, vars, source);
|
|
} else if (abbrev("_message_echo_private", mc) && v("echo") != "on") {
|
|
return;
|
|
} else if (abbrev("_notice_place_enter", mc)) {
|
|
mc = "_notice_place_enter";
|
|
vars["_source_relay"] = mkjid(source);
|
|
} else if (abbrev("_echo_place_enter", mc)) {
|
|
#if 1
|
|
// we need ::msg to do the check only - this is a dirty hack
|
|
// see ::msg _echo_place_enter comment on enterMembers
|
|
if (::msg(source, "_echo_place_enter_INTERNAL_CHECK", data, vars, showingLog) == -1)
|
|
return;
|
|
// there probably is a nicer way to do this, but for now this will do
|
|
#endif
|
|
#if 0
|
|
affiliations[source] = vars["_duty"];
|
|
mc = "_echo_place_enter";
|
|
vars["_source_relay"] = myjid;
|
|
#else
|
|
# ifdef ENTER_MEMBERS
|
|
string template;
|
|
if (vars["_list_members"]) {
|
|
jid = mkjid(source);
|
|
array(string) rendered;
|
|
// find local objects, they should be displayed as locals
|
|
vars["_list_members"] = map(copy(vars["_list_members"]),
|
|
(: return psyc_object($1) || $1; :));
|
|
// PARANOID check missing, but we no longer need it
|
|
rendered = renderMembers(vars["_list_members"],
|
|
vars["_list_members_nicks"]);
|
|
packet = "";
|
|
|
|
// this one is different from active.c
|
|
// we want local users to be shown with nicknames, even
|
|
// if we are in a remote room
|
|
// TODO: there does not seem to be proper support for
|
|
// room-local nicknames and clashnick
|
|
// (this is already in active.c)
|
|
// this also affects other places... probably it's best
|
|
// to include the 210 status code?
|
|
template = T("_notice_place_enter", "");
|
|
for(int i = 0; i < sizeof(vars["_list_members"]); i++) {
|
|
#if 0
|
|
if (rendered[i] == vars["_nick"]) {
|
|
// this only happens with local users
|
|
rendered[i] = SERVER_UNIFORM +"~"+ rendered[i];
|
|
}
|
|
#endif
|
|
packet += psyctext(template, ([
|
|
"_source_relay" : mkjid(vars["_list_members"][i]),
|
|
"_INTERNAL_source_jabber" : jid + "/" + RESOURCEPREP(rendered[i]),
|
|
"_INTERNAL_target_jabber" : myjidresource,
|
|
"_duty" : "none"
|
|
]));
|
|
}
|
|
}
|
|
template = T("_echo_place_enter", "");
|
|
packet += psyctext(template, ([
|
|
"_source_relay" : myjid,
|
|
"_INTERNAL_source_jabber" : jid + "/" + RESOURCEPREP(vars["_nick_local"] || vars["_nick"]),
|
|
"_INTERNAL_target_jabber" : myjidresource,
|
|
"_duty" : affiliations[source] || "none"
|
|
]));
|
|
emit(packet);
|
|
# endif
|
|
return 1;
|
|
#endif
|
|
} else if (abbrev("_notice_place_leave", mc)) {
|
|
mc = "_notice_place_leave";
|
|
}
|
|
switch (mc) {
|
|
case "_status_description_time":
|
|
case "_status_person_present":
|
|
case "_status_person_present_implied":
|
|
case "_status_person_absent":
|
|
case "_status_person_absent_recorded":
|
|
P2(("%O got %O\n", ME, mc))
|
|
|
|
// actually.. we never send _time_idle with this
|
|
if (member(vars, "_time_idle")) {
|
|
t = vars["_time_idle"];
|
|
if (stringp(t)) {
|
|
t = to_int(t);
|
|
P2(("_time_idle %O == %O, right?\n", vars["_time_idle"], t))
|
|
}
|
|
t = gmtime(time() - t);
|
|
vars["_INTERNAL_time_jabber"] = JABBERTIME(t);
|
|
} else {
|
|
vars["_INTERNAL_time_jabber"] = JABBERTIME(gmtime(time()));
|
|
}
|
|
break;
|
|
case "_notice_friendship_established":
|
|
// TODO:
|
|
// it should be checked that this request is valid
|
|
// but for this hack xbuddylist[buddy] != 0 is enough
|
|
ret = ::msg(source, mc, data, vars, showingLog);
|
|
buddy = objectp(source) ? source -> qName() : source;
|
|
jid = mkjid(source);
|
|
emit(sprintf("<iq type='set'>"
|
|
"<query xmlns='jabber:iq:roster'>"
|
|
"<item jid='%s' subscription='both'>%s"
|
|
"</item></query></iq>",
|
|
jid, IMPLODE_XML(xbuddylist[buddy], "<group>") || ""));
|
|
return ret;
|
|
case "_notice_friendship_removed":
|
|
ret = ::msg(source, mc, data, vars, showingLog);
|
|
buddy = objectp(source) ? source -> qName() : source;
|
|
jid = mkjid(source);
|
|
// is this supposed to delete the groups as well?
|
|
// ::msg will delete this person from ppl so if
|
|
// we want to use pplgroups for channel routing
|
|
// we need to keep the two structures in sync..
|
|
// sind sie... wenn das hier dazu führt, dass
|
|
// der status im ppl auf "nix subscribed" gesetzt
|
|
// wird statt es zu loeschen
|
|
emit("<iq type='set'>"
|
|
"<query xmlns='jabber:iq:roster'>"
|
|
"<item jid='"+ jid +"' subscription='none'>"
|
|
"</item></query></iq>");
|
|
if (xbuddylist[buddy]) {
|
|
m_delete(xbuddylist, buddy);
|
|
}
|
|
return ret;
|
|
# ifndef ENTER_MEMBERS
|
|
case "_status_place_members":
|
|
int i;
|
|
string skip;
|
|
array(string) rendered;
|
|
string template, packet;
|
|
// this would have been done by ::msg
|
|
#ifdef PARANOID
|
|
unless (pointerp(vars["_list_members"]))
|
|
vars["_list_members"] = ({ vars["_list_members"] });
|
|
#endif
|
|
// copy still necessary here? TODO
|
|
vars["_list_members"] = map(copy(vars["_list_members"]),
|
|
(: return psyc_object($1) || $1; :));
|
|
#ifdef PARANOID
|
|
unless (pointerp(vars["_list_members_nicks"]))
|
|
vars["_list_members_nicks"] = ({ vars["_list_members_nicks"] });
|
|
#endif
|
|
rendered = renderMembers(vars["_list_members"],
|
|
vars["_list_members_nicks"]);
|
|
packet = "";
|
|
|
|
// this one is different from active.c
|
|
// we want local users to be shown with nicknames, even
|
|
// if we are in a remote room
|
|
jid = mkjid(source);
|
|
template = T("_status_place_members_each", "");
|
|
for(i = 0; i < sizeof(vars["_list_members"]); i++) {
|
|
if (vars["_list_members"][i] == ME) {
|
|
skip = vars["_list_members_nicks"][i];
|
|
continue;
|
|
}
|
|
packet += psyctext(template, ([
|
|
"_source_relay" : mkjid(vars["_list_members"][i]),
|
|
"_INTERNAL_source_jabber" : jid + "/" + RESOURCEPREP(rendered[i]),
|
|
"_INTERNAL_target_jabber" : myjidresource,
|
|
|
|
"_duty" : "none"
|
|
]));
|
|
}
|
|
template = T("_status_place_members_self", "");
|
|
packet += psyctext(template, ([
|
|
"_source_relay" : myjid,
|
|
"_INTERNAL_source_jabber" : jid + "/" + RESOURCEPREP(vars["_nick_local"] || vars["_nick"]),
|
|
"_INTERNAL_target_jabber" : myjidresource,
|
|
"_duty" : affiliations[source] || "none"
|
|
]));
|
|
emit(packet);
|
|
return;
|
|
# endif
|
|
}
|
|
// ::msg should get called at the *beginning* of this function
|
|
// but that would cause some new problems..
|
|
return ::msg(source, mc, data, vars, showingLog);
|
|
}
|
|
|
|
showFriends() {
|
|
// send presence for online friends
|
|
string packet = "";
|
|
mixed person;
|
|
int av;
|
|
|
|
foreach(person: m_indices(friends)) if (person) {
|
|
av = friends[person, FRIEND_AVAILABILITY];
|
|
packet += psyctext(T("_status_presence"+ avail2mc[av],
|
|
"<presence to='[_INTERNAL_target_jabber]' "
|
|
"from='[_INTERNAL_source_jabber]'/>"), ([
|
|
"_INTERNAL_target_jabber" : myjid,
|
|
"_INTERNAL_source_jabber" : mkjid(person),
|
|
"_description_presence" : "", // TODO: get these from state
|
|
"_INTERNAL_XML_description_presence" : "",
|
|
"_INTERNAL_mood_jabber" : "neutral"
|
|
]));
|
|
}
|
|
if (strlen(packet)) emit(packet);
|
|
P2(("%O jabberish showFriends: %O outputs as %O\n", ME, friends, packet))
|
|
}
|
|
|
|
logon() {
|
|
// language support is in server.c
|
|
vSet("scheme", "jabber");
|
|
vDel("layout");
|
|
vDel("agent");
|
|
vDel("query"); // server-side query would drive jabbers crazy as well
|
|
vDel("place"); // we do use this, but we don't autojoin it
|
|
#ifdef INPUT_NO_TELNET
|
|
input_to(#'feed, INPUT_IGNORE_BANG | INPUT_CHARMODE | INPUT_NO_TELNET);
|
|
#else
|
|
// enable_telnet(0, ME);
|
|
input_to(#'feed, INPUT_IGNORE_BANG | INPUT_CHARMODE);
|
|
#endif
|
|
nodeHandler = #'jabberMsg;
|
|
set_prompt("");
|
|
myjid = NODEPREP(MYLOWERNICK) +"@" + NAMEPREP(SERVER_HOST);
|
|
myjidresource = myjid +"/"+ RESOURCEPREP(resource);
|
|
P2(("%O ready to rumble (%O)\n", myjidresource, ME))
|
|
// reicht auch aufzurufen, wenn announce 0 returned..
|
|
showFriends();
|
|
return ::logon();
|
|
}
|
|
|
|
autojoin() {
|
|
// jabber as a protocol is unable to recognize a forced join
|
|
// into a room. there is a method for autojoin, but its
|
|
// client-based and does not work this way
|
|
return 1;
|
|
}
|
|
|
|
jabberMsg(XMLNode node) {
|
|
switch (node[Tag]) {
|
|
case "iq":
|
|
tag = node["@id"];
|
|
return iq(node);
|
|
case "message":
|
|
return message(node);
|
|
case "presence":
|
|
return presence(node);
|
|
default:
|
|
return ::jabberMsg(node);
|
|
}
|
|
}
|
|
|
|
presence(XMLNode node) {
|
|
// this ignores the resource completely
|
|
// but as long as it works...
|
|
// we could check that... but hey...
|
|
string target, host, res, nick, text;
|
|
|
|
isplacemsg = ISPLACEMSG(node["@to"]);
|
|
// note: same code is in mixin_parse...
|
|
if (!isplacemsg && getchild(node, "x", "http://jabber.org/protocol/muc#user")) {
|
|
isplacemsg = 2;
|
|
}
|
|
#ifndef _flag_disable_presence_directed_XMPP
|
|
if (node["@to"]) {
|
|
target = jid2ppl(node["@to"]);
|
|
if (isplacemsg) {
|
|
mixed *u = parse_uniform(XMPP + node["@to"]);
|
|
P2(("some kind of place stuff\n"))
|
|
if (node["@type"] == "unavailable") {
|
|
P2(("requesting to leave %O\n", target))
|
|
placeRequest(target,
|
|
# ifdef SPEC
|
|
"_request_context_leave"
|
|
# else
|
|
"_request_leave"
|
|
# endif
|
|
, 1);
|
|
place = 0; // should we do it when we receive the notice?
|
|
// anyway, w/out this we show up as still being
|
|
// in that room in /p
|
|
} else {
|
|
# ifdef ENTER_MEMBERS
|
|
// TODO: this might be needed and should work for remote rooms
|
|
// doing it in local rooms is a bad idea
|
|
if (is_formal(target))
|
|
placeRequest(target,
|
|
# ifdef SPEC
|
|
"_request_context_enter"
|
|
# else
|
|
"_request_enter"
|
|
# endif
|
|
"_again", 0, 1, ([
|
|
"_nick" : MYNICK,
|
|
"_nick_local" : u[UResource]
|
|
]));
|
|
else
|
|
placeRequest(target,
|
|
# ifdef SPEC
|
|
"_request_context_enter"
|
|
# else
|
|
"_request_enter"
|
|
# endif
|
|
"_again", 0, 1);
|
|
# else
|
|
P2(("teleporting to %O\n", target))
|
|
teleport(target, "_join", 0, 1);
|
|
# endif
|
|
}
|
|
# ifndef _flag_disable_module_friendship
|
|
} else if (node["@type"] == "subscribe") {
|
|
friend(0, 0, jid2ppl(node["@to"]));
|
|
} else if (node["@type"] == "unsubscribe") {
|
|
friend(1, 0, jid2ppl(node["@to"]));
|
|
# endif // _flag_disable_module_friendship
|
|
} else if (abbrev(XMPP, target)) {
|
|
// if the person is not on our buddylist,
|
|
// this is usually a muc join
|
|
// but i am not sure if there are other uses of
|
|
// presence in jabber
|
|
# ifdef JABBER_TRANSPARENCY
|
|
mapping vars = ([ "_nick" : MYNICK ]);
|
|
mixed *u = parse_uniform(XMPP + node["@to"]);
|
|
P3(("jtranz presence to %O\n", target))
|
|
|
|
// TODO: presence out! im prinzip dem places mapping
|
|
// funktinal sehr aehnlich, sofern das ziel nicht
|
|
// auf unserer buddylist ist
|
|
// wir muessen halt beim logout unavailable schicken
|
|
// an alle auf der liste
|
|
// unless(mappingp(presence_out)) presence_out = ([ ]);
|
|
vars["_jabber_XML"] = innerxml;
|
|
|
|
// TODO: wir fliegen wir mit _host_XMPP auf die
|
|
// NASE.
|
|
// wir muessen die resource in die vars stecken...
|
|
vars["_INTERNAL_source_jabber"] = myjidresource;
|
|
if (node["@type"] == "unavailable") {
|
|
// TODO: delete this from presence out
|
|
sendmsg(target,
|
|
"_jabber_presence_unavailable",
|
|
"[_nick] is sending you a jabber presence of type unavailable.",
|
|
vars);
|
|
} else {
|
|
// TODO: add this to presence out
|
|
sendmsg(target,
|
|
"_jabber_presence",
|
|
"[_nick] is sending you a jabber presence.",
|
|
vars);
|
|
}
|
|
# endif
|
|
} else {
|
|
// TODO: what can we do in this case?
|
|
// we can look at our buddylist, if target is a member
|
|
// then this is a directed presence
|
|
}
|
|
} /* end of directed presence handling */
|
|
#endif // _flag_disable_presence_directed_XMPP
|
|
else if (node["@type"] == "unavailable") {
|
|
// announce OFFLINE manually here? no.. see comment in person:quit()
|
|
// _flag_enable_manual_announce_XMPP is nothing we really need.. i think
|
|
return quit();
|
|
}
|
|
#ifdef AVAILABILITY_AWAY
|
|
else if (node["/show"]) {
|
|
// this is one of the so-called "presence broadcasts"
|
|
announce(jabber2avail[node["/show"] && node["/show"][Cdata]], 0, 0,
|
|
node["/status"] && node["/status"][Cdata]);
|
|
} else {
|
|
// TODO: quiet?
|
|
announce(AVAILABILITY_HERE);
|
|
}
|
|
#endif // AVAILABILITY_AWAY
|
|
}
|
|
|
|
|
|
message(XMLNode node) {
|
|
XMLNode helper;
|
|
string target, host, nick;
|
|
string res;
|
|
mixed t, msg;
|
|
mixed *u;
|
|
|
|
unless (node["@to"] && strlen(node["@to"])) {
|
|
// this could well become valid in terms of a psyc friendcast
|
|
P0(("%O jabber message() without 'to'-attribute\n"))
|
|
return;
|
|
}
|
|
target = jid2ppl(node["@to"]);
|
|
|
|
unless (u = parse_uniform(XMPP + node["@to"])) { D("impossible!\n"); }
|
|
isplacemsg = ISPLACEMSG(node["@to"]);
|
|
|
|
if (is_localhost(lower_case(node["@to"]))) {
|
|
// it's too unusual to have commands without cmdchar
|
|
// so let's use input() instead of cmd() here
|
|
// IMHO this should check if input is a cmd
|
|
if (u[UResource] == "announce") {
|
|
// setting a MOTD
|
|
P1(("%O TODO: setting a MOTD %O\n", ME, node))
|
|
#if 0
|
|
} else if (u[UResource] == "announce/online") {
|
|
// sending a message to all online users
|
|
parsecmd("yell " + node["/body"][Cdata]);
|
|
#endif
|
|
} else {
|
|
// was input() up to recently.
|
|
if (node["/body"]) parsecmd(node["/body"][Cdata]);
|
|
else {
|
|
P1(("%O got msg w/out body: %O\n", ME, node))
|
|
}
|
|
}
|
|
return;
|
|
#ifndef STRICT_NO_JID_HACKS
|
|
} else if (node["@to"] == "xmpp") {
|
|
// mh... someone talking to our virtual xmpp gateway :-)
|
|
P1(("%O talking to me?? %O\n", ME, node))
|
|
#endif
|
|
}
|
|
|
|
// lets ignore typing notifications and such
|
|
// they are evil for s2s karma
|
|
unless((msg = node["/body"])) {
|
|
P1(("%O talking, but nothing to say %O\n", ME, node))
|
|
return;
|
|
}
|
|
unless(stringp(msg = msg[Cdata])) {
|
|
P1(("%O why isn't msg a string!? %O of %O\n", ME, msg, node))
|
|
return;
|
|
}
|
|
if ((helper = getchild(node, "x", "jabber:x:oob")) && helper["/url"]) {
|
|
// now this is what i call a dirty hack,
|
|
// still better than ignoring it however..
|
|
//msg += " < "+ node["/x"]["/url"] +" >";
|
|
// we need formats for _message, huh?
|
|
// so we can put this into a var where it belongs..
|
|
// well, we don't have psyc-cmd() yet anyhow, so let's
|
|
// just append it in case it's an argument for a command
|
|
msg += " " + helper["/url"][Cdata];
|
|
}
|
|
if (node["@type"] == "groupchat") {
|
|
P4(("groupchat in %O: %O, %O. place is %O\n",
|
|
ME, target, v("place"), place))
|
|
// _message_public
|
|
// TODO: this wont work with remote places at all...
|
|
if (isplacemsg) {
|
|
P4(("isplacemsg in %O: %O == %O (%O) ?\n", ME,
|
|
target, v("place"), place))
|
|
if ((!v("place") || stricmp(target, v("place")))
|
|
&& (t = find_place(target))) {
|
|
place = t;
|
|
vSet("place", target);
|
|
P4(("find_place in %O: %O is the place for love\n", ME, t))
|
|
}
|
|
input(msg);
|
|
} else {
|
|
#ifdef JABBER_TRANSPARENCY
|
|
mapping vars = ([ "_nick" : MYNICK ]);
|
|
|
|
P3(("jtranz groupchat to %O\n", target))
|
|
vars["_jabber_XML"] = innerxml;
|
|
// TODO: dirty hack
|
|
vars["_INTERNAL_target_jabber"] = target[5..];
|
|
vars["_INTERNAL_source_jabber"] = myjidresource;
|
|
sendmsg(target,
|
|
"_jabber_message_groupchat",
|
|
"[_nick] is sending you a jabber groupchat message.",
|
|
vars);
|
|
#endif
|
|
}
|
|
} else {
|
|
if (isplacemsg) { // damn similar code in gateway.c!!!!
|
|
#if 0 // STRICTLY UNFRIENDLY NON-FUN TREATMENT
|
|
sendmsg(ME, "_failure_unsupported_function_whisper",
|
|
"Routing private messages through groupchat managers is dangerous to your privacy and therefore disallowed. Please communicate with the person directly.");
|
|
#else // MAKE FUN OF ST00PID JABBER USERS VARIANT
|
|
mixed o = find_place(u[UUser][1..]);
|
|
if (objectp(o) && node["/body"]) {
|
|
// handle this by doing "flüstern" in room ;)
|
|
// <from> whispers to <to>: <cdata>
|
|
P0(("private message in place.. from %O to %O\n",
|
|
ME, o))
|
|
sendmsg(o, "_message_public_whisper",
|
|
node["/body"][Cdata], ([ "_nick_target": u[UNick]]));
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
// _message_private
|
|
nick = jid2ppl(node["@to"]);
|
|
// P2(("nick: %s\n", nick))
|
|
// TODO: vars["_time_INTERNAL"] => jabber:x:delay, JEP-0091
|
|
#if 0
|
|
if (msg[0..2] == "/me") {
|
|
parsecmd(msg, nick);
|
|
} else {
|
|
tell(nick, msg);
|
|
}
|
|
#else
|
|
input(msg, nick);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
iq(XMLNode node) {
|
|
string target;
|
|
string friend;
|
|
XMLNode helper;
|
|
XMLNode iqchild;
|
|
string t;
|
|
string packet;
|
|
string template;
|
|
|
|
mixed *vars;
|
|
|
|
vars = ([ "_nick": MYNICK ]);
|
|
target = jid2ppl(node["@to"]);
|
|
isplacemsg = stringp(target) && strlen(target) && ISPLACEMSG(target);
|
|
|
|
P3(("+++ %O IQ node %O\n", ME, node))
|
|
iqchild = getiqchild(node);
|
|
unless (iqchild && mappingp(iqchild)) switch(node["@type"]) {
|
|
case "get":
|
|
case "set":
|
|
case "result":
|
|
case "error":
|
|
P1(("%O got empty iq %O\n", ME, node))
|
|
return;
|
|
default:
|
|
P1(("%O got invalid iq %O\n", ME, node))
|
|
return;
|
|
}
|
|
helper = iqchild;
|
|
|
|
switch(iqchild["@xmlns"]) {
|
|
case "jabber:iq:version":
|
|
switch(node["@type"]) {
|
|
case "get":
|
|
// TODO: as we need to fiddle with every tag, we probably could
|
|
// overload sendmsg() of uni.c
|
|
m_delete(vars, "_tag");
|
|
sendmsg(target, "_request_version", 0, vars, 0, 0,
|
|
(:
|
|
$4["_tag_reply"] = tag;
|
|
msg($1, $2, $3, $4);
|
|
return;
|
|
:));
|
|
break;
|
|
case "set":
|
|
// HUH?
|
|
break;
|
|
case "result":
|
|
break;
|
|
case "error":
|
|
break;
|
|
}
|
|
break;
|
|
case "jabber:iq:last":
|
|
// TODO: request idle time
|
|
break;
|
|
case "urn:xmpp:ping":
|
|
switch(node["@type"]) {
|
|
case "get":
|
|
case "set": // I dont know why xep 0199 uses set... its a request
|
|
// it got fixed in version 0.2 of the xep
|
|
w("_echo_ping", 0, vars);
|
|
break;
|
|
}
|
|
break;
|
|
#if !defined(REGISTERED_USERS_ONLY) && !defined(_flag_disable_registration_XMPP)
|
|
case "jabber:iq:register":
|
|
switch(node["@type"]) {
|
|
case "get":
|
|
if (node["@to"] && node["@to"] != myjid) {
|
|
// query this information from someone else
|
|
// e.g. a transport
|
|
} else {
|
|
emit(sprintf("<iq type='result' id='%s'>"
|
|
"<query xmlns='jabber:iq:register'>"
|
|
"<password/>"
|
|
"<instructions/>"
|
|
"<name>%s</name>"
|
|
"<email>%s</email>"
|
|
"</query></iq>",
|
|
tag,
|
|
v("publicname") || "",
|
|
v("email") || ""));
|
|
}
|
|
break;
|
|
case "set":
|
|
if (node["@to"] && node["@to"] != myjid) {
|
|
// send this information from someone else
|
|
// e.g. a transport
|
|
} else {
|
|
if (t = helper["/email"])
|
|
vSet("email", t[Cdata]);
|
|
if (t = helper["/name"])
|
|
vSet("publicname", t[Cdata]);
|
|
// TODO: better use legal_password() for that
|
|
if ((t = helper["/password"]) && strlen(t[Cdata]))
|
|
vSet("password", t[Cdata]);
|
|
save();
|
|
emit(sprintf("<iq type='result' id='%s'/>", tag));
|
|
}
|
|
break;
|
|
case "result":
|
|
break;
|
|
case "error":
|
|
break;
|
|
}
|
|
#endif // jabber:iq:register
|
|
case "jabber:iq:roster":
|
|
switch(node["@type"]) {
|
|
case "get":
|
|
// TODO: this assumes that to is unset and the query is
|
|
// to itself
|
|
hasroster = 1;
|
|
packet = sprintf("<iq type='result' to='%s' id='%s'>"
|
|
"<query xmlns='jabber:iq:roster'>",
|
|
myjid, tag);
|
|
// TODO: listAcq does the same basically
|
|
foreach (friend : ppl) {
|
|
mixed *u;
|
|
string ro;
|
|
mixed *va;
|
|
string variant = "";
|
|
|
|
if (is_formal(friend)) t = mkjid(friend);
|
|
else
|
|
t = friend + "@" SERVER_HOST;
|
|
switch (ppl[friend][PPL_NOTIFY]) {
|
|
case PPL_NOTIFY_MUTE:
|
|
break;
|
|
case PPL_NOTIFY_DELAYED_MORE:
|
|
case PPL_NOTIFY_DELAYED:
|
|
// wieso sind die mute genauso wie die
|
|
// delayed?
|
|
variant = "_delayed";
|
|
break;
|
|
case PPL_NOTIFY_IMMEDIATE:
|
|
variant = "_immediate";
|
|
break;
|
|
// these two are handled differently
|
|
case PPL_NOTIFY_OFFERED:
|
|
case PPL_NOTIFY_PENDING:
|
|
continue;
|
|
default:
|
|
// va["_acquaintance"] = "none";
|
|
}
|
|
// TODO: subscription states
|
|
//
|
|
// we append _roster because this output format is not
|
|
// compatible
|
|
// to the /show command.
|
|
template = T("_list_acquaintance_notification" + variant + "_roster", "");
|
|
ro = psyctext(template, ([
|
|
"_friend" : t,
|
|
"_list_groups" : IMPLODE_XML(xbuddylist[friend], "<group>") || "",
|
|
#ifdef ALIASES
|
|
"_nick" : raliases[friend] || friend
|
|
#else
|
|
"_nick" : friend
|
|
#endif
|
|
]));
|
|
if (ro) packet += ro;
|
|
else {
|
|
P2(("%O empty result for %O with buddy %O\n",
|
|
ME, "_list_acquaintance_notification" + variant + "_roster", friend))
|
|
}
|
|
}
|
|
emit(packet +"</query></iq>");
|
|
// here we should redisplay requests skipped above
|
|
// oder kann man die einfach reinmixen - nein
|
|
// foreach skipped
|
|
// _list_acquaintance_notification_pending
|
|
// _list_acquaintance_notification_offered
|
|
showFriends();
|
|
break;
|
|
case "set":
|
|
helper = helper["/item"];
|
|
if (!mappingp(helper)) {
|
|
P1(("no item in iq set %O from %O in %O\n",
|
|
node, previous_object(), ME))
|
|
// FIXME: what is the correct behaviour
|
|
// in this case?
|
|
return;
|
|
}
|
|
if (helper["@subscription"] == "remove") {
|
|
string buddy = jid2ppl(helper["@jid"]);
|
|
#ifndef _flag_disable_module_friendship
|
|
P2(("remove %O from roster\n", helper["@jid"]))
|
|
friend(1, 0, buddy);
|
|
#endif
|
|
m_delete(xbuddylist, buddy);
|
|
emit(sprintf("<iq type='result' id='%s'/>", tag));
|
|
} else {
|
|
// note: "<group/> is opaque to jabber servers
|
|
string subscription;
|
|
string jid;
|
|
string buddy;
|
|
string name;
|
|
|
|
jid = helper["@jid"];
|
|
unless (stringp(jid)) {
|
|
P0(("invalid jid for %O in %O\n", name, ME))
|
|
break;
|
|
}
|
|
buddy = jid2ppl(jid);
|
|
unless (xbuddylist[buddy])
|
|
subscription = "none";
|
|
else
|
|
subscription = "both";
|
|
|
|
// we should store the alias name also.. TODO
|
|
if (helper["/group"]) {
|
|
if (nodelistp(helper["/group"])) {
|
|
xbuddylist[buddy] = ({ });
|
|
foreach(XMLNode iter : helper["/group"]) {
|
|
if (t = iter[Cdata])
|
|
xbuddylist[buddy] += ({ t });
|
|
}
|
|
} else if (t = helper["/group"][Cdata]) {
|
|
xbuddylist[buddy] = ({ t });
|
|
} else
|
|
xbuddylist[buddy] = ({ });
|
|
}
|
|
name = helper["@name"];
|
|
// USE ALIAS HERE
|
|
// if (name) xbuddylist[buddy][XBUDDY_NICK] = name;
|
|
// maybe we need to do this...
|
|
// exodus always sends _all_ groups a nick
|
|
// has to be in. but on the other hand we
|
|
// may want to act differently with psyc clients
|
|
//
|
|
// roster push
|
|
// maybe we can just implode the former "groups" here
|
|
unless (stringp(name)) {
|
|
// nicht schlimm.. hat der user das alias-feld
|
|
// einfach leergelassen
|
|
P4(("no name %O for %O in %O\n", name, jid, ME))
|
|
name = "";
|
|
}
|
|
emit(sprintf("<iq type='set'>" // TODO: needs a id
|
|
"<query xmlns='jabber:iq:roster'>"
|
|
"<item jid='%s' name='%s' "
|
|
"subscription='%s'>%s</item></query></iq>",
|
|
jid, name, subscription,
|
|
IMPLODE_XML(xbuddylist[buddy], "<group>")));
|
|
if (stringp(tag))
|
|
emit(sprintf("<iq type='result' id='%s'/>", tag));
|
|
#ifndef _flag_disable_module_friendship
|
|
// client will send presence subscribe in opposite direction
|
|
// so we shouldn't need this. alas, there seems to be a bug
|
|
// with re-subscribe
|
|
friend(0, 0, buddy);
|
|
#else
|
|
save();
|
|
#endif
|
|
}
|
|
break;
|
|
case "result":
|
|
// may happen in reply to roster pushes
|
|
break;
|
|
case "error":
|
|
}
|
|
break;
|
|
case "http://jabber.org/protocol/disco#info":
|
|
switch(node["@type"]) {
|
|
case "get":
|
|
if (!node["@to"])
|
|
sendmsg(ME, "_request_list_feature", 0, vars);
|
|
else if (is_localhost(lower_case(node["@to"])))
|
|
sendmsg("/", "_request_list_feature", 0, vars);
|
|
/* else... TODO */
|
|
break;
|
|
case "set":
|
|
break;
|
|
case "result":
|
|
break;
|
|
case "error":
|
|
break;
|
|
}
|
|
break;
|
|
case "http://jabber.org/protocol/disco#items":
|
|
// send a list of rooms to the client
|
|
switch(node["@type"]) {
|
|
case "get":
|
|
if (!node["@to"])
|
|
// "my" places - let person.c handle this
|
|
sendmsg(ME, "_request_list_item", 0, vars);
|
|
else if (is_localhost(lower_case(node["@to"])))
|
|
// server's places - let root.c handle this
|
|
sendmsg("/", "_request_list_item", 0, vars);
|
|
/* else... TODO */
|
|
break;
|
|
case "set": // never saw this
|
|
break;
|
|
case "result":
|
|
break;
|
|
case "error":
|
|
break;
|
|
}
|
|
break;
|
|
case "jabber:iq:private":
|
|
switch(node["@type"]) {
|
|
case "get":
|
|
helper = helper["/storage"];
|
|
if (helper && helper["@xmlns"] =="storage:bookmarks") {
|
|
// private xml storage, used for storing conference
|
|
// room bookmarks and autojoining them
|
|
packet = sprintf("<iq type='result' id='%s'>"
|
|
"<query xmlns='jabber:iq:private'>"
|
|
"<storage xmlns='storage:bookmarks'>",
|
|
tag);
|
|
// hey wait.. we are sending the list of places here..
|
|
// why is it i have never seen a jabber client actually
|
|
// executing autojoins? FIXME
|
|
if (v("subscriptions"))
|
|
foreach (string s in v("subscriptions")) {
|
|
string jid;
|
|
if (is_formal(s)) { // abbrev psyc:// is true
|
|
// watch out, this assumes that only psyc rooms
|
|
// can be entered remote
|
|
// is this way valid for remote psyc rooms?
|
|
// should use makejid or whatever this is called
|
|
jid = PLACEPREFIX + s[7..];
|
|
} else {
|
|
jid = PLACEPREFIX + s + "@" SERVER_HOST;
|
|
}
|
|
packet += sprintf("<conference name='%s' "
|
|
"autojoin='1' "
|
|
"jid='%s'>"
|
|
"<nick>%s</nick>"
|
|
"</conference>",
|
|
s, jid, MYNICK);
|
|
}
|
|
emit(packet + "</storage></query></iq>");
|
|
} else {
|
|
vars["_INTERNAL_source_jabber"] = node["@to"] || myjid;
|
|
vars["_INTERNAL_target_jabber"] = myjidresource;
|
|
vars["_tag_reply"] = tag;
|
|
render("_error_unsupported_method", 0, vars);
|
|
}
|
|
break;
|
|
case "set":
|
|
/* updating your subscriptions / private xml storage */
|
|
helper = helper["/storage"];
|
|
if (helper && helper["@xmlns"] =="storage:bookmarks"){
|
|
// TODO:
|
|
}
|
|
vars["_INTERNAL_source_jabber"] = node["@to"] || myjid;
|
|
vars["_INTERNAL_target_jabber"] = myjidresource;
|
|
vars["_tag_reply"] = tag;
|
|
render("_error_unsupported_method", 0, vars);
|
|
break;
|
|
case "result": // never saw this
|
|
break;
|
|
case "error": // never saw this
|
|
break;
|
|
}
|
|
break;
|
|
case "vcard-temp":
|
|
switch(node["@type"]) {
|
|
case "get":
|
|
vars["_tag"] = node["@id"];
|
|
if (node["@to"] && node["@to"] != myjid) {
|
|
/* request vcard from someone else */
|
|
} else {/* fake your own vcard */
|
|
emit("<iq type='result' id='" + tag + "' "
|
|
"to='" + (node["@from"] || myjid) + "' "
|
|
"from='" + myjid + "'>"
|
|
"<vCard xmlns='vcard-temp'>"
|
|
"</vCard></iq>");
|
|
|
|
}
|
|
break;
|
|
case "set":
|
|
if (node["@to"] && node["@to"] != myjid) {
|
|
// setting the vcard of someone else is an error
|
|
emit("<iq type='error' id='" + tag + "'>"
|
|
"<x xmlns='vcard-temp'/>"
|
|
"<error code='403' type='cancel'/>"
|
|
"<forbidden xmlns='" NS_XMPP "xmpp-stanzas'/>"
|
|
"</error>"
|
|
"</iq>");
|
|
} else { // update your own vcard
|
|
mixed mvars;
|
|
// watch out, dont use this email for v("email")-setting
|
|
// this one is public while v("email") is more
|
|
// private
|
|
mvars = convert_profile(node["/vCard"], "jCard");
|
|
P2(("%O received vCard from client (%O)\n", ME, sizeof(mvars)))
|
|
emit("<iq type='result' id='" + tag + "'/>");
|
|
request(ME, "_store", mvars);
|
|
return;
|
|
}
|
|
break;
|
|
case "result": // never saw this
|
|
break;
|
|
case "error": // never saw this
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
#if 0
|
|
case "urn:xmpp:blocking":
|
|
/* JEP-0191 style privacy lists
|
|
* When implementing this, pay special attention to
|
|
* http://www.xmpp.org/extensions/xep-0191.html#matching
|
|
* although I dont think that we will implement the resource part
|
|
*
|
|
* TODO: I would like to have an qPerson api that does this matching
|
|
* automatically ontop of ppl
|
|
*
|
|
* yuck... totgeburt
|
|
*/
|
|
switch(node["@type"]) {
|
|
case "get": /* return the whole blocklist */
|
|
packet = "<iq type='result'";
|
|
if (tag) packet += " id='" + tag + "'";
|
|
packet += "><blocklist xmlns='urn:xmpp:blocking'>";
|
|
foreach(mixed p, mixed val : ppl) {
|
|
if (val[PPL_DISPLAY] == PPL_DISPLAY_NONE)
|
|
packet += "<item jid='" + mkjid(p) + "'/>";
|
|
}
|
|
packet += "</blocklist></iq>";
|
|
break;
|
|
case "set":
|
|
if (iqchild["/item"] && !nodelistp(iqchild["/item"]))
|
|
iqchild["/item"] = ({ iqchild["/item"] });
|
|
unless(iqchild["/item"]) { /* clear the blocklist */
|
|
foreach(mixed p, mixed val : ppl) {
|
|
if (val[PPL_DISPLAY] == PPL_DISPLAY_NONE)
|
|
sPerson(p, PPL_DISPLAY, PPL_DISPLAY_DEFAULT);
|
|
}
|
|
} else {
|
|
int block = iqchild[Tag] == "block";
|
|
foreach (helper : iqchild["/item"]) {
|
|
/* add/remove each item to/from the blocklist */
|
|
if (block) {
|
|
/* TODO:
|
|
* integrate with friendship (/cancel) here!
|
|
*/
|
|
sPerson(helper["@jid"], PPL_DISPLAY, PPL_DISPLAY_NONE);
|
|
} else
|
|
sPerson(helper["@jid"], PPL_DISPLAY, PPL_DISPLAY_DEFAULT);
|
|
}
|
|
}
|
|
emit("<iq type='result'" + (tag ? "id='" + tag + "'" : "") + "/>");
|
|
/* push the updated items to other clients - that is delta
|
|
* just like roster
|
|
*/
|
|
// as we only have a single client this does not apply currently
|
|
// do pushes have to go to the 'current' client also?!
|
|
break;
|
|
case "result":
|
|
break;
|
|
case "error":
|
|
break;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
P1(("%O got IQ with unknown namespace: %O\n", ME, node))
|
|
switch(node["@type"]) {
|
|
case "get":
|
|
if (node["@to"]) {
|
|
#ifdef JABBER_TRANSPARENCY
|
|
vars["_jabber_XML"] = innerxml;
|
|
sendmsg(target,
|
|
"_jabber_iq_get",
|
|
"[_nick] is sending you a jabber iq get.", vars);
|
|
P0(("iq get to %O from %O\n", target, ME))
|
|
#else
|
|
/* what else? */
|
|
#endif
|
|
} else { /* service-unavailable */
|
|
vars["_INTERNAL_source_jabber"] = node["@to"]||myjid;
|
|
vars["_INTERNAL_target_jabber"] = myjid;
|
|
vars["_tag_reply"] = tag;
|
|
render("_error_unsupported_method", 0, vars);
|
|
}
|
|
break;
|
|
case "set":
|
|
if (node["@to"]) {
|
|
#ifdef JABBER_TRANSPARENCY
|
|
vars["_tag"] = tag;
|
|
vars["_jabber_XML"] = innerxml;
|
|
sendmsg(target, "_jabber_iq_set",
|
|
"[_nick] is sending you a jabber iq set.", vars);
|
|
return;
|
|
#else
|
|
/* what else? */
|
|
#endif
|
|
} else { /* service-unavailable */
|
|
vars["_INTERNAL_source_jabber"] = node["@to"]||myjid;
|
|
vars["_INTERNAL_target_jabber"] = myjid;
|
|
vars["_tag_reply"] = tag;
|
|
render("_error_unsupported_method", 0, vars);
|
|
}
|
|
break;
|
|
case "result":
|
|
if (node["@to"]) {
|
|
#ifdef JABBER_TRANSPARENCY
|
|
vars["_tag_reply"] = tag;
|
|
vars["_jabber_XML"] = innerxml; // || "" ???
|
|
sendmsg(target, "_jabber_iq_result",
|
|
"[_nick] is sending you a jabber iq result.", vars);
|
|
#else
|
|
/* what else? */
|
|
#endif
|
|
} else {
|
|
vars["_INTERNAL_source_jabber"] = node["@to"]||myjid;
|
|
vars["_INTERNAL_target_jabber"] = myjid;
|
|
vars["_tag_reply"] = tag;
|
|
render("_error_unsupported_method", 0, vars);
|
|
}
|
|
break;
|
|
case "error":
|
|
vars["_tag_reply"] = tag;
|
|
if (node["@to"]) {
|
|
#ifdef JABBER_TRANSPARENCY
|
|
vars["_jabber_XML"] = innerxml;
|
|
sendmsg(target, "_jabber_iq_error",
|
|
"[_nick] is sending you a jabber iq error.", vars);
|
|
#else
|
|
/* what else? */
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// this converts a user@host into a local nick or uniform
|
|
// in a potentially not too consistent way
|
|
string jid2ppl(string jid) {
|
|
string node, host, resource;
|
|
string t;
|
|
|
|
P3(("jid2ppl saw %O\n", jid))
|
|
unless(jid) return 0;
|
|
//
|
|
// TODO: what if jid == SERVER_HOST?
|
|
// or alike?
|
|
sscanf(jid, "%s@%s", node, host);
|
|
unless (host) { // it is a bare hostname
|
|
host = jid;
|
|
node = "";
|
|
}
|
|
// never happens, because node is "" not 0
|
|
// unless (node) {
|
|
// return "psyc://" + host;
|
|
// }
|
|
sscanf(host, "%s/%s", host, resource);
|
|
#if 1 // wieso 0??? sendmsg() hätte das hinkriegen sollen.. war der gedanke
|
|
if (is_localhost(lower_case(host))) { // local user
|
|
P4(("is_localhost? %O, YES, then use %O\n", host, node))
|
|
// TODO: what about returning objects?
|
|
if (strlen(node) && ISPLACEMSG(node)) return PREFIXFREE(node);
|
|
return node;
|
|
}
|
|
# if 1
|
|
else if (strlen(node) && ISPLACEMSG(node)) {
|
|
return "psyc://" + host + "/@" + PREFIXFREE(node);
|
|
}
|
|
# endif
|
|
P4(("is_localhost? %O, NO, don't use %O\n", host, node))
|
|
#endif
|
|
#if 0
|
|
else if (host == "sip." SERVER_HOST) {
|
|
return "sip:" + replace(node, "%", "@");
|
|
}
|
|
else
|
|
#endif
|
|
#ifndef STRICT_NO_JID_HACKS
|
|
if (host == "xmpp") {
|
|
// send message to xmpp:node while replacing first % with @
|
|
unless(node) return 0; // iq query to "xmpp", which is our
|
|
// virtual gateway
|
|
t = XMPP + replace(node, "%", "@");
|
|
if (resource) t += "/" + resource;
|
|
// should it be RESOURCEPREP(resource) here? --lynX
|
|
return t;
|
|
}
|
|
else
|
|
#endif
|
|
#if 0 // sendmsg does this part, too
|
|
// TODO: maybe we need some kind of support for remote
|
|
// rooms here. As usual, we will use the # prefix for that
|
|
if (node[0] == '*' || node[0] == '~' || node[0] == '$') {
|
|
return "psyc://" + host + "/" + node;
|
|
}
|
|
#endif
|
|
// leave it to sendmsg() to figure out what to do
|
|
return jid;
|
|
}
|
|
|
|
varargs string mkjid(mixed who, mixed vars, mixed ignore_nick, mixed ignore_context, string target) {
|
|
if (stringp(who) && strlen(who) && !isplacemsg) {
|
|
string t;
|
|
mixed *u = parse_uniform(who);
|
|
|
|
unless (u) {
|
|
P3(("mkjid leaving %O as is\n", who))
|
|
return who; // it already _is_ a jid!
|
|
}
|
|
switch(u[UScheme]) {
|
|
case "psyc":
|
|
#if 0
|
|
// let psyc users be jidded as ~nick@host? would be cleaner
|
|
// but no.. we want to be jabber-upgradable and SWITCH2PSYC
|
|
// is a reality.. vive la legacy compatibility!
|
|
t = u[UResource] +"@"+ u[UHost];
|
|
#else
|
|
if ((t = u[UResource]) && strlen(t) > 1) {
|
|
// or let psyc users be the same person as on xmpp?
|
|
// YES we want transparent upgrades from xmpp to psyc!
|
|
if (t[0] == '@')
|
|
t = PLACEPREFIX+ u[UNick] +"@"+ u[UHost];
|
|
else
|
|
t = u[UNick] +"@"+ u[UHost];
|
|
} else {
|
|
// the usual "shouldn't happen" case which however does
|
|
t = u[UHost];
|
|
}
|
|
#endif
|
|
// ... and what about the resource in jabber sense?
|
|
P3(("mkjid encoding %O as %O (psyc)\n", who, t))
|
|
return t;
|
|
#if 1 // let xmpp users have their normal jids
|
|
case "xmpp":
|
|
case 0:
|
|
t = u[UUserAtHost] || u[UUser] +"@"+ u[UHost];
|
|
t = u[UResource] ? t+"/"+u[UResource] : t;
|
|
// should we use RESOURCEPREP(resource) here? --lynX
|
|
P3(("mkjid encoding %O as %O (xmpp)\n", who, t))
|
|
return t;
|
|
#endif
|
|
default:
|
|
// do funny user%host@scheme encoding
|
|
// could also croak back and deny this communication..
|
|
if (u[UUser]) {
|
|
t = u[UUser] +"%"+ u[UHost] +"@"+ u[UScheme];
|
|
t = u[UResource] ? t+"/"+u[UResource] : t;
|
|
// should we use RESOURCEPREP(resource) here? --lynX
|
|
}
|
|
else if (u[UResource]) {
|
|
t = u[UResource] +"%"+ u[UHost] +"@"+ u[UScheme];
|
|
}
|
|
else t = u[UHost] +"@"+ u[UScheme];
|
|
P3(("mkjid encoding %O as %O\n", who, t))
|
|
return t;
|
|
}
|
|
}
|
|
P3(("calling regular ::mkjid for %O\n", who))
|
|
return ::mkjid(who, vars, ignore_nick, ignore_context, target, SERVER_HOST);
|
|
}
|
|
|
|
// message rendering a la jabber
|
|
w(string mc, string data, mapping vars, mixed source) {
|
|
mixed t;
|
|
|
|
unless (mappingp(vars)) vars = ([]);
|
|
else if (vars["_nick_verbatim"]) vars["_nick"] = vars["_nick_verbatim"];
|
|
// ^^ this is a temporary workaround until we fix the real problem!
|
|
|
|
switch (mc) {
|
|
case "_notice_login":
|
|
// suppresses the jabber:iq:auth reply in the SASL case
|
|
unless (stringp(tag)) return;
|
|
break;
|
|
case "_notice_session_end":
|
|
// jabber clients do not want to know
|
|
// (they already closed the connection when we get here)
|
|
return;
|
|
case "_error_status_place_matches":
|
|
P2(("still _error_status_place_matches?\n"))
|
|
return;
|
|
case "_echo_request_friendship":
|
|
vars["_list_groups"] = xbuddylist[vars["_nick"]] || "";
|
|
vars["_nick"] = mkjid(vars["_nick"]);
|
|
break;
|
|
/* copy/paste from active.c, BAD */
|
|
case "_notice_list_feature":
|
|
case "_notice_list_feature_person":
|
|
case "_notice_list_feature_place":
|
|
case "_notice_list_feature_server":
|
|
case "_notice_list_feature_newsfeed":
|
|
#ifndef _flag_disable_query_server
|
|
mixed id2jabber = shared_memory("disco_identity");
|
|
mixed feat2jabber = shared_memory("disco_features");
|
|
unless (mappingp(id2jabber)) return 1;
|
|
vars["_identity"] = id2jabber[vars["_identity"]] || vars["_identity"];
|
|
vars["_list_feature"] = implode(map(vars["_list_feature"],
|
|
(: return "<feature var='" + feat2jabber[$1] + "'/>"; :)), "");
|
|
break;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
case "_notice_list_item":
|
|
t = "";
|
|
// same stuff in user.c (what happened to code sharing?)
|
|
for (int i = 0; i < sizeof(vars["_list_item"]); i++) {
|
|
t += "<item name='"+ xmlquote(vars["_list_item_description"][i]) +"'";
|
|
if (vars["_list_item"] && vars["_list_item"][i])
|
|
t += " jid='" + mkjid(vars["_list_item"][i]) + "'";
|
|
if (vars["_list_item_node"] && vars["_list_item_node"][i])
|
|
t += " node='" + vars["_list_item_node"][i] + "'";
|
|
t += "/>";
|
|
}
|
|
vars["_list_item"] = t;
|
|
break;
|
|
}
|
|
|
|
if (source)
|
|
determine_sourcejid(source, vars);
|
|
else {
|
|
vars["_INTERNAL_source_jabber"] = SERVER_HOST;
|
|
vars["_INTERNAL_source_jabber_bare"] = SERVER_HOST;
|
|
}
|
|
// determine_targetjid(target, vars);
|
|
unless (vars["_INTERNAL_target_jabber"])
|
|
vars["_INTERNAL_target_jabber"] = myjidresource;
|
|
unless (vars["_INTERNAL_target_jabber_bare"])
|
|
vars["_INTERNAL_target_jabber_bare"] = myjid;
|
|
|
|
if (vars["_place"]) vars["_place"] = mkjid(vars["_place"]);
|
|
// TODO: this tag strategy needs to be reworked...
|
|
// the unlesses are because sometimes the psyced does tagging internally
|
|
// or if the packet is arriving from remote
|
|
unless (vars["_tag"]) vars["_tag"] = tag;
|
|
unless (vars["_tag_reply"]) vars["_tag_reply"] = tag;
|
|
if (vars["_list_groups"])
|
|
vars["_list_groups"] = IMPLODE_XML(vars["_list_groups"], "<group>");
|
|
#if 0
|
|
if (stringp(data) && strstr(data, "r00t") >= 0) {
|
|
P0(("user:w(%O, %O, %O, %O)\n", mc, data, vars, source))
|
|
}
|
|
#endif
|
|
unless (interactive(ME)) {
|
|
P1(("%O not interactive. w(%O) from %O.\n", ME, mc, source))
|
|
return;
|
|
}
|
|
return render(mc, data, vars, source);
|
|
}
|