mirror of
git://git.psyced.org/git/psyced
synced 2024-08-15 03:25:10 +00:00
place/threads: better _notify msgs & error handling, improved html & json generation, TODO: use signatures here as well place/userthreads: added /add, /remove & /privacy cmds person: (un)subscribe to user channel when a user uses the /add & /remove commands, autojoin to #friends & #follow after friendship is established, added _channels data to qDescription user: htDescription: generate html from _channels data http: TODO: fix web examine cmd so it's not executed twice
2071 lines
66 KiB
C
2071 lines
66 KiB
C
// $Id: user.c,v 1.547 2008/12/22 11:11:36 lynx Exp $ // vim:syntax=lpc
|
|
//
|
|
// common subclass for PSYC clients, whatever the user interface
|
|
|
|
// local debug messages - turn them on by using psyclpc -DDuser=<level>
|
|
#ifdef Duser
|
|
# undef DEBUG
|
|
# define DEBUG Duser
|
|
#endif
|
|
|
|
#include <net.h>
|
|
#include <person.h>
|
|
#include <psyc.h>
|
|
#include <uniform.h>
|
|
|
|
inherit NET_PATH "person";
|
|
inherit NET_PATH "common";
|
|
inherit NET_PATH "sockets";
|
|
|
|
#include <text.h>
|
|
//virtual inherit NET_PATH "output";
|
|
|
|
#define NO_INHERIT
|
|
#include <user.h>
|
|
#undef NO_INHERIT
|
|
|
|
volatile mixed query;
|
|
volatile mapping tags;
|
|
volatile int showEcho;
|
|
volatile mixed beQuiet;
|
|
|
|
// my nickspace. used by psyctext(). could be passed as closure, but then
|
|
// it wouldn't be available for *any* psyctext call in user objects.
|
|
uni2nick(source, vars) {
|
|
P2(("uni2nick(%O) in %O\n", source, ME))
|
|
#ifdef USE_AUTOALIAS
|
|
string ni = raliases[source];
|
|
if (ni) return ni;
|
|
if (member(raliases, source)) return UNIFORM(source); // marked dead
|
|
// ok let's do an autoalias then
|
|
string ln;
|
|
// currently only net/irc employs objects as psyctext sources
|
|
// we should either use them more aggressively or abolish them
|
|
// and have mixed case uniforms instead
|
|
if (objectp(source)) ni = source->qName();
|
|
else {
|
|
mixed u = parse_uniform(source);
|
|
// unfortunately UNick already comes lowercased..
|
|
// we should start using mixed case uniforms..
|
|
unless (u && ni = u[UNick]) {
|
|
// source is probably a server root
|
|
raliases[source] = 0; // mark dead
|
|
return source;
|
|
}
|
|
}
|
|
ln = lower_case(ni);
|
|
string al = aliases[ln];
|
|
if (al) {
|
|
if (UNIFORM(source) == UNIFORM(al)) {
|
|
// just the object of a known uniform or vice versa
|
|
raliases[source] = ni;
|
|
P2(("uni2nick: re-aliased %O to %O\n", source, al))
|
|
return ni;
|
|
}
|
|
raliases[source] = 0; // mark dead
|
|
P2(("uni2nick: already belongs to %O\n", al))
|
|
w("_failure_unavailable_alias", 0,
|
|
([ "_uniform_new": source,
|
|
"_uniform_old": al,
|
|
"_nick_old": ni ]) );
|
|
return UNIFORM(source);
|
|
}
|
|
aliases[ln] = source;
|
|
raliases[source] = ni;
|
|
P2(("uni2nick: autoaliased %O to %O\n", source, ni))
|
|
w("_notice_add_alias_temporary", 0,
|
|
([ "_uniform_entity": source,
|
|
"_nick_entity": ni ]) );
|
|
return ni;
|
|
#else
|
|
// this enables local nickspace, but it fails on local uniforms,
|
|
// it fails when local nick collides with alias, so either you make
|
|
// it super complicated here, or you keep it super stupid.
|
|
//if (objectp(n)) return n->qName();
|
|
# ifdef ALIASES
|
|
return raliases[lower_case(source)] || source;
|
|
# else
|
|
return source;
|
|
# endif
|
|
#endif
|
|
}
|
|
|
|
// jabber needs no private echo but plenty of public echo..
|
|
// net/jabber takes care of that, we just give it echo here.
|
|
// in psyc echoes are essential, so they are on.
|
|
// on irc it's better to turn them on, but you first need a
|
|
// client like irssi. for telnets and webchats you usually
|
|
// want echoes, but you can turn them off
|
|
#define ASSIGN_SHOWECHO(setting, scheme) switch(scheme) { \
|
|
case "psyc": \
|
|
case "jabber": \
|
|
showEcho = 1; \
|
|
break; \
|
|
case "irc": \
|
|
showEcho = setting == "on"; \
|
|
break; \
|
|
default: \
|
|
showEcho = setting != "off"; \
|
|
}
|
|
|
|
#include "usercmd.i"
|
|
|
|
sName(a) {
|
|
// currently this sName gets called more than once
|
|
//ASSERT("user:sName called again", !MYNICK, MYNICK)
|
|
// i don't know if this should get cleaned up TODO
|
|
unless (places) {
|
|
places = ([ ]);
|
|
tags = ([ ]); // this doesn't need an extra if, does it?
|
|
cmdchar = '/';
|
|
}
|
|
return ::sName(a);
|
|
}
|
|
|
|
qHasCurrentPlace() {
|
|
return 1;
|
|
}
|
|
|
|
qResource() {
|
|
return v("scheme");
|
|
}
|
|
|
|
#ifdef HTTP_PATH
|
|
makeToken() {
|
|
string token = sprintf("%x", random(time() ^ 98987));
|
|
P2(("%O's new %O and old %O token\n", ME, token, v("token")))
|
|
vSet("token", token);
|
|
return token;
|
|
}
|
|
|
|
validToken(token) {
|
|
P2(("token %O == given %O?\n", v("token"), token))
|
|
if (is_localhost(query_ip_number())) return 1;
|
|
if (v("token") && token != v("token")) return 0;
|
|
return 1;
|
|
}
|
|
|
|
volatile mapping descvars;
|
|
|
|
#ifndef DEFAULT_FILE_STYLE_EXAMINE
|
|
//# define DEFAULT_FILE_STYLE_EXAMINE "http://www.psyced.org/examine.css"
|
|
# define DEFAULT_FILE_STYLE_EXAMINE "/static/examine.css"
|
|
#endif
|
|
htDescription(anonymous, query, headers, qs, variant, vars) {
|
|
string k, t, page, foto, buttons, profile, nick, type;
|
|
#ifndef HTTP_PATH
|
|
int doTalk = 1;
|
|
#else
|
|
int doTalk = 0;
|
|
|
|
P3(("htDescription in %O for %O showing %O or %O\n", ME, qs, vars, descvars))
|
|
if (query) sTextPath(query["layout"] || v("layout"),
|
|
query["lang"] || v("language"), "html");
|
|
else sTextPath(v("layout"), v("language"), "html");
|
|
#endif
|
|
unless (vars) {
|
|
unless (descvars) return 0;
|
|
// vars = descvars; descvars = 0;
|
|
vars = copy(descvars); // will be modded later
|
|
// right now we do not htquote the descvars.
|
|
// seen how much creativity not quoting html can
|
|
// produce we should probably at least allow friends
|
|
// to send us html code in profiles.. whatcha think?
|
|
// then again, maybe only certain fields should be
|
|
// htmlized, otherwise psyc profiles end up heavily
|
|
// polluted by html code.. extreme solution: let
|
|
// every var have an _HTML trailed variant?!?
|
|
// or go the other way: accept html code in psyc
|
|
// profiles, but remove it when outputting to plain
|
|
// devices? that's a smart thing to say. (buy the record!)
|
|
}
|
|
// store this before we delete some vars
|
|
//doTalk = vars["_language"] || vars["_color"];
|
|
|
|
// adventurous guess.. what if a user provides this? hmm
|
|
// well it looks funny, that's all
|
|
if (stringp(nick = vars["_nick_place"])) {
|
|
type = "_place";
|
|
// used by exa() in _PAGES_start_description
|
|
vars["_nick"] = nick;
|
|
} else {
|
|
type = "_person";
|
|
nick = vars["_nick"];
|
|
}
|
|
// we could also try _identification here, or even apply inheritance
|
|
if (vars["_source"]) profile = ppl[ vars["_source"] ];
|
|
if (!profile && stringp(nick)) profile = ppl[ lower_case(nick) ];
|
|
|
|
page = listDescription(vars, 0, variant
|
|
? "<a href=\"javascript:exa('%s')\">%s</a>"
|
|
: "<a href=\"%s\">%s</a>"); // direct psyc: links!
|
|
if (anonymous) {
|
|
// anonymous profile inspection from the web
|
|
// we are rendering ourselves for somebody else
|
|
// not the other way around
|
|
} else if (type == "_place") {
|
|
// new meaning of _context used here.. ay ay ay
|
|
// we should only show one of these
|
|
buttons = T("_HTML_examine_context_enter", "")
|
|
+ T("_HTML_examine_context_leave", "");
|
|
} else {
|
|
string display = "_normal";
|
|
|
|
switch (profile && profile[PPL_DISPLAY]) {
|
|
case PPL_DISPLAY_NONE:
|
|
// this, of course, needs to happen before we use
|
|
// the "doTalk" variable
|
|
doTalk = 0;
|
|
display = "_none";
|
|
break;
|
|
case PPL_DISPLAY_SMALL:
|
|
display = "_reduce";
|
|
break;
|
|
case PPL_DISPLAY_BIG:
|
|
display = "_magnify";
|
|
break;
|
|
}
|
|
vars["_display_none"] = "";
|
|
vars["_display_reduce"] = "";
|
|
vars["_display_magnify"] = "";
|
|
vars["_display_normal"] = "";
|
|
vars["_display"+display] = "selected";
|
|
|
|
if (profile && profile[PPL_NOTIFY] >= PPL_NOTIFY_PENDING) {
|
|
buttons = T("_HTML_examine_friend_delete", "");
|
|
if (doTalk) buttons += T("_HTML_examine_talk", "");
|
|
} else {
|
|
buttons = T("_HTML_examine_friend_new", "");
|
|
if (doTalk && !FILTERED(vars["_source"]))
|
|
buttons += T("_HTML_examine_talk", "");
|
|
}
|
|
#ifdef TELEPHONY_SECRET
|
|
buttons += T("_HTML_examine_button_call", "");
|
|
#endif
|
|
buttons += T("_HTML_examine_display", "");
|
|
}
|
|
|
|
#ifndef _flag_enable_profile_table
|
|
page = T("_list_description_on", "")
|
|
+ page
|
|
+ T("_list_description_off", "");
|
|
#else
|
|
page = T("_list_description_on_table", "")
|
|
+ page
|
|
+ T("_list_description_off_table", "");
|
|
#endif
|
|
if (buttons) page = T("_HTML_examine_buttons_start", "")
|
|
+ buttons + T("_HTML_examine_buttons_end", "") + page;
|
|
if (t = vars["_uniform_photo"] || vars["_uniform_photo_small"]) {
|
|
foto = "<img class=\"Peup\" src=\""+ t +"\">";
|
|
if (vars["_page_photo"])
|
|
foto = "<a target=\"_blank\" href=\""
|
|
+ vars["_page_photo"] +"\">"+ foto +"</a>";
|
|
// foto = "<span class=\"Peup\">"+ foto +"</span>\n";
|
|
} else foto = "";
|
|
if (variant) page = "<link rel=\"stylesheet\" href=\""+
|
|
(vars["_uniform_style"] || DEFAULT_FILE_STYLE_EXAMINE)
|
|
+"\">\n"+ T("_PAGES_start_description"+ variant, "") + page
|
|
+ T("_PAGES_end_description"+ variant, "");
|
|
else {
|
|
// we send only the "body" of the page to psyc clients.
|
|
// psyczilla doesn't really need a form,
|
|
// but the formatting goes wrong if there isn't one
|
|
page = foto +"\n<form class='Pef'>\n"+ page +"</form>";
|
|
}
|
|
P4(("EXAMINE psyctext %O with %O\n", page, vars))
|
|
// <form target=cout_" CHATNAME " name=f" HTFORMOPTS "
|
|
#ifdef HTTP_PATH
|
|
sTextPath(v("layout"), v("language"), v("scheme"));
|
|
#endif
|
|
|
|
// <input type=hidden name=user value=\""+qName()+"\">\n\
|
|
// <input type=hidden name=token value=\""+v("token")+"\">\n\
|
|
// <input type=hidden name=lang value=\""+v("language")+"\">\n\
|
|
//
|
|
|
|
# ifdef _flag_enable_module_microblogging
|
|
mapping channels;
|
|
string htchannels;
|
|
if (vars["_channels"] && (channels = parse_json(vars["_channels"]))) {
|
|
P3((">>> channels: %O\n", channels))
|
|
htchannels =
|
|
"<script type='text/javascript'>\n"
|
|
"function selectTab(parent, id) {"
|
|
"var tabs = document.getElementById(parent);"
|
|
"var tab = tabs.firstChild;"
|
|
"while (tab) {"
|
|
"tab.className = (tab.id == parent+'-'+id) ? 'selected' : '';"
|
|
"tab = tab.nextSibling;"
|
|
"}"
|
|
"}\n"
|
|
"function switchTab(id) {"
|
|
"selectTab('tabs', id);"
|
|
"selectTab('tab-contents', id);"
|
|
"}\n"
|
|
"</script>\n";
|
|
|
|
string tabs = "";
|
|
string contents = "";
|
|
int first = 1;
|
|
object threads = clone_object(NET_PATH "place/threads");
|
|
|
|
foreach (string channel, array(mapping) entries : channels) {
|
|
unless (sizeof(entries) > 0) continue;
|
|
tabs +=
|
|
"<a id='tabs-" + channel + "' onclick=\"switchTab('" + channel + "')\" "
|
|
+ (first ? "class='selected'" : "") + ">#" + channel + "</a>\n";
|
|
contents +=
|
|
"<div id='tab-contents-" + channel + "' "
|
|
+ (first ? "class='selected'" : "") + ">" + threads->htmlEntries(entries, 0, first, channel, anonymous ? "" : vars["_identification"] + "#" + channel) + "</div>\n";
|
|
first = 0;
|
|
}
|
|
|
|
if (first) htchannels = "";
|
|
else htchannels +=
|
|
"<div id='tabs' class='tabs'>\n" + tabs + "</div>\n"
|
|
"<div id='tab-contents' class='tab-contents'>\n" + contents + "</div>\n";
|
|
}
|
|
# endif
|
|
return psyctext(page, vars + ([
|
|
"_FORM_start" : "\
|
|
<form class=\"Pef\" name=\"Pef\" action=\"\">\n\
|
|
<input type=\"hidden\" name=\"cmd\" value=\"\">\n\
|
|
<input type=\"hidden\" name=\"dest\" value=\"\">\n",
|
|
"_QUERY_STRING" : qs || "",
|
|
"_HTML_photo" : foto,
|
|
"_nick_me" : MYNICK,
|
|
"_FORM_end" : "</form>\n",
|
|
])
|
|
)
|
|
# ifdef _flag_enable_module_microblogging
|
|
+ htchannels
|
|
# endif
|
|
;
|
|
}
|
|
#endif
|
|
|
|
// leavePlace: extracted from _echo_place_leave handling so we can
|
|
// do it without depending on the place's answer
|
|
//
|
|
// "impatient leave" is an apparently necessary strategy:
|
|
// _ when servers crash, the UNI would not reissue a join request because
|
|
// it still sees the context in its places. this also helps with remote MUCs.
|
|
// _ the /cycle feature of some IRC clients expects no delays between direct
|
|
// subsequent PART and JOIN commands, but if psyced hasn't received leave
|
|
// confirmation yet, it used to simply ignore the JOIN.
|
|
//
|
|
// both issues could probably also be solved with glue around
|
|
// "polite leave", but it's a lot more complicated and maybe it's
|
|
// the wrong strategy anyhow.
|
|
//
|
|
protected leavePlace(where) {
|
|
P2(("%O leavePlace's %O\n", ME, where))
|
|
if (place == where) {
|
|
// current place.. psyced specific thing
|
|
place = 0;
|
|
// vDel("place"); // wrong! dann kommen die
|
|
// leute nich mehr in ihren
|
|
// letzten raum rein!
|
|
}
|
|
// leave the context slave, so we immediately stop getting
|
|
// the context's traffic even before it knows about us leaving
|
|
deregister_context(ME, where);
|
|
// after this step any castmsg from 'where' will be considered spam.
|
|
// luckily we're not in the context slave anymore.
|
|
m_delete(places, where);
|
|
}
|
|
|
|
/** PSYC-conformant message receiving function
|
|
**
|
|
** after doing basic PSYC, ppl and lastlog processing in
|
|
** person:msg() we're here to do the output..
|
|
*/
|
|
msg(source, mc, data, mapping vars, showingLog) {
|
|
string family;
|
|
mixed pal, t, variant;
|
|
int glyph;
|
|
#ifdef USE_THE_NICK
|
|
string nick, nick2;
|
|
#endif
|
|
|
|
P3(("%O user:msg(%O,%O,%O,%O)\n", ME, source, mc, data, vars))
|
|
// context checking since ip paranoia is applied to context
|
|
// not source whenever context is available..
|
|
//
|
|
// this should probably also apply for objectp sources since
|
|
// one day even they might be spoofed by a smart context..
|
|
// but right now sources are not resolved to objects
|
|
//
|
|
// also.. this belongs into person.c if it werent for the
|
|
// rplaces ... hmmm!
|
|
// Note: with context mcast we dont need this any more as we
|
|
// will only get valid contexts
|
|
if (vars["_context"]) {
|
|
if (showingLog) {
|
|
// sometimes lastlog comes with a _context which i
|
|
// need to rename here as an indicator for the final
|
|
// renderer that this is a retransmission
|
|
vars["_INTERNAL_context"] = vars["_context"];
|
|
m_delete(vars, "_context");
|
|
// unless (source) source = vars["_source_relay"];
|
|
#if DEBUG > 0
|
|
} else if (!objectp(vars["_context"]) &&
|
|
!places[vars["_context"]] &&
|
|
// !psyc_object(vars["_context"]) &&
|
|
!abbrev("_notice_place", mc)) { // just _notice_place? TODO
|
|
string ctx, chan;
|
|
|
|
// monitor_report("_warning_abuse_invalid_context",
|
|
// S("%O: Invalid context %O in %O from %O (%O)",
|
|
// ME, vars["_context"], mc, source, data));
|
|
if ((sscanf(vars["_context"], "%s#%s", ctx, chan) == 2)
|
|
&& places[ctx]) {
|
|
P1(("got valid context %O with channel %O\n",
|
|
ctx, chan))
|
|
} else {
|
|
P1(("Invalid context: %O says %s(%O) with %O ... stack %O ... places %O\n",
|
|
source, to_string(mc), data, vars,
|
|
caller_stack(1), places))
|
|
return 0;
|
|
}
|
|
#endif
|
|
}
|
|
unless (source) source = vars["_source_relay"] || vars["_context"];
|
|
}
|
|
// before? after?
|
|
unless (source) source = vars["_INTERNAL_source"];
|
|
// call the psyc handler in person.c
|
|
// it returns some extra info concerning display
|
|
t = source; // keep the real source before entity.c puts the UNI into it
|
|
// and here we call person::msg() without ensuring that local sources
|
|
// are objectp. this may cause problems. shouldn't we have all the
|
|
// location resolution happen BEFORE we let person act on things?
|
|
// this looks like a big TODO here.
|
|
|
|
// <fippo>: if _INTERNAL_target_resource is set, calling person::msg
|
|
// is INAPPROPRIATE. This is the current terminology of
|
|
// net/jabber, but we will have to do something similar in net/psyc
|
|
//
|
|
// Rationale: if this variable is set, we dont want the uni to
|
|
// handle this.
|
|
#ifdef PRO_PATH
|
|
unless (variant = ::msg(&source, &mc, &data, &vars, showingLog))
|
|
return 0;
|
|
if (member(vars, "_authenticated")) variant = "_important";
|
|
#else
|
|
unless (::msg(&source, &mc, &data, &vars, showingLog)) {
|
|
P3(("%O ::msg no display for %O's %O\n", ME, source, mc))
|
|
return 0;
|
|
}
|
|
variant = "";
|
|
#endif
|
|
P4(("after p:msg(%O,%O,%O,%O) -> %O\n", source,mc,data,vars, variant))
|
|
// D2(unless (showingLog) D(S("user:msg(%O,%O,%O..) -> %O\n",
|
|
// source, mc, data, variant));)
|
|
#ifdef USE_THE_NICK
|
|
nick = vars["_nick"];
|
|
#endif
|
|
|
|
#ifdef ALIASES
|
|
# ifdef BRAIN
|
|
unless (mappingp(v("aliases"))) vSet("aliases", ([]));
|
|
# endif
|
|
#endif
|
|
if (stringp(t)
|
|
#ifdef SLAVE
|
|
// we trust user if he got relayed from local room object
|
|
&&! objectp(vars["_context"])
|
|
#endif
|
|
#ifndef UNSAFE_LASTLOG
|
|
// evil scary rewrite to support changed lastlog behaviour ,)
|
|
// TODO:: watch for _source_relay||(source resp. t)
|
|
&&! (stringp(source)
|
|
&& abbrev(SERVER_UNIFORM +"~", source))
|
|
#endif
|
|
) {
|
|
#ifdef PRO_PATH
|
|
if (data && index(data, '\n') != -1)
|
|
data = replace(data, "\n", " ");
|
|
#endif
|
|
#ifdef USE_THE_NICK
|
|
if (t != source) {
|
|
if (objectp(source)) {
|
|
# ifdef ALIASES
|
|
string t2;
|
|
|
|
if (t2 = raliases[nick = source->qNameLower()]) {
|
|
nick = t2;
|
|
} else if (aliases[lower_case(nick)]) {
|
|
nick = t;
|
|
}
|
|
# else
|
|
nick = source->qName();
|
|
# endif
|
|
} else {
|
|
# ifdef ALIASES
|
|
nick = raliases[source] || source;
|
|
# else
|
|
nick = source;
|
|
# endif
|
|
}
|
|
|
|
# ifndef TRUST_PSYC_HACK
|
|
# ifdef ALIASES
|
|
} else if (raliases[source]) {
|
|
nick = raliases[source];
|
|
# endif
|
|
} else {
|
|
// short term solution (nick manager to follow)
|
|
//if (nick) nick = "("+ source + ") "+ nick;
|
|
// middle term solution
|
|
if (nick && nick != source) {
|
|
// this ensures we always escape the
|
|
// original nick, not an already escaped one
|
|
// other solution would probably be not
|
|
// to send the same vars to all users..
|
|
// eeeeh.. probably the better solution
|
|
unless (vars["_nick_verbatim"])
|
|
vars["_nick_verbatim"] = nick;
|
|
vars["_INTERNAL_nick_plain"] = nick;
|
|
// sometimes we get here with source being
|
|
// a context - that of course looks very
|
|
// stupid - how does it happen? TODO
|
|
// beQuiet is -1 exactly with those schemes
|
|
// where this nick patching habit is completely
|
|
// inappropriate. we may want to change the
|
|
// var name.
|
|
if (beQuiet != -1)
|
|
nick = psyctext(
|
|
T("_MISC_identification_remote",
|
|
"«[_source]» [_nick]"), ([
|
|
"_source":source,
|
|
"_nick":vars["_nick_verbatim"] ]));
|
|
}
|
|
else nick = stringp(source) ? source : to_string(source);
|
|
# if 0 //DEBUG > 1
|
|
// checking for validity of messages should
|
|
// happen at psyc-parsing level.. maybe? YEEEES!!
|
|
// and.. vars["_context"] is zero when showing log!
|
|
// member might help.
|
|
if (abbrev("_message", mc)) {
|
|
D("patching remote mc\n");
|
|
mc = member(vars, "_context") ?
|
|
"_message_public" : "_message_private";
|
|
}
|
|
// this just wont work.. it even breaks
|
|
// remote _message_echo_private's
|
|
# endif
|
|
# endif
|
|
}
|
|
nick2 = nick;
|
|
#endif // USE_THE_NICK
|
|
// belongs into person.c?
|
|
if (mc == "_message") mc = vars["_context"] ?
|
|
"_message_public" : "_message_private";
|
|
P3(("%O got msg(%O,%O,%O,%O)\n", ME,source,mc,data,vars))
|
|
}
|
|
#ifdef USE_THE_NICK
|
|
else if (nick && nick != MYNICK) {
|
|
# ifdef ALIASES
|
|
string tn;
|
|
// ein kleines bisschen hilflosigkeit (&&! showingLog)
|
|
//
|
|
// why are summoned_persons incarnated as users, not net/person??
|
|
// any reason? don't want to think over it, but it kinda sucks for
|
|
// aliases.. says tobij. lynX adds: well in fact with some clean
|
|
// up summoned persons could be reverted to NOT be users again.
|
|
// so please go ahead! TODO
|
|
if (raliases[tn = lower_case(nick)] &&! showingLog) {
|
|
nick2 = nick = raliases[tn];
|
|
} else if (aliases[tn] &&! showingLog) {
|
|
nick2 = nick = UNIFORM(source);
|
|
} else
|
|
# endif
|
|
nick2 = vars["_nick_stylish"] || vars["_nick_local"] || nick;
|
|
} else nick2 = nick = MYNICK;
|
|
P3(("q/n/n2: %O,%O,%O\n", MYNICK,nick,nick2))
|
|
#endif // USE_THE_NICK
|
|
|
|
t = vars && vars["_context"] || source;
|
|
#ifdef SANDBOX
|
|
unless (objectp(t) && stringp(geteuid(t)) && geteuid(t)[0] == '/') {
|
|
#else // duh. we cannot combine this, ldmud complains, might be a preprocessor
|
|
// bug or just my very own stupidity.
|
|
unless (objectp(t)) {
|
|
#endif
|
|
string rnick;
|
|
array(mixed) u;
|
|
#ifdef SANDBOX
|
|
|
|
if (objectp(t)) {
|
|
rnick = object_name(t);
|
|
rnick = rnick[rmember(rnick, '/')+1..];
|
|
|
|
if (!vars["_nick_place"]
|
|
|| stricmp(rnick, vars["_nick_place"])) {
|
|
// TODO: we shouldn't raise_error here, as we shouldn't
|
|
// allow unprivileged places to annoy users.
|
|
// what to do? enforce rnick? drop msg?
|
|
raise_error(sprintf("INVALID _nick_place in msg by %O(%O)\n",
|
|
t, rnick));
|
|
}
|
|
} else
|
|
#endif
|
|
if (stringp(t) && (rnick = (u = parse_uniform(t, 1))[UResource])
|
|
&& strlen(rnick)
|
|
&& rnick[0] == '@') {
|
|
string hnick = rnick[1..];
|
|
|
|
rnick = vars["_nick_place"];
|
|
|
|
if (!rnick || stricmp(hnick, rnick)) {
|
|
rnick = hnick;
|
|
}
|
|
|
|
vars["_nick_place"] = u[URoot] + "/@" + rnick;
|
|
}
|
|
}
|
|
|
|
// verrry similar code in psyctext()
|
|
// didn't we want to eliminate these?
|
|
// also we have the textdb with inheritance, so we could deal with
|
|
// these things in a proper way!
|
|
t = vars["_time_place"];
|
|
if (!t && showingLog) {
|
|
// _time_log is just for psyc clients: since we had to
|
|
// delete _time_INTERNAL from the psyc out, we need a
|
|
// variable for the case when this _really_ is a /log output
|
|
unless (t = vars["_time_log"]) {
|
|
if (t = vars["_time_INTERNAL"]) {
|
|
//m_delete(vars, "_time_INTERNAL");
|
|
vars["_time_log"] = t;
|
|
} else {
|
|
// sort-of impossible condition
|
|
P0(("%O got showingLog w/out _time in %O\n",
|
|
ME, vars))
|
|
}
|
|
}
|
|
P3(("%O time %O data %O from %O\n", ME, t, data, vars))
|
|
}
|
|
PSYC_TRY(mc) {
|
|
#ifndef ALPHA
|
|
case "_jabber_iq_error": // DONT reply
|
|
P2(("%O got %O", ME, mc))
|
|
break;
|
|
// TODO: add the xmlns to unsupported method format
|
|
// technisch gesehen sollten wir auch die resource
|
|
// wieder zurueckgeben an die das gesendet wurde...
|
|
// aber kriegen wir das hier?
|
|
case "_jabber_iq_get":
|
|
case "_jabber_iq_set":
|
|
sendmsg(source, "_error_unsupported_method", 0,
|
|
([ "_tag_reply" : vars["_tag"] ]));
|
|
break;
|
|
#endif
|
|
case "_jabber":
|
|
P1(("%O got %O", ME, mc))
|
|
break;
|
|
case "_notice_composing_media":
|
|
if (v("scheme") != "psyc") return 1;
|
|
break;
|
|
case "_message_video":
|
|
case "_message_audio":
|
|
// not being displayed to users other than psyc clients
|
|
if (v("scheme") != "psyc") return 1;
|
|
data = ""; // temporary for @welcome history
|
|
break;
|
|
case "_message_private_question":
|
|
variant = "_ask" + variant;
|
|
m_delete(vars, "_action");
|
|
// fall thru
|
|
case "_message": // sollte schon vorher abgefangen worden sein
|
|
mc = "_message_private";
|
|
// fall thru
|
|
case "_message_private":
|
|
// question recognition takes place on sender side
|
|
// for public talk, so it should also for private talk..
|
|
#ifdef USE_THE_NICK
|
|
#if 0
|
|
# ifdef BRAIN
|
|
P2(( "this shouldn't happen - the brain aliases bug\n" ))
|
|
unless (v("aliases")) vSet("aliases", ([]));
|
|
// for some reason v("aliases") can be undefined here
|
|
// but it never happens on my test server
|
|
# endif
|
|
if (objectp(source)) {
|
|
if (v("aliases")[nick]) { // nick collision detected!
|
|
nick = psyc_name(source);
|
|
} else {
|
|
nick = raliases[lower_case(nick)] || nick2;
|
|
// patchen in den vars.. vorsicht!
|
|
vars["_nick_long"] = vars["_nick"];
|
|
}
|
|
} else if (stringp(source)) {
|
|
nick = raliases[source] || nick2;
|
|
} else {
|
|
nick = nick2;
|
|
}
|
|
#else
|
|
nick = nick2;
|
|
#endif
|
|
#endif
|
|
#ifdef QUESTION_RECOGNITION_ON_RECEPTION
|
|
if (variant == "" && data && index(data, '?') != -1) {
|
|
// mc += "_ask";
|
|
variant = "_ask";
|
|
m_delete(vars, "_action");
|
|
break;
|
|
}
|
|
#endif
|
|
break;
|
|
case "_message_echo_private_question":
|
|
variant = "_ask" + variant;
|
|
m_delete(vars, "_action");
|
|
mc = mc[..<10];
|
|
case "_message_echo":
|
|
case "_message_echo_private":
|
|
unless (showingLog || showEcho) return 1;
|
|
#if 0 //def CAN_WE_DARE_THIS // why do we have this block at all!?
|
|
// this is the wrong way and the wrong place to create _source_relay
|
|
pal = vars["_nick_target"];
|
|
if (pal) {
|
|
if (wAction(mc, data, vars, source, variant, nick))
|
|
return 1;
|
|
#ifdef QUESTION_RECOGNITION_ON_RECEPTION
|
|
if (variant == "" && index(data, '?') != -1) {
|
|
w(mc+"_ask", data,
|
|
([ "_nick_target" : pal,
|
|
"_time_log" : vars["_time_log"],
|
|
"_source_relay" : source ]));
|
|
return 1;
|
|
}
|
|
#endif
|
|
// should have been "vars + ([" as below for public?
|
|
w(mc, data, ([ "_nick_target" : pal,
|
|
"_time_log" : vars["_time_log"],
|
|
"_source_relay" : source ]));
|
|
return 1;
|
|
}
|
|
return 0; // dont walk into _message_public if !pal
|
|
#else
|
|
break;
|
|
#endif
|
|
case "_message_public_question":
|
|
#ifndef NO_PUBLIC_QUESTIONS
|
|
variant = "_ask" + variant;
|
|
m_delete(vars, "_action");
|
|
#endif
|
|
mc = "_message_public";
|
|
// fall thru
|
|
case "_message_public":
|
|
// could theoretically move into the if, but the devil etc.
|
|
//unless (ONLINE) vInc("new");
|
|
if (source != ME) {
|
|
if (qHasCurrentPlace()) {
|
|
mixed room;
|
|
room = vars["_context"] || vars["_INTERNAL_context"];
|
|
if (room && room != place) { // && place ..not!
|
|
vSet("otherplace", room);
|
|
// rplaces will normally do the job
|
|
room = places[room] ||
|
|
(objectp(room) ? room->qName()
|
|
: to_string(room));
|
|
w((data ? "_message_public_other":
|
|
"_message_public_other_action")+variant,
|
|
data, vars + ([
|
|
#ifdef USE_THE_NICK
|
|
"_nick": nick2,
|
|
#endif
|
|
"_nick_place": room ]) );
|
|
return 1;
|
|
}
|
|
}
|
|
#ifdef USE_THE_NICK
|
|
nick = nick2;
|
|
#endif
|
|
break;
|
|
}
|
|
if (vars["_nick_local"] && MYLOWERNICK != lower_case(vars["_nick_local"])) {
|
|
// should we have special templates for this?
|
|
//mc = "_message_echo_public_masquerade";
|
|
#ifdef USE_THE_NICK
|
|
nick = vars["_nick_local"];
|
|
#endif
|
|
// or should we rather make sure that all clients
|
|
// learn to detect echo themselves, since future
|
|
// multicast routing may not give us the possibility
|
|
// to patch methods on the way to the client
|
|
// like we do here:
|
|
} else mc = "_message_echo_public";
|
|
// fall through for echoes coming from rooms
|
|
case "_message_echo_public":
|
|
unless (showingLog || showEcho || vars["_INTERNAL_force_echo"])
|
|
return 1;
|
|
break;
|
|
case "_message_announcement":
|
|
case "_message_behaviour_warning":
|
|
case "_message_behaviour_punishment":
|
|
case "_message_behaviour":
|
|
case "_message_twitter":
|
|
unless (stringp(data)) variant = "_default";
|
|
else variant = "";
|
|
#ifdef USE_THE_NICK
|
|
nick = nick2;
|
|
// fmt = nick ? "%s announces: %s\n"
|
|
// : "*** Announcement: %s ***\n";
|
|
#endif
|
|
break;
|
|
case "_request_message_public_question":
|
|
case "_request_message":
|
|
mc = "_request_message_public";
|
|
case "_request_message_public":
|
|
break;
|
|
case "_request_description":
|
|
// if (source == ME) return 1;
|
|
return 1;
|
|
case "_status_description":
|
|
case "_status_description_person":
|
|
case "_status_description_place":
|
|
P4(("x-ret %O\n", vars))
|
|
if (vars["_tag_reply"]) {
|
|
sscanf(vars["_tag_reply"], "%s %s", variant, t);
|
|
//PT(("format %O tag %O\n", variant, t))
|
|
descvars = vars;
|
|
descvars["_source"] = source;
|
|
switch (variant) {
|
|
default:
|
|
// client doesn't want HTML
|
|
if (sizeof(v("locations")[0])) sendmsg(m_indices(v("locations")[0])[0],
|
|
mc, data, ([
|
|
"_tag_reply": t,
|
|
"_source_relay": source,
|
|
"_uniform_style": vars["_uniform_style"]
|
|
]) + vars);
|
|
return 1;
|
|
case "_HTML":
|
|
// client wants HTML
|
|
if (sizeof(v("locations")[0])) sendmsg(m_indices(v("locations")[0])[0],
|
|
mc+"_HTML", htDescription(), ([
|
|
"_type_data": "text/html",
|
|
"_tag_reply": t,
|
|
"_source_relay": source,
|
|
"_uniform_style": vars["_uniform_style"]
|
|
]));
|
|
return 1;
|
|
case "_surf":
|
|
// we're doing a /surf, so stop here
|
|
return 1;
|
|
case "0":
|
|
case 0:
|
|
// fall thru to manual examine
|
|
}
|
|
}
|
|
// internal transformation of a single msg into a list of msgs
|
|
// hmmm.. well, that's how psyc to client happens here
|
|
mc = "_list"+ mc[7..];
|
|
w(mc+"_on", data, ([ // used by irc whois..
|
|
"_source_relay": source, "_tag_reply": t,
|
|
"_nick" : vars["_nick"] || vars["_nick_place"],
|
|
"_name_public" : vars["_name_public"] || "",
|
|
"_action_motto" : vars["_action_motto"] || "",
|
|
"_description_motto" : vars["_description_motto"] || ""
|
|
]));
|
|
listDescription(vars, 1);
|
|
w(mc+"_off", 0, ([
|
|
"_source_relay": source, "_tag_reply": t,
|
|
"_nick" : vars["_nick"] ]));
|
|
return 1;
|
|
case "_status_place_members_none_automatic":
|
|
if (beQuiet != -1) {
|
|
PT(("%O skipping %O from %O\n", ME, mc, source))
|
|
return 1;
|
|
}
|
|
break;
|
|
case "_status_place_members_automatic":
|
|
if (beQuiet != -1) {
|
|
PT(("%O skipping %O from %O\n", ME, mc, source))
|
|
return 1;
|
|
}
|
|
// fall thru
|
|
case "_status_place_members":
|
|
#ifdef PARANOID
|
|
//unless (pointerp(vars["_list_members"])) break;
|
|
// t = implode(renderMembers(vars["_list_members"],
|
|
// vars["_list_members_nicks"], objectp(source)), ", ");
|
|
if (stringp(t = vars["_list_members"])) { // _tab
|
|
vars["_list_members"] = ({ t });
|
|
} else unless (pointerp(vars["_list_members"])) break;
|
|
if (stringp(t = vars["_list_members_nicks"])) {
|
|
vars["_list_members_nicks"] = ({ t });
|
|
} // no else break here by intention, renderMembers() won't be
|
|
// scared by that.
|
|
#endif
|
|
// same code in _echo_place_enter etc.
|
|
// renderMembers is also useful for psyc clients
|
|
vars["_list_members_nicks"] =
|
|
renderMembers(vars["_list_members"],
|
|
vars["_list_members_nicks"], objectp(source));
|
|
break;
|
|
// if (v("verbatimuniform") == "on")
|
|
// t = replace(t, "@", "%");
|
|
// w(mc, "In [_nick_place]: [_list_members_nicks]", ([
|
|
// "_nick_place" : vars["_nick_place"],
|
|
// "_list_members_nicks" : t ]) );
|
|
// irc-thang: w(mc + "_end", 0, vars);
|
|
// return 1;
|
|
#ifdef TELEPHONY_SECRET
|
|
# include <sys/tls.h>
|
|
|
|
case "_notice_answer_call_click":
|
|
case "_notice_answer_call":
|
|
t = vars["_time_expire"];
|
|
if (to_int(t) < time()) {
|
|
P1(("Expired phone call for %O from %O: %O\n",
|
|
ME, source, t))
|
|
return 1; // call expired
|
|
}
|
|
string t2, t3;
|
|
t2 = "?thats=me&user="+ MYNICK +"&expiry="+ t +"&jack="+
|
|
(t3 = hmac(TLS_HASH_SHA256, TELEPHONY_SECRET,
|
|
"I"+ t +":"+ MYNICK));
|
|
P1(("hmac/call %O for %O\n", t3, "I"+t+":"+MYNICK))
|
|
t3 = NET_PATH "http/call"->make_session(MYNICK, to_int(t), t3);
|
|
w("_notice_answer_call_link", 0, ([
|
|
"_page_call": (
|
|
#ifdef __TLS__
|
|
(tls_available() && HTTPS_URL) ||
|
|
#endif
|
|
HTTP_URL) + NET_PATH +"http/call?thats=me&sid=" +t3,
|
|
# ifdef TELEPHONY_SERVER
|
|
"_uniform_call": TELEPHONY_SERVER + t2,
|
|
# endif
|
|
]));
|
|
return 1;
|
|
#endif
|
|
case "_notice_received_email":
|
|
case "_notice_email_received":
|
|
if (stringp(vars["_origin"])) vars["_origin"] =
|
|
decode_embedded_charset(vars["_origin"]);
|
|
if (stringp(vars["_subject"])) vars["_subject"] =
|
|
decode_embedded_charset(vars["_subject"]);
|
|
break;
|
|
case "_notice_presence_here_quiet":
|
|
mc = mc[..<7]; // _quiet is just internal
|
|
// humm.. not since i started using it for CACHE_PRESENCE
|
|
// problem? should i use _request_friend_present instead?
|
|
// fall thru
|
|
case "_notice_presence":
|
|
case "_status_presence":
|
|
break;
|
|
case "_notice_person_absent_netburp":
|
|
if (vars["_context"] != place) return 1;
|
|
break;
|
|
case "_failure_unsuccessful_delivery":
|
|
case "_failure_network_connect_invalid_port":
|
|
// is this the right place to do this? i have seen a recursion where
|
|
// person.c was never asked for opinion, so i'm putting this into user.c
|
|
#if 0 //def ALPHA
|
|
string loc;
|
|
foreach (t, loc : v("locations")) {
|
|
if (member(loc, vars["_source_relay"])) {
|
|
P1(("%O in %O talking to its %O location at %O.",
|
|
mc, ME, t, loc))
|
|
sLocation(t, 0);
|
|
}
|
|
}
|
|
#else
|
|
foreach (string type, mapping locs : v("locations")) {
|
|
if (member(locs, vars["_source_relay"])) {
|
|
P0(("%O got %O, deleting %O from %O\n", ME, mc, vars["_source_relay"], v("locations")))
|
|
m_delete(locs, vars["_source_relay"]);
|
|
unless (sizeof(locs)) m_delete(v("locations"), type);
|
|
}
|
|
}
|
|
P4(("locations left: %O\n", v("locations")))
|
|
#endif
|
|
// fall thru - not strictly necessary but adds a feature
|
|
case "_failure_redirect_permanent":
|
|
// we currently have no implementation for permanent changes
|
|
case "_failure_redirect_temporary":
|
|
case "_failure_redirect":
|
|
// current policy: we trust local senders to do this
|
|
if (objectp(source) && vars["_source_redirect"]) {
|
|
PT(("%O REDIRECTED TO %O\n", ME,
|
|
vars["_source_redirect"]))
|
|
// how did we get here without a vars["_method_relay"]?
|
|
mc = vars["_method_relay"];
|
|
// intended for _enter and _leave, but right now
|
|
// only places send this, so it's ok to simplify
|
|
if (mc && abbrev("_request", mc)) {
|
|
// but we use placeRequest here, because we
|
|
// need a _tag
|
|
placeRequest(vars["_source_redirect"],
|
|
mc, 0, 1);
|
|
} // else.. we'll see
|
|
return 1;
|
|
}
|
|
break;
|
|
case "_echo_place_enter_INTERNAL_CHECK": // only do the check and dont write anything
|
|
pal = 3093;
|
|
// _unicast comes from MUCs and carries a faked _context
|
|
// that's why it has to be a _notice even if it's an _echo
|
|
// maybe psycmatch() could solve this issue instead of abbrev()
|
|
case "_echo_place_enter_unicast":
|
|
case "_notice_place_enter_unicast_INTERNAL_ECHO": // MUC join
|
|
case "_echo_place_enter_automatic":
|
|
case "_echo_place_enter_automatic_subscription":
|
|
case "_echo_place_enter_subscribe": // /sub place differs from auto-
|
|
// joining a subscribed place after
|
|
// logon
|
|
variant = 2933; // do not make it the current place
|
|
case "_echo_place_enter": // = /c <channel>
|
|
case "_echo_place_enter_other": // = /c ??
|
|
case "_echo_place_enter_login": // primary join, but used to be quiet..
|
|
if (beQuiet != -1 && vars["_context"] != place) {
|
|
if (beQuiet == source) pal = 3093;
|
|
else beQuiet = source;
|
|
}
|
|
case "_echo_place_enter_follow": // = /f
|
|
case "_echo_place_enter_home": // = /h
|
|
case "_echo_place_enter_join": // = /j <channel>
|
|
P3(("%O %O %O\n", ME, mc, vars))
|
|
# ifndef TAGS_ONLY
|
|
if (ME == source // || psyc_object(source) == ME
|
|
// weil bei remotes _source_relay der string von ME ist..
|
|
// TODO: die Policy dass die Raeume das echo nach dem
|
|
// castmsg direkt an uns sendet sollte den Code
|
|
// hier vereinfachen
|
|
// u.u. auch deshalb, weil wir unterschiedliche
|
|
// cases machen koennen fuer _notice und _echo
|
|
// || vars["_nick_verbatim"] == MYNICK
|
|
|| ME == vars["_source_relay"]
|
|
|| ME == psyc_object(vars["_source_relay"])) {
|
|
# else
|
|
if (objectp(source) || tags[vars["_tag_reply"]]
|
|
|| tags[vars["_tag"]] // BUGGY PLACES
|
|
) {
|
|
# endif
|
|
mixed p;
|
|
p = vars["_context"] || source;
|
|
t = vars["_nick_place"];
|
|
# if 0 // DEBUG_FLAGS & 0x02 // abgesehen davon wurde der auch bei D0...
|
|
// dieser unless leuchtet mir nicht mehr ein..
|
|
// warum sollte context oder source jemals ME sein?
|
|
unless (t || p == ME) {
|
|
log_file("USER", "%O < broken (%O,%s,%O,%O)\n",
|
|
ME, source, mc, data, vars);
|
|
return;
|
|
}
|
|
# endif
|
|
if (variant != 2933) { // primary join operation
|
|
place = p;
|
|
vSet("place", objectp(place) ? place->qName() : place);
|
|
P3(("%O goes to %O (%O)\n", ME,
|
|
place, v("place")))
|
|
} else {
|
|
P3(("%O just joins %O (%O)\n", ME, p, t))
|
|
}
|
|
#ifdef ENTER_MEMBERS
|
|
// net/jabber does some funky things that do
|
|
// not fit in here.
|
|
if (variant != 2933) switch(v("scheme")) {
|
|
case "jabber":
|
|
break;
|
|
case "irc":
|
|
case "psyc":
|
|
// this is a nice service the UNI provides
|
|
if (sizeof(vars["_list_members"])) { // _tab
|
|
vars["_list_members_nicks"] =
|
|
renderMembers(vars["_list_members"],
|
|
vars["_list_members_nicks"], objectp(source));
|
|
}
|
|
break;
|
|
default:
|
|
// this part belongs into the client emulator
|
|
// instead (excluding the renderMembers)
|
|
if (sizeof(vars["_list_members"])) { // _tab
|
|
vars["_list_members_nicks"] =
|
|
renderMembers(vars["_list_members"],
|
|
vars["_list_members_nicks"], objectp(source));
|
|
w("_status_place_members", 0, vars);
|
|
} else if (vars["_amount_members"])
|
|
w("_status_place_members_amount", 0, vars);
|
|
else
|
|
w("_status_place_members_none", 0, vars);
|
|
}
|
|
#endif
|
|
// so much semantics in here.. shouldnt this be in person.c ?
|
|
// wasn't user.c:msg just meant to be about display? TODO
|
|
// incoming context isnt always objectp()
|
|
// even for local contexts
|
|
// then again.. as of now it should
|
|
//rplaces[psyc_name(p)] = t;
|
|
unless (places[p]) {
|
|
places[p] = t;
|
|
// D(S("p, t: %O, %O\n", p, t));
|
|
} else {
|
|
// we have already joined this
|
|
return 1;
|
|
}
|
|
if (stringp(source)) {
|
|
P3(("%O joins mcast group for %O\n", ME, source))
|
|
register_context(ME, source);
|
|
}
|
|
variant = "";
|
|
P4(("enter: places = %O after %s(%O) from %O\n",
|
|
places, mc, vars, source))
|
|
} else {
|
|
// untagged or wrong tag
|
|
P0(("%O got untagged %s from %O: %O\n", ME, mc, source, vars))
|
|
// eh... da stimmt was net
|
|
return -1;
|
|
}
|
|
if (pal == 3093) return 1;
|
|
pal = 0;
|
|
break;
|
|
case "_notice_place_enter_automatic":
|
|
case "_notice_place_enter_automatic_subscription":
|
|
P3(("auto-enter(%O,%O,%O,%O) for %O in %O\n", source,
|
|
mc, data, vars, ME, place))
|
|
case "_notice_place_enter_subscribe":
|
|
case "_notice_place_enter": // = /c <channel>
|
|
case "_notice_place_enter_other": // = /c
|
|
case "_notice_place_enter_login": // primary join, but used to be quiet..
|
|
// _unicast comes from MUCs and carries a faked _context
|
|
case "_notice_place_enter_unicast":
|
|
//case "_notice_place_enter_quiet":
|
|
if (beQuiet != -1 && vars["_context"] != place) {
|
|
if (beQuiet == source) pal = 3093;
|
|
else beQuiet = source;
|
|
}
|
|
case "_notice_place_enter_follow": // = /f
|
|
case "_notice_place_enter_home": // = /h
|
|
case "_notice_place_enter_join": // = /j <channel>
|
|
P2(("msg.enter(%O,%O,%O,%O) for %O in %O\n", source,
|
|
mc, data, vars, ME, place))
|
|
// der neue parse.i liefert source als object
|
|
if (pal == 3093) return 1;
|
|
vSet("otherplace", vars["_context"] || source);
|
|
variant = "";
|
|
pal = 0;
|
|
break;
|
|
case "_notice_place_leave_automatic":
|
|
case "_notice_place_leave_automatic_subscription":
|
|
P2(("auto-leave(%O,%O,%O,%O) for %O in %O\n",
|
|
source, mc, data, vars, ME, place))
|
|
case "_notice_place_leave_logout":
|
|
case "_notice_place_leave_netburp":
|
|
case "_notice_place_leave_disconnect":
|
|
// _unicast comes from MUCs and carries a faked _context
|
|
case "_notice_place_leave_unicast":
|
|
if (beQuiet != -1 && vars["_context"] != place) {
|
|
if (beQuiet == source) pal = 3093;
|
|
else beQuiet = source;
|
|
}
|
|
// fall thru
|
|
case "_echo_place_leave_follow":
|
|
case "_echo_place_leave_home":
|
|
case "_echo_place_leave_invalid":
|
|
case "_echo_place_leave_netburp":
|
|
case "_echo_place_leave_other":
|
|
case "_echo_place_leave_reload":
|
|
case "_echo_place_leave_reload_server":
|
|
case "_echo_place_leave_subscribe":
|
|
case "_echo_place_leave_banned":
|
|
// stellt sich die frage, ob _notice und _echo wirklich
|
|
// beide existieren sollten, nur weil es ein unicast ist.
|
|
// _notice kann man doch auch unicasten, und man würde sich
|
|
// diese dämliche verdoppelung sparen.
|
|
case "_notice_place_leave_follow":
|
|
case "_notice_place_leave_home":
|
|
case "_notice_place_leave_invalid":
|
|
case "_notice_place_leave_other":
|
|
case "_notice_place_leave_reload":
|
|
case "_notice_place_leave_reload_server":
|
|
case "_notice_place_leave_subscribe":
|
|
case "_notice_place_leave_banned":
|
|
variant = 0; // no double variants
|
|
// fall further
|
|
case "_echo_place_leave":
|
|
case "_notice_place_leave":
|
|
// P1(("vars %O != %O(%O)\n", vars,place,v("place")))
|
|
#if 0
|
|
if ((ME == source || (member(rplaces, source) && nick ==
|
|
MYNICK)) && (t = vars["_nick_place"])) {
|
|
#else
|
|
if (ME == source && member(places, t = vars["_context"])
|
|
|| member(places, t = source)) {
|
|
#endif
|
|
if (abbrev("_notice_place_leave", mc)
|
|
&&! abbrev("_notice_place_leave_reload", mc))
|
|
mc[0..6] = "_echo";
|
|
#if 0
|
|
P2(("%O %s's %O\n", ME, mc, t))
|
|
m_delete(places, t); // places -= ([ t ]);
|
|
P4(("%O %O: place %O, t %O, context %O\n", ME, mc,
|
|
place, t, vars["_context"]))
|
|
// t should be more accurate
|
|
if (place == (vars["_context"] || source)) {
|
|
place = 0;
|
|
// vDel("place"); // wrong! dann kommen die
|
|
// leute nich mehr in ihren
|
|
// letzten raum rein!
|
|
// leave the context slave
|
|
// BUG!! this does not belong within this if
|
|
deregister_context(ME, vars["_context"] || source);
|
|
// t should be more accurate
|
|
}
|
|
#else
|
|
leavePlace(t);
|
|
#endif
|
|
}
|
|
if (pal == 3093) return 1;
|
|
pal = 0;
|
|
break;
|
|
case "_notice_list_feature_server":
|
|
case "_notice_list_feature":
|
|
case "_notice_list_item":
|
|
case "_notice_list":
|
|
case "_notice":
|
|
case "_status_place_topic_official":
|
|
case "_status_place_topic":
|
|
case "_status_place":
|
|
case "_status":
|
|
break; // optimization: avoid frequent unnecessary slicing
|
|
PSYC_SLICE_AND_REPEAT
|
|
}
|
|
#if 0 // reserved for the future when aliases are also affecting places
|
|
if (v("aliases")[nick])) {
|
|
if (objectp(source)) { // this should alway be true
|
|
// as /alias doesn't allow aliases to
|
|
// contain ":", but you can never know.
|
|
// nick collision!
|
|
nick = psyc_name(source);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef USE_THE_NICK
|
|
// imho macht das hier probleme bei _echo_place_enter
|
|
// da ist der raum die source, es ist kein _context dabei
|
|
// vars["_nick"] ist der nick des joinenden (==qName())
|
|
// u:msg _nick "fippo" is changed into
|
|
// "«psyc://goodadvice.pages.de/@your-community» fippo"
|
|
// IMHO this stuff should be hacked into an _INTERNAL var,
|
|
// not a real var - fippo
|
|
D2( if (vars["_nick"] != nick)
|
|
D(S("u:msg _nick %O is changed into %O\n", vars["_nick"], nick)); )
|
|
|
|
#ifdef ALPHA
|
|
// DANGEROUS CHANGE! I added this line to make sure
|
|
// remote UNI is shown on private messages. let's see
|
|
// if it breaks anything --- 2003-05-09
|
|
//
|
|
// for irssi w/ no-irssi-echo-chatting a backup comes in handy.
|
|
// --- 2005-01-07
|
|
//
|
|
// tg has found out that this little line keeps masquerade from
|
|
// breaking for ircers: they would see object path otherwise
|
|
unless (vars["_nick_verbatim"]) vars["_nick_verbatim"] = vars["_nick"];
|
|
#endif
|
|
vars["_nick"] = nick;
|
|
// this at least works also for HTTP
|
|
#endif
|
|
if (! wAction(mc, data, vars, source, variant
|
|
#ifdef USE_THE_NICK
|
|
, nick
|
|
#endif
|
|
)) {
|
|
if (!data && vars["_action_possessive"])
|
|
w(mc+"_action_possessive", data, vars, source, showingLog);
|
|
else
|
|
w(variant ? mc+variant : mc, data, vars, source, showingLog);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// pr() has replaced p(), P() and print() in most cases.
|
|
// it uses the PSYC "message code" (actually a PSYC method)
|
|
// for classification of output going to the user
|
|
// (a bit like lastlog_level in ircII, remember?)
|
|
// but all of this should become w() now, since sprintf isnt flexible enuff
|
|
//
|
|
pr(mc, fmt, a,b,c,d,e,f,g,h,i,j,k) {
|
|
P2(("user:pr(%O,%O,%O,%O..)\n", mc,fmt,a,b))
|
|
if (mc) {
|
|
#ifdef PRO_PATH
|
|
mapping di = printStyle(mc);
|
|
if (di["_method"]) mc = di["_method"];
|
|
#endif
|
|
unless (fmt = T(mc, fmt)) return;
|
|
#ifdef PRO_PATH
|
|
if (di["_prefix"]) fmt = di["_prefix"]+fmt;
|
|
#endif
|
|
}
|
|
return emit(sprintf(fmt, a,b,c,d,e,f,g,h,i,j,k));
|
|
}
|
|
|
|
// the next generation comes along: w() expects
|
|
// a properly psyc-formatted string in data where it can replace
|
|
// the vars into and then output it. the text database can provide
|
|
// a "localized" format string using psyc-formatting, too.
|
|
//
|
|
w(string mc, string data, mapping vars, mixed source, int showingLog) {
|
|
string template, output, type, loc;
|
|
mapping locs;
|
|
mapping di = printStyle(mc); // display infos
|
|
int t;
|
|
|
|
unless (vars) vars = ([]);
|
|
else t = vars["_time_log"] || vars["_time_place"];
|
|
// would be nicer to have _time_log in /log rather than showingLog
|
|
if (!t && showingLog) t = vars["_time_INTERNAL"];
|
|
if (t && intp(t)) di["_prefix"] = time_or_date(t) +" ";
|
|
#if 0
|
|
template = T(di["_method"] || mc, 0);
|
|
#else
|
|
template = T(di["_method"] || mc, "");
|
|
#endif
|
|
P3(("%O user:w(%O,%O,%O,%O) - %O\n", ME,mc,data,vars,source, template))
|
|
|
|
#ifndef _flag_enable_alternate_location_forward
|
|
// why did it say.. if (mc != lastmc && ...
|
|
if (mappingp(v("locations")) && sizeof(v("locations"))) {
|
|
string nudata = data;
|
|
|
|
// this little thing enables languages for psyc clients etc.
|
|
if (template && strlen(template) && !abbrev("_message", mc))
|
|
nudata = template;
|
|
|
|
//PT(("%O user:w(%O,%O..%O) - %O\n", ME,mc,data,source, template))
|
|
P4(("%O user:w locations %O\n", ME, v("locations")))
|
|
foreach (type, locs : v("locations")) {
|
|
// check uniformness of location here?
|
|
// no! no broken location should have made it into the
|
|
// mapping at this point. if you need to check it, check
|
|
// it in sLocation()
|
|
#if DEBUG > 0
|
|
if (!sizeof(locs)) {
|
|
// oh.. happens on beta?
|
|
P1(("%O late deletion of a %O zero location - should never happen\n", ME, type))
|
|
//m_delete(v("locations"), type);
|
|
sLocation(type, 0);
|
|
continue;
|
|
}
|
|
#endif
|
|
// allow for intp(type) then we can have several
|
|
// see-all clients, not just [0]. needs support from person.c
|
|
if (intp(type) || strstr(mc, type) != -1) {
|
|
// we dont need no rendered data! who needs that anyway?
|
|
// so this whole thing probably belongs
|
|
// back into person:msg()
|
|
// P3(("PSYCW: %s -> %O == %O? (%O)\n", mc, loc, source, vars))
|
|
// do we want to see stupid echoes of our stuff?
|
|
// if (source == loc) continue;
|
|
// this is the place to generate _source_relay for clients
|
|
//
|
|
// this causes /history to not provide the true originator
|
|
// of a message, as the room providing history already IS a
|
|
// _source_relay. so do we really need a hierarchy of relays?
|
|
// who's going to vouch for them? :)
|
|
//
|
|
if (source) {
|
|
#if 0 //ndef EXPERIMENTAL
|
|
// _source_relay already contains context submitter
|
|
if (member(vars, "_source_relay")) {
|
|
if (source == vars["_source_relay"]) {
|
|
// shouldn't happen:
|
|
m_delete(vars, "_source_relay_relay");
|
|
} else {
|
|
vars["_source_relay_relay"] =
|
|
vars["_source_relay"];
|
|
vars["_source_relay"] = source;
|
|
}
|
|
} else {
|
|
vars["_source_relay"] = source;
|
|
// shouldn't happen:
|
|
m_delete(vars, "_source_relay_relay");
|
|
}
|
|
#else
|
|
// whoever gave us _source_relay was trustworthy
|
|
unless (member(vars, "_source_relay")) {
|
|
vars["_source_relay"] = source;
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef LPC3
|
|
// i have no idea how a _message_private can trigger this
|
|
// code but it does. member(vars, "_context") should not
|
|
// return true in that case. very strange!! therefore
|
|
// i add "&& vars["_nick_place"]" ... sigh!
|
|
if (member(vars, "_context") &&! vars["_context"]
|
|
&& vars["_nick_place"]) {
|
|
// the context was an object, but got lost
|
|
// during ldmud's lastlog persistence. let's
|
|
// reconstruct it!
|
|
vars["_INTERNAL_context"] = SERVER_UNIFORM;
|
|
// the place nick shouldn't be missing, but
|
|
// sometimes it does. let's figure out why
|
|
if (stringp(vars["_nick_place"]))
|
|
vars["_INTERNAL_context"] += "@"+
|
|
lower_case(vars["_nick_place"]);
|
|
else {
|
|
P1(("%O encountered context reconstruction from nickless context (%O, %O, %O)\n", ME, mc, nudata, vars))
|
|
}
|
|
}
|
|
#else
|
|
# echo No LPC? Wow. Good luck!
|
|
#endif
|
|
foreach (loc : locs) {
|
|
#ifdef _flag_enable_circuit_proxy_multiplexing
|
|
// this is necessary when a single proxy is emulating
|
|
// several clients for several users. to figure out
|
|
// which context stuff is forwarded to which client
|
|
// we need to add this _target_forward. this is not the
|
|
// way psyc should operate in the long term. psyc clients
|
|
// should be integrated into the context distribution
|
|
// tree themselves, thus the proxy would manage a cslave
|
|
// for them instead of accepting forwards from each UNI
|
|
vars["_target_forward"] = loc;
|
|
// maybe this can be avoided when no _context is set...?
|
|
P3(("%O user:w forwarding %O to %O\n", ME, mc, vars["_target_forward"]))
|
|
#endif
|
|
sendmsg(loc, mc, nudata, vars);
|
|
// PT(("PSYCW: %s -> %O (%O)\n", mc, loc, vars))
|
|
#if DEBUG > 1
|
|
log_file("PSYCW", "%s(%O) %O » %O\n", mc, type,
|
|
nudata, type && loc);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // !_flag_enable_alternate_location_forward
|
|
|
|
unless (interactive(ME)) return 1;
|
|
// who is that output for anyway?
|
|
|
|
output = psyctext(template, vars, data, source);
|
|
if (di["_prefix"]) output = di["_prefix"]+output;
|
|
|
|
if (output) {
|
|
if (template == "") {
|
|
// desperate security precaution until we have known
|
|
// methods. quite bad for english language users.
|
|
// the additional check for [_nick] is very desperate
|
|
// and should disappear sometime soon.. TODO
|
|
//
|
|
// we could aswell refine this by taking trustworthiness
|
|
// into consideration.. showing the localhost url for CVS
|
|
// notifications is just plain wrong
|
|
//
|
|
if (!stringp(source)|| (vars["_INTERNAL_trust"] >5
|
|
|| (vars["_nick"] && data && strlen(data)
|
|
&& strstr(data, "[_nick]") != -1)))
|
|
output += "\n";
|
|
else {
|
|
//PT(("notemp %O %O %O\n", source, vars, data))
|
|
output = "«"+source+"» "+ output +"\n";
|
|
}
|
|
}
|
|
if (output != "")
|
|
#ifdef RELAY
|
|
emit(output, source);
|
|
#else
|
|
emit(output);
|
|
#endif
|
|
}
|
|
D2(else PP(("user:w zero output in %O for %O (%O)\n", ME, source, mc));)
|
|
return 1;
|
|
}
|
|
|
|
wAction(mc, data, vars, source, variant, nick) {
|
|
P3(("wAction(%O,%O..%O,%O,%O)\n", mc, data, source, variant, nick))
|
|
ASSERT("sLocation", v("locations"), v("locations"))
|
|
if (vars["_action"]) {
|
|
mapping va;
|
|
|
|
ASSERT("wAction", stringp(vars["_action"])
|
|
&& strlen(vars["_action"]), vars["_action"])
|
|
if (nick) {
|
|
#ifdef VARS_IS_SACRED
|
|
// still holding on to shared vars. probably bad idea.
|
|
va = copy(vars);
|
|
#else
|
|
va = vars;
|
|
#endif
|
|
va["_nick"] = nick;
|
|
} else va = vars;
|
|
|
|
if (data && data != "")
|
|
w(mc+"_text_action"+variant, data, va, source);
|
|
else {
|
|
if (va["_nick_target"])
|
|
w(mc+"_action"+variant, 0, va, source);
|
|
else
|
|
w(mc+"_action"+variant,
|
|
#ifdef ALPHA
|
|
0,
|
|
#else
|
|
"[_nick] [_action].",
|
|
#endif
|
|
va, source);
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if DEBUG > 0
|
|
// coming from master/master.c
|
|
runtime_error(error, program, current_object, line, sprintfed) {
|
|
w("_failure_exception_runtime", sprintfed);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
logon() {
|
|
string t;
|
|
|
|
P2(("LOGON %O from %O\n", ME, query_ip_name() ))
|
|
unless (legal_host(query_ip_number(), 0, 0, 0)) {
|
|
// this happens when people reconnect during the shutdown
|
|
// procedure.. and also when they are banned, but huh..
|
|
// that hardly ever happens :)
|
|
w("_error_rejected_address",
|
|
"You are temporarily not permitted to connect here.");
|
|
//"I'm afraid you are no longer welcome here.");
|
|
return remove_interactive(ME);
|
|
// and the object will deteriorate when user gives up..
|
|
}
|
|
// shouldn't this be qScheme() instead? little paranoid TODO
|
|
// but then we would have to move qScheme() from the server.c's
|
|
// into the common.c's .. well, we could do that some day
|
|
t = v("scheme");
|
|
ASSIGN_SHOWECHO(v("echo"), t)
|
|
if (t == "irc" || t == "psyc" || t == "jabber")
|
|
beQuiet = -1; // never turn off less interesting enter/leave echoes
|
|
// makeToken() isn't a good idea here, apparently
|
|
sTextPath(v("layout"), v("language"), t);
|
|
// cannot if (greeting) this since jabber:iq:auth depends on this
|
|
// (use another w() maybe?)
|
|
w("_notice_login", 0, ([ "_nick": MYNICK,
|
|
"_page_network": "http://www.psyc.eu/",
|
|
"_name_network": "PSYC" ]), ME );
|
|
while (remove_call_out(#'quit) != -1);
|
|
#ifndef _flag_disable_info_session
|
|
if (t == "irc" || t == "tn") w("_warning_usage_set_charset",
|
|
0, ([ "_charset": v("charset") || SYSTEM_CHARSET ]));
|
|
#endif
|
|
autojoin();
|
|
cmdchar = (v("commandcharacter") ||
|
|
T("_MISC_character_command", "/"))[0..0];
|
|
#ifndef _flag_disable_info_session
|
|
if (greeting) {
|
|
w("_warning_usage_set_language",
|
|
"Mittels \"/set language de\" kann zur deutschen Sprache gewechselt werden.");
|
|
if (cmdchar != "/") w("_warning_modified_command_character",
|
|
"Beware, \"[_command_character]\" is configured as your command character.",
|
|
([ "_command_character" : cmdchar ]) );
|
|
}
|
|
#endif
|
|
cmdchar = cmdchar[0];
|
|
|
|
actchar = v("actioncharacter") || T("_MISC_character_action",":");
|
|
actchar = actchar == "off" ? 0 : actchar[0];
|
|
|
|
// this code appears twice
|
|
if (v("query")) w("_status_query_on",
|
|
"Dialogue with [_nick_target] continued.",
|
|
([ "_nick_target" : v("query") ])
|
|
);
|
|
|
|
#ifdef JABBER_PATH
|
|
// currently only used by net/jabber/user.c
|
|
unless(v("peoplegroups")) vSet("peoplegroups", ([]));
|
|
#endif
|
|
#ifdef _flag_log_hosts
|
|
vSet("ip", query_ip_number(ME) ||
|
|
(this_interactive() && query_ip_number(this_interactive())) );
|
|
#endif
|
|
::logon( query_ip_name(ME) ||
|
|
(this_interactive() && query_ip_name(this_interactive())) );
|
|
}
|
|
|
|
// net/jabber/user filters this call, so this is never executed for
|
|
// jabber users. how sad, why miss out on all newscasts? we should
|
|
// refine this so at least newscasts are kept. or should we solve
|
|
// that by +enrol? that would be jabber style. TODO
|
|
// btw irc has it's own autojoin, which is a little different
|
|
autojoin() {
|
|
#ifndef _flag_disable_place_enter_automatic
|
|
string s;
|
|
object o;
|
|
|
|
# ifdef FORCE_PLACE
|
|
// per local policy, all users must join the default place
|
|
// a community-esq feature
|
|
vSet("place", DEFPLACE);
|
|
# endif
|
|
P2(("autojoin with %O %O %O\n", v("place"), place, places))
|
|
// see also http://about.psyc.eu/Client_coders#Room_members
|
|
if (sizeof(places)) {
|
|
#if 0
|
|
if (v("scheme") != "irc" && v("scheme") != "psyc" &&
|
|
v("scheme") != "jabber") {
|
|
// ouch.. this has to be a notice! TODO!
|
|
sendmsg(place, "_status_person_present_netburp",
|
|
"[_nick] turns alive again.", ([
|
|
"_nick" : MYNICK ]) );
|
|
#if 0
|
|
// fake it - use _echo_place_enter_relinked?
|
|
// this message should only be displayed for IRC users
|
|
// well, psyc clients need it too..
|
|
foreach (o, s : places)
|
|
msg(o, "_echo_place_enter_relinked",
|
|
"You reenter [_nick_place] after interruption.",
|
|
([ "_nick_place" : s,
|
|
"_source_relay" : ME ]));
|
|
#endif
|
|
} else {
|
|
#endif
|
|
foreach (o, s : places) {
|
|
# ifdef FORCE_PLACE_RESET
|
|
// we could have a v("leaveonreconnect") instead of an ifdef for this
|
|
placeRequest(o,
|
|
# ifdef SPEC
|
|
"_request_context_leave"
|
|
# else
|
|
"_request_leave"
|
|
# endif
|
|
"_netburp", 0, 1);
|
|
# else
|
|
P2(("%O relinking %O\n", ME, o))
|
|
placeRequest(o,
|
|
# ifdef SPEC
|
|
"_request_context_enter"
|
|
# else
|
|
"_request_enter"
|
|
# endif
|
|
"_relink", 0, 1);
|
|
# endif
|
|
}
|
|
// }
|
|
}
|
|
else {
|
|
# ifndef _flag_disable_place_default
|
|
unless (v("place"))
|
|
vSet("place", T("_MISC_defplace", DEFPLACE));
|
|
// re-entering your last place is unusual by irc
|
|
// habits, but since we don't want people to set up
|
|
// autojoins they may find this quite practical until
|
|
// they decide upon subscribe or not. the requested
|
|
// history is probably not necessary for ircers, but
|
|
// shouldn't harm either. jabberers never get here.
|
|
# ifndef _limit_amount_history_place_default
|
|
# define _limit_amount_history_place_default 5 // what about history glimpse?
|
|
// history glimpse is how much a room will
|
|
// allow (at max), this is how much the client
|
|
// requests. maybe this should go, _login kind of
|
|
// says it, too.
|
|
# endif
|
|
teleport(v("place"), "_login", 0, 0, ([ "_amount_history":
|
|
_limit_amount_history_place_default ]));
|
|
# endif
|
|
// subscriptions are stored in lowercase, warum auch immer
|
|
if (sizeof(v("subscriptions"))) {
|
|
// string t1;
|
|
P3(("places %O subscriptions %O\n",
|
|
places, v("subscriptions")))
|
|
// code to avoid "double join" in particular for irc
|
|
// t1 = lower_case(v("place") || "");
|
|
// used to also handle v("home") which was complete
|
|
// non-sense...
|
|
foreach (s in v("subscriptions"))
|
|
// if (s != t1)
|
|
{
|
|
P3(("enter: %O\n", s))
|
|
// teleport() weeds out all places we are in
|
|
// and avoids so called 'double joins' which
|
|
// are bad for IRC
|
|
teleport(s, "_automatic_subscription",
|
|
1, 1);
|
|
// so we don't use this stuff anymore:
|
|
//placeRequest(s,
|
|
// "_request_enter_automatic_subscription");
|
|
}
|
|
}
|
|
}
|
|
#endif // _flag_disable_place_enter_automatic
|
|
}
|
|
|
|
quit(immediate, variant) {
|
|
string s;
|
|
object o;
|
|
|
|
// this change has something to do with SANE_QUIT and fixing
|
|
// the double joins.. says tobij. just a micro-hint.
|
|
//if (immediate) return ::quit(immediate);
|
|
if (find_call_out(#'quit) != -1) return ::quit(immediate);
|
|
|
|
if (leaving++) {
|
|
D1( D("recursive quit() in net/user: intercepted\n"); )
|
|
return 0; // ignore potential errors from emit()
|
|
}
|
|
P2(("QUIT(%O,%O) in %O: %O\n", immediate, variant, ME, places))
|
|
unless (variant) variant = "_logout";
|
|
// if (v("subscriptions")) foreach (s in v("subscriptions"))
|
|
// placeRequest(s, "_request_leave_automatic_subscription", 1);
|
|
#ifdef SUBSCRIBE_PERMANENT
|
|
// this may even work, but irc clients go crazy at seeing double joins
|
|
// we should make this a feature of /detach instead, maybe?
|
|
int stayinalive = 0;
|
|
|
|
// so, SUBSCRIBE_PERMANENT won't work for now.
|
|
if (sizeof(v("subscriptions")) && widthof(v("subscriptions"))) {
|
|
foreach (o, s : places) {
|
|
P3(("Stay in %O, %O?\n", o, s))
|
|
if (v("subscriptions")[lower_case(s)] != SUBSCRIBE_PERMANENT)
|
|
placeRequest(o,
|
|
#ifdef SPEC
|
|
"_request_context_leave"
|
|
#else
|
|
"_request_leave"
|
|
#endif
|
|
+ variant, 1, 1);
|
|
else
|
|
stayinalive++;
|
|
}
|
|
} else
|
|
#endif
|
|
foreach (o, s : places)
|
|
placeRequest(o,
|
|
#ifdef SPEC
|
|
"_request_context_leave"
|
|
#else
|
|
"_request_leave"
|
|
#endif
|
|
+ variant, 1, 1);
|
|
leaving = 0; // sonst gehen die announce's nicht raus
|
|
//place = 0; // maybe? dunno.
|
|
return ::quit(immediate, variant);
|
|
}
|
|
|
|
// driver calls us here to tell us we lost the connection
|
|
// if you don't like this default behaviour, override it
|
|
//
|
|
// we also call this manually from _request_unlink_disconnect
|
|
disconnected(remainder) {
|
|
P2(("disconnected(%O) called in %O\nlocations: %O\n", remainder, ME, v("locations")))
|
|
// if there are any catch-all connections left don't quit, just delete link
|
|
if (member(v("locations"), 0) && sizeof(v("locations")[0]) > 1)
|
|
return linkDel(0, previous_object());
|
|
|
|
// user did not detach his client properly. we'll make a wild guess
|
|
// at how many messages he may have missed - enough to make the user
|
|
// check the lastlog if that's not enough.
|
|
// FIXME: problem with jabber/user running into some bug when
|
|
// lastlog messages are shown.. which at this point of course
|
|
// creates a recursion - thus, eliminating the otherwise useful
|
|
// feature of replay-on-tcp-loss here.
|
|
if (find_call_out(#'quit) == -1 && v("scheme") != "jabber") {
|
|
// can't use "leaving" here because it only serves the
|
|
// purpose of detecting recursion within quit(), thus
|
|
// gets resetted before arriving here.
|
|
P1(("unexpected disconnect in %O\n", ME))
|
|
vInc("new", 7);
|
|
}
|
|
// actually - we could show all messages since last activity
|
|
// from user. TODO
|
|
#ifdef AVAILABILITY_OFFLINE
|
|
if (availability == AVAILABILITY_OFFLINE) {
|
|
P1(("i think i am already offline, so i won't quit (%O)\n", ME))
|
|
return;
|
|
}
|
|
#endif
|
|
if (find_call_out(#'quit) != -1) return;
|
|
// if (place) sendmsg(place, "_notice_place_leave_disconnect",
|
|
// "[_nick] disconnects from the twilight zone.",
|
|
// ([ "_nick" : MYNICK ]) );
|
|
#if 1
|
|
// disconnect is never a good way of quitting anyway
|
|
//call_out(#'quit, 10, 1);
|
|
// okay but why immediate here?
|
|
call_out(#'quit, 10, 0, "_disconnect");
|
|
#else
|
|
quit(0, "_disconnect");
|
|
#endif
|
|
return 0; // unexpected
|
|
}
|
|
|
|
#ifndef _flag_disable_module_presence
|
|
// usually called from logon, unless set to manual
|
|
announce(level, manual, verbose, text) {
|
|
::announce(level, manual, verbose, text);
|
|
unless (ONLINE) return;
|
|
// psyc://x-net.hu/~tg probably just ran into a bug:
|
|
// i forgot to put this if here.
|
|
if (verbose) return showFriends(1);
|
|
}
|
|
#endif
|
|
|
|
// belongs into person.c as it is being called from there..?
|
|
static showFriends(verbose) {
|
|
int i, idle, lnum, isaway;
|
|
string *list;
|
|
array(mixed) k;
|
|
mixed o, n, *fmt;
|
|
|
|
P3(("showFriends in %O: %O\n", ME, friends))
|
|
k = m_indices(friends);
|
|
unless(k && sizeof(k)) return;
|
|
|
|
fmt = allocate(2);
|
|
fmt[1] = T("_list_friends_present_each", "%s\n");
|
|
fmt[1] = (fmt[1] == "%s\n") ? 0 : fmt[1][0..<2];
|
|
fmt[0] = T("_list_friends_away_each", "%s\n");
|
|
fmt[0] = (fmt[0] == "%s\n") ? 0 : fmt[1][0..<2];
|
|
|
|
list = allocate(6, "");
|
|
list[2] = ([ ]); list[5] = ([ ]);
|
|
for(i=sizeof(k); i;) {
|
|
// there is a 'bug' inside which exposes idletimes of people you have offered
|
|
// friendship to - just check the lvl
|
|
o = k[--i];
|
|
#ifdef AVAILABILITY_NEARBY
|
|
isaway = friends[o, FRIEND_AVAILABILITY] < AVAILABILITY_NEARBY;
|
|
#else
|
|
isaway = 0;
|
|
#endif
|
|
list[isaway * 3 + 2][o] = friends[o, FRIEND_NICK];
|
|
|
|
if (objectp(o)) {
|
|
#ifdef ALIASES
|
|
string n2;
|
|
|
|
n = o->qNameLower();
|
|
if (n && !(n2 = raliases[n]) && aliases[n]) {
|
|
n2 = psyc_name(o);
|
|
}
|
|
if (n2) n = n2;
|
|
#else
|
|
unless(stringp(n = friends[o, FRIEND_NICK])) friends[o, FRIEND_NICK] = n = o->qName();
|
|
#endif
|
|
list[isaway * 3] += ", "+ n;
|
|
list[isaway * 3 + 1] += ", "+ (fmt[isaway] ? sprintf(fmt[isaway],n,n,n) : n);
|
|
// for once we leave out the CALC_IDLE_TIME.. ;)
|
|
if (verbose && idle = o->vQuery("aliveTime"))
|
|
list[isaway * 3 + 1] += " [" + timedelta(time() - idle) + "]";
|
|
} else {
|
|
// TODO: do handling of remote nicks here
|
|
// aliases + nickname here?
|
|
// can we do the aliases bit when *storing* the friends?
|
|
// unless (stringp(n = friends[o])) friends[o] =
|
|
n = to_string(o);
|
|
#ifdef ALIASES
|
|
n = raliases[lower_case(n)] || n;
|
|
#endif
|
|
list[isaway * 3] += ", "+ n;
|
|
list[isaway * 3 + 1] += ", "+ (fmt[isaway] ? sprintf(fmt[isaway],n,n,n) : n);
|
|
}
|
|
}
|
|
// if (list2 == "") list2 = list1;
|
|
if (strlen(list[1]) > 2)
|
|
w("_list_friends_present", "Friends online: [_friends].",
|
|
([ "_friends" : list[1][2..],
|
|
"_list_friends" : m_indices(list[2]), // _tab
|
|
"_list_friends_nicknames" : m_values(list[2]) ]) );
|
|
// man könnte ja die blöden textstrings dahinterschreiben
|
|
// wenn es nicht zuviele sind..
|
|
if (strlen(list[4]) > 2)
|
|
w("_list_friends_away", "Friends away: [_friends_away].",
|
|
([ "_friends_away" : list[4][2..],
|
|
"_list_friends_away" : m_indices(list[5]), // _tab
|
|
"_list_friends_nicknames_away" : m_values(list[5]) ]) );
|
|
// pr("_list_friends_present", "Friends online: %s\n", list2[2..]);
|
|
return list[0][2..];
|
|
}
|
|
|
|
listDescription(vars, eachout, nicklink) {
|
|
string k, na, va, buf, fmt, t;
|
|
|
|
buf = "";
|
|
P3(("%O listDescription(%O,%O,%O)\n", ME, vars, eachout, nicklink))
|
|
#ifndef _flag_enable_profile_table
|
|
// html version with more intelligent tables:
|
|
// <tr id="[_key]"><td class="ldpek">[_name_key]</td><td class="ldpev">[_value]</td></tr>
|
|
fmt = T("_list_description_each_item", "[_key]: [_value]\n");
|
|
#else
|
|
// maybe someday we'll use psyctext() instead, but for now this
|
|
// is sooo much faster. too bad the %22 trick doesn't work for utf8
|
|
fmt = T("_list_description_each", "%22s: %s\n");
|
|
#endif
|
|
if (vars["_identification_alias"]) {
|
|
m_delete(vars, "_identification_scheme_XMPP");
|
|
//m_delete(vars, "_identification_scheme_SIP");
|
|
//m_delete(vars, "_identification_scheme_IRC");
|
|
}
|
|
if (vars["_host_name"] &&! boss(ME)) {
|
|
m_delete(vars, "_host_IP");
|
|
}
|
|
#if 1 // fix and remove this.. TODO
|
|
if (vars[0]) {
|
|
P1(("Encountered vars[0] containing %O\n", vars[0]))
|
|
vDel(vars, 0);
|
|
}
|
|
#endif
|
|
foreach (k : sort_array(m_indices(vars), #'>)) {
|
|
va = vars[k];
|
|
switch(k) {
|
|
case "_color":
|
|
if (v("scheme") == "applet") break;
|
|
// else fall thru and filter
|
|
case "_nick":
|
|
case "_nick_stylish":
|
|
case "_uniform_style":
|
|
case "_uniform_photo_small":
|
|
case "_INTERNAL_type_image_photo":
|
|
va = 0;
|
|
break;
|
|
case "_uniform_photo":
|
|
P4(("photo is %O\n", va))
|
|
if (nicklink) va = 0;
|
|
break;
|
|
case "_language":
|
|
if (va == v("language")) va = 0;
|
|
break;
|
|
case "_protocol_agent":
|
|
if (va == v("scheme")) va = 0;
|
|
break;
|
|
case "_agent_design":
|
|
if (va == v("layout")) va = 0;
|
|
break;
|
|
case "_action_motto":
|
|
va = vars["_nick"] +" "+ vars[k];
|
|
k = "_motto";
|
|
break;
|
|
case "_list_groups": // _tab
|
|
#if 0
|
|
if (nicklink) {
|
|
int i;
|
|
string n, u;
|
|
string s = SERVER_UNIFORM;
|
|
|
|
va = copy(va);
|
|
for (i=sizeof(va)-1; i>=0; --i) {
|
|
n = va[i];
|
|
#if 1
|
|
// TODO? this doesn't handle jids but i am
|
|
// also not sure if jids belong here anyway
|
|
u = is_formal(n) ? n : s +"@"+ n;
|
|
va[i] = sprintf(nicklink, u, n, n);
|
|
#else
|
|
va[i] = sprintf(nicklink, n, n, n);
|
|
#endif
|
|
}
|
|
}
|
|
va = implode(va, ", ");
|
|
k = "_groups";
|
|
break;
|
|
#endif
|
|
// fall thru, since (currently) all entries are formal
|
|
// the special case for ~ will never happen
|
|
case "_list_friends": // _tab
|
|
if (nicklink) {
|
|
int i;
|
|
string n, u;
|
|
string s = SERVER_UNIFORM;
|
|
|
|
va = copy(va);
|
|
for (i=sizeof(va)-1; i>=0; --i) {
|
|
n = va[i];
|
|
#if 1
|
|
// TODO? this doesn't handle jids but i am
|
|
// also not sure if jids belong here anyway
|
|
u = is_formal(n) ? n : s +"~"+ n;
|
|
va[i] = sprintf(nicklink, u, n, n);
|
|
#else
|
|
va[i] = sprintf(nicklink, n, n, n);
|
|
#endif
|
|
}
|
|
}
|
|
va = implode(va, ", ");
|
|
//k = "_friends";
|
|
k = k[sizeof("_list")..]; // _tab
|
|
break;
|
|
case "_time_idle":
|
|
case "_time_age":
|
|
if (intp(va)) va = timedelta(va);
|
|
break;
|
|
case "_INTERNAL_image_photo":
|
|
t = T("_list_description_image", "");
|
|
// this scheme cannot display this
|
|
if (t == "") { va = 0; break; }
|
|
va = psyctext(t, ([
|
|
"_image_base64": va,
|
|
"_type_image": vars["_INTERNAL_type_image_photo"]
|
|
]));
|
|
k = "_image_photo";
|
|
break;
|
|
//default:
|
|
}
|
|
if (stringp(va)) {
|
|
na = T("_TEXT"+k, "");
|
|
unless (na) {
|
|
P1(("Description: No _TEXT for %O\n", k))
|
|
} else if (na != "") {
|
|
if (eachout) w("_list_description"+k,
|
|
// sprintf("%22s: [%s]",na,k),
|
|
sprintf("%22s: %s",na,va),
|
|
([ k: va, // "_text"+k: na
|
|
]));
|
|
else
|
|
#ifndef _flag_enable_profile_table
|
|
buf += psyctext(fmt, ([ "_key": k,
|
|
"_name_key": na, "_value": va ]) );
|
|
#else
|
|
buf += sprintf(fmt, na, va);
|
|
#endif
|
|
}
|
|
// else.. no output encoding
|
|
}
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
// no longer in use
|
|
qPlace() { if (place) return v("place"); }
|
|
|
|
qPublicInfo(showguests, showplace, showinvisibles) {
|
|
mapping m;
|
|
int newb, t;
|
|
|
|
if (!ONLINE) return 3; // gets called when an offline user
|
|
// has been summoned by /tell
|
|
newb = IS_NEWBIE;
|
|
if (!showguests && newb) return 2;
|
|
if (!showinvisibles && (v("guest") || v("visibility") == "off"))
|
|
return 1;
|
|
|
|
CALC_IDLE_TIME(t);
|
|
m = ([ "nick" : MYNICK, "lowerNick" : MYLOWERNICK,
|
|
"me" : v("me"), "age" : v("age"),
|
|
"registered" : !newb,
|
|
// actual aliveTime is for /lu usage only.. not for public output
|
|
"idleTime" : t, "aliveTime" : v("aliveTime"),
|
|
"name": v("publicname") || v("longname"),
|
|
"operator" : boss(ME) > 9,
|
|
]);
|
|
#ifdef SHOW_REMOTE_PLACES_IN_PEOPLE_LIST
|
|
if (showplace) m["place"] = objectp(place)
|
|
? place->qPublicName() : place;
|
|
#else
|
|
if (showplace && objectp(place)) m["place"] = place->qPublicName();
|
|
#endif
|
|
#if 0 // __EFUN_DEFINED__(tls_query_connection_state)
|
|
if (interactive(ME) && tls_query_connection_state()) m["encrypted"] = 1;
|
|
#endif
|
|
return m;
|
|
}
|
|
|