1
0
Fork 0
mirror of git://git.psyced.org/git/psyced synced 2024-08-15 03:25:10 +00:00
psyced/world/net/include/place.gen
psyc://loupsycedyglgamf.onion/~lynX e45a2b4ef0 DEFAULT_HT_LOGO
2021-01-18 20:44:23 +01:00

791 lines
21 KiB
Text

// vim:noexpandtab:syntax=lpc
// $Id: place.gen,v 1.137 2008/09/12 15:37:39 lynx Exp $
//
// documentation on http://about.psyc.eu/Create_Place
//
// place.gen was inspired by loc.h, a similar room generation system in
// the Nemesis mudlib. Nemesis itself being a legendary MUD based in
// Munich (http://www.nemesis.de). the innovation in this room generation
// approach is to use the #include to generate code rather than to define
// a series of macros and hope the user uses them the right way.
// of course these days you could do a room generator in -say- perl..
// or you could implement a mega do-everything room class which can then
// be configured at runtime by web interface or slash command. in fact
// standard.c is just that, only that it didn't get all that popular.
#include <net.h>
#include <storage.h>
#include <status.h>
#include <uniform.h>
#ifndef DEFAULT_HT_LOGO
# define DEFAULT_HT_LOGO "/img/psyc.gif"
#endif
#ifndef HT_LOGO
# define HT_LOGO DEFAULT_HT_LOGO
#endif
#ifdef BRAIN
# ifdef SLAVE
# echo Configuration Error: BRAIN cannot be SLAVE
# endif
# undef CONNECT
#else
#ifndef DEFAULT_MASTER
# define DEFAULT_MASTER "psyc://psyced.org"
#endif
#ifdef CONNECT_DEFAULT
# ifdef NAME
//# if DEBUG > 0
//# echo CONNECT_DEFAULT is psyc://beta.ve.symlynX.com
//# define CONNECT "psyc://beta.ve.symlynX.com/@" NAME
//# else
// and after several changes in tagging and forking and dunnowhat
// the old master/slave system has died and not returned to life
# define REDIRECT DEFAULT_MASTER "/@" NAME
//# define CONNECT DEFAULT_MASTER "/@" NAME
//# endif
# else
# echo place.gen: Cannot use CONNECT_DEFAULT without #define NAME
# endif
#endif
#if 1
#ifdef SLAVE
//# undef SLAVE // slave system br0ken.. migrating to context slaves
// (it cannot deal with missing _tags for local joins etc etc)
# ifdef NAME
# define REDIRECT CONNECT
# ifndef CONNECT
# ifdef _uniform_node
# define CONNECT _uniform_node "@" NAME
# else
# define CONNECT "psyc://" SERVER_HOST "/@" NAME
# endif
# endif
# else
# echo place.gen: Cannot use SLAVE without #define NAME
# endif
#endif
#else
#ifdef SLAVE
# ifndef CONNECT
# ifdef NAME
# ifdef _uniform_node
# define CONNECT _uniform_node "@" NAME
# else
# define CONNECT "psyc://" SERVER_HOST "/@" NAME
# endif
# else
# echo place.gen: Cannot use SLAVE without #define NAME
# endif
# endif
#else
# ifdef CONNECT
# ifndef JUNCTION
# define SLAVE "local"
# endif
# endif
#endif
#endif
#if defined(SLAVE) || defined(VOLATILE)
# undef CONNECT_IRC
#endif
#endif
#if defined(MODERATED) && defined(PRO_PATH)
# ifdef SLAVE
inherit PRO_PATH "place/moslave";
# else
# ifdef LECTIC
inherit PRO_PATH "place/lectic";
# else
inherit PRO_PATH "place/momaster";
# endif
# endif
#else
# if defined(JUNCTION) || (defined(MASTER) && defined(SLAVE))
# define ALLOW_FROM_LINKS
inherit NET_PATH "place/junction";
# else
# ifdef SLAVE
inherit NET_PATH "place/slave";
# else
# ifdef CONNECT_IRC
# ifdef EMULATE_SERVER
inherit NET_PATH "irc/gateway";
# else
inherit NET_PATH "irc/gatebot";
# endif
# else
# ifdef THREADS
inherit NET_PATH "place/threads";
# else
# ifdef GAMESERV
inherit NET_PATH "place/gamespy";
# else
# ifdef OWNED
inherit NET_PATH "place/owned";
# else
# ifdef NEWSFEED_RSS
inherit NET_PATH "place/news";
# else
# ifdef MASTER
inherit NET_PATH "place/master";
# else
# ifdef HISTORY
inherit NET_PATH "place/storic";
# else
# ifdef MAILCAST
inherit NET_PATH "place/mailcast";
# else
# ifdef PUBLIC
inherit NET_PATH "place/public";
# else
# ifdef ON_UNKNOWN
// special place type that supports unknownmsg()
inherit NET_PATH "place/intercept";
# else
// special case in the archetype options logic, see also psyconf
# ifndef PLACE_HISTORY
# ifdef PLACE_HISTORY_EXPORT
# define PLACE_HISTORY
# endif
# endif
# include "place.i" // archetype model generator code 2007
# ifdef PLACE_HISTORY
# define HISTORY // compatibility
# endif
# ifdef PLACE_OWNED
# define OWNED PLACE_OWNED // compatibility
# endif
# endif
# endif
# endif
# endif
# endif
# endif
# endif
# endif
# endif
# endif
# endif
# endif
#endif
#ifdef CONNECT_TELNET
inherit NET_PATH "tn/outgoing";
#endif
#ifdef FETCH_TEXTDB
volatile object feed, tob;
fetched(buffer) {
# ifdef FETCH_TEXTPATH
P1(("%O fetched: %O bytes\n", ME, strlen(buffer)))
// this destruct() should be avoidable, but there is some reason
// why it doesn't always work that needs inspection time.
if (tob) destruct(tob);
tob = (FETCH_TEXTPATH "/text") -> parseDB(buffer, FETCH_TEXTDB);
if (tob) castmsg(ME, "_notice_update_database_text",
"The text database for [_path] has been refreshed.",
([ "_path": FETCH_TEXTPATH ]) );
# else
P0(("%O fetched: %O bytes.. but what to do with it?\n",
ME, strlen(buffer)))
# endif
}
// same code in news.c !? simplify!
fetchDB() {
unless (feed) {
feed = FETCH_TEXTDB -> load();
feed->content(#'fetched, 1);
return 1;
} else {
feed->refetch(#'fetched);
return 2;
}
}
#endif
#if defined(SILENCE) || defined(NEWSFEED_RSS)
# define FILTER_PRESENCE // FILTER_TRAFFIC is also an interesting name
# define FILTER_CONVERSATION
# ifdef NEWSFEED_RSS
qNewsfeed() { return NEWSFEED_RSS; }
# define BLAME "!newsfeed"
# else
# define BLAME "!configuration"
# endif
#else
// BLAME contains an "illegal" meta-nickname indicating that something
// has been done by admin configuration rather than an interactive user
# define BLAME "!configuration"
#endif
#ifdef MAY_HISTORY
// only use this hook if PLACE_MAY_HISTORY was defined in the place
// blueprint you are using (see archetype.gen). otherwise this hook
// will have zero effect, which is the default. i don't know of any
// application who needs this. it's more like a proof of concept
// how hooks work and how they can be compile time optional.
//
mayHistory(source, mc, data, vars, b) { MAY_HISTORY; }
#endif
#ifdef HISTORY_GLIMPSE
qHistoryGlimpse() { return HISTORY_GLIMPSE; }
#endif
#ifdef OWNED
qOwners() { return ([ OWNED ]); }
# ifdef HISTORY_PROTECTION
// you have to explicitely request this behaviour as we normally
// consider it a user's privacy right to delete the history
histClear(a, b, source, mapping vars) {
if (b || qAide(SNICKER))
return ::histClear(a, b >= 50 ? b : 50, source, vars);
}
# endif
#endif
#ifdef CHAT_CHANNEL
qChatChannel() { return CHAT_CHANNEL; }
#else
qChatChannel() { return "PSYC"; }
#endif
#if defined(RESET) || defined(CRESET) || defined(NEWSFEED_RSS) \
|| defined(RESET_INTERVAL)
void reset(int again) {
# ifdef CRESET
CRESET
# endif
# if defined(HISTORY) || defined(PLACE_HISTORY)
// basic.c currently has no reset to call
::reset(again);
# endif
# ifdef CONNECT_IRC
// if (!interactive(ME)) call_out(#'connect, 3, CONNECT_IRC);
# endif
# if __EFUN_DEFINED__(set_next_reset)
# ifdef RESET_INTERVAL
set_next_reset(RESET_INTERVAL * 60);
# else
// apparently this is needed for place/news not to
// fall into deep sleep. strange.
set_next_reset(24 * 60 * 60);
# endif
# endif
if (again) {
# ifdef RESET_INTERVAL
# if ! __EFUN_DEFINED__(set_next_reset)
if (time() < v("lastreset") + RESET_INTERVAL * 60) return;
vSet("lastreset", time());
# endif
# endif
# ifdef RESET
RESET
# endif
# ifdef NEWSFEED_RSS
connect();
# endif
return;
}
}
#endif
void create() {
::create();
#ifdef CRESET
CRESET
#endif
#ifdef LINK
P1(("%O - ignoring old-fashioned #define LINK %O\n", ME, LINK))
#endif
#ifdef CONNECT
# ifdef IDENTIFICATION
P1(("PLACE %O SLAVE of %O with MASTER %O\n", ME, CONNECT, IDENTIFICATION))
# else
# ifdef SLAVE
P1(("PLACE %O SLAVE of %O\n", ME, CONNECT))
# else
P1(("PLACE %O MASTER\n", ME))
# endif
# endif
# ifdef _uniform_node
# if CONNECT == _uniform_node
D1(D("link [" CONNECT "] == host ["+ _uniform_node +"]\n");)
# else
D1(D("link [" CONNECT "] != host ["+ _uniform_node +"]\n");)
# endif
# endif
#endif
#if 0 //def CONNECT
P0(("..\nPLACE %O initiating connection to %O\n", ME, CONNECT))
clone_object(PSYC_PATH "active") -> connect(CONNECT);
#endif
#ifdef NAME
// double load happens only with certain room types
// this load() is important to set identification var
// replacing it is no solution
//sName(NAME); vSet("name", NAME);
load(NAME, 1);
#else
load();
#endif
#if defined(FILTER_PRESENCE) || defined(QUIET)
vSet("_filter_presence", BLAME);
#endif
#if defined(FILTER_CONVERSATION) || defined(SILENT)
vSet("_filter_conversation", BLAME);
#endif
#ifdef RESTRICTED
vSet("_restrict_invitation", BLAME);
#else
vDel("_restrict_invitation");
#endif
#ifdef UNIFORM_STYLE
vSet("_uniform_style", UNIFORM_STYLE);
#endif
//#ifdef IRCGATE_NAME
// vSet("_gateway_name", IRCGATE_NAME);
//#endif
#ifdef SLAVE
# ifdef CONNECT
// this needs to happen before sIdentification
sMaster(CONNECT);
# endif
# if defined(IDENTIFICATION)
sIdentification(IDENTIFICATION);
# endif
#endif
#ifdef CONNECT_IRC
link(CONNECT_IRC);
#endif
#ifdef CONNECT_TELNET
link(CONNECT_TELNET);
#endif
#if defined(NEWSFEED_RSS) && defined(DEVELOPMENT)
// do not connect newsfeeds immediately if running in production mode
connect();
#endif
#ifdef MODERATED
# define ALLOW_EXTERNAL // FROM_LINKS?
# ifdef PRO_PATH
sModeratable(1);
# endif
#endif
// exits currently not supported
#ifdef EXITS
// sExits( EXITS );
#endif
#ifdef CREATE
CREATE
#endif
}
#ifdef PRIVATE
//qPublic() { return 0; }
#else
qPublicName() { return MYNICK; }
qPublic() { return BLAME; }
#endif
#ifdef ALLOW_EXTERNAL
qAllowExternal() { return 1; }
#else
# if defined(ALLOW_EXTERNAL_FROM) || defined(ALLOW_FROM_LINKS) \
|| defined(ALLOW_EXTERNAL_HOST) || defined(ALLOW_METHOD) \
|| defined(ALLOW_EXTERNAL_LOCALS) || defined(ALLOW_TRUSTED)
qAllowExternal(source, mc, vars) {
P2(("qAllowExternal(%O,%O,%O)\n", source,mc,vars))
// only one #define of ALLOW_* type makes sense
// unless (abbrev("_notice_news", mc)) return 0;
// unless (abbrev("_notice_software", mc)) return 0;
# ifdef ALLOW_TRUSTED
if (vars["_INTERNAL_trust"] > 5) return 1;
# endif
# ifdef ALLOW_METHOD
unless (abbrev(ALLOW_METHOD, mc)) return 0;
# endif
if (stringp(source)) {
# ifdef ALLOW_FROM_LINKS
// does this make things spoofable?
if (isLink(source) || isLink(vars["_source_relay"])) return 1;
# endif
# ifdef ALLOW_EXTERNAL_FROM
if (abbrev(ALLOW_EXTERNAL_FROM, source)) return 1;
# endif
# ifdef ALLOW_EXTERNAL_HOST
string *u = parse_uniform(source);
if (u && same_host(ALLOW_EXTERNAL_HOST, u[UHost])) return 1;
# endif
return 0;
}
# ifdef ALLOW_EXTERNAL_HOST
// does anyone need this?
//else if (same_host(ALLOW_EXTERNAL_HOST, query_ip_number(source))) return 1;
# endif
# ifdef ALLOW_EXTERNAL_LOCALS
// this is equivalent to irc/mode -n for local ircers. thx fip.
return 1;
# else
return 0;
# endif
}
# endif
#endif
#ifdef LAYOUT
viewHead() { return LAYOUT; }
#endif
#if defined(REQUEST_ENTER) || defined(REGISTERED) || defined(SECURE) ||\
defined(RESTRICTED) || defined(TRUSTED) || defined(NICKLESS) ||\
defined(LOCAL)
volatile mixed lastTry;
# if defined(SECURE) && defined(HISTORY) && defined(PLACE_HISTORY_EXPORT)
# undef PLACE_HISTORY_EXPORT
# endif
enter(source, mc, data, vars) {
# ifdef TRUSTED
if (vars["_INTERNAL_trust"] > 5) return 1;
# endif
# ifdef LOCAL
unless (objectp(source)) {
sendmsg(source, "_error_place_enter_restricted_local",
"Sorry, [_nick_place] is only accessible for users of the same server.",
([ "_nick_place" : MYNICK ]) );
if (source != lastTry) {
vars["_source_relay"] = source;
castmsg(ME, "_failure_place_enter_restricted_local",
"Admission into [_nick_place] denied for remote user [_source_relay].",
vars);
lastTry = source;
}
return 0;
}
# endif
# ifdef RESTRICTED
unless (qAide(SNICKER)) {
sendmsg(source, "_error_place_enter_necessary_invitation",
"[_nick_place] can only be entered upon invitation.",
([ "_nick_place" : qName() ]) );
if (source != lastTry) {
castmsg(ME, "_failure_place_enter_necessary_invitation",
"Admission into [_nick_place] denied for uninvited user [_nick].",
vars);
lastTry = source;
}
return 0;
}
# endif
# if defined(SECURE)
// let people in who are either connected via an MITM-prone TLS
// protocol or are coming from the reasonably safe localhost
// (either SSH or Tor users).
//
// both cases are no absolute guarantee for safety.. it is still
// in the hands of each user in the room to safeguard true secrecy
//
// SECURE by itself also doesn't enforce that people are registered
// or belong to a certain group, so you have to use the respective
// #defines to also ensure that, if that's what you want.
//
// -lynX 2004, updated 2015
//
int intimacy = probably_private(source);
// psyc client.. may also one day be a psyc server, in that
// case we have to hope the rest of the link is secured, too
if (intimacy == PRIVACY_UNKNOWN)
intimacy = probably_private(vars["_INTERNAL_origin"]);
if (intimacy <= PRIVACY_SURVEILLED) {
sendmsg(source, "_error_place_enter_necessary_encryption",
"[_nick_place] may only be accessed by clients with enabled encryption.",
([ "_nick_place" : qName() ]) );
if (source != lastTry) {
castmsg(ME, "_failure_place_enter_necessary_encryption",
"Admission into [_nick_place] denied for insecure user [_nick].",
vars);
lastTry = source;
}
return 0;
}
# endif
# if defined(REGISTERED)
if (objectp(source) && source->isNewbie()) {
sendmsg(source, "_error_place_enter_necessary_registration",
"We want you to be a registered PSYC user before entering [_nick_place].",
([ "_nick_place" : MYNICK ]) );
if (source != lastTry) {
castmsg(ME, "_failure_place_enter_necessary_registration",
"Admission into [_nick_place] denied for unregistered user \"[_nick]\".",
vars);
lastTry = source;
}
return 0;
}
# endif
# if defined(REQUEST_ENTER)
REQUEST_ENTER
# endif
# ifdef NICKLESS
return 1; // LEGAL_NICK_IN_ENTER
# else
return ::enter(source, mc, data, vars);
# endif
}
#endif
#if defined(ON_STATUS)
showStatus(verbosity, al, source, mc, data, vars) {
ON_STATUS
return ::showStatus(verbosity, al, source, mc, data, vars);
}
#endif
#if defined(ON_ANY) || defined(ON_CONVERSE) || defined(REDIRECT)
msg(source, mc, data, vars) {
# ifdef ON_ANY
ON_ANY
# endif
# ifdef ON_CONVERSE
if (abbrev("_message", mc)) {
ON_CONVERSE
}
# endif
# ifdef REDIRECT
sendmsg(source, "_failure_redirect_temporary",
"[_nick_place] is currently unable to fulfil this operation. Please direct your request to [_source_redirect]",
([ "_method_relay": mc,
"_tag_reply" : vars["_tag"] || 0,
// "_data_relay" : data,
# ifdef NAME
"_nick_place" : NAME,
# else
"_nick_place" : MYNICK,
# endif
"_source_redirect": REDIRECT ]));
return 0;
# else
::msg(source, mc, data, vars);
# endif
}
#endif
// this hook only works in archetypes that support it
#ifdef ON_UNKNOWN
unknownmsg(source, mc, data, vars) {
ON_UNKNOWN
::unknownmsg(source, mc, data, vars);
}
#endif
#ifdef ON_ENTER
onEnter(source, mc, data, vars) {
ON_ENTER
return ::onEnter(source, mc, data, vars);
}
#endif
// obsolete for new code.. use PLACE_HISTORY_EXPORT or not
#ifdef HISTORY_PRIVATE
# echo HISTORY_PRIVATE is obsolete. Unset PLACE_HISTORY_EXPORT instead.
// disables web-export of HISTORY in a somewhat drastic way, i admit
htget(prot, query, headers, qs) {
return 0;
}
#endif
#ifdef CHALLENGE_QUESTION // should make media player etc work also w/o challenge FIXME
#include <sys/regexp.h>
#include "ht/http.h"
#define CHALOG(verb) log_file("CHALLENGE", "%s %s %O A:%O P:%O C:%O\n", \
MYNICK, verb, query_ip_name(), \
query["answer"], query["parameters"], headers["cookie"])
// maybe this all belongs into archetype.gen.. chesmo!
htget(prot, query, headers, qs, data, noprocess) {
PT(("%O place.gen::htget\n", ME))
if (probably_private(this_interactive()) <= PRIVACY_SURVEILLED) {
hterror(prot, R_PAYMENTREQ, "To protect against abuse in this nasty world this function needs 'https' instead of 'http'.");
htnotify(query, headers, "_challenge_disabled_encryption",
"[_nick_place] sees no TLS by [_web_on] from [_web_from].");
return 0;
}
string item = "/@"+ MYNICK;
if (
# ifdef CHALLENGE_AGENT
stringp(headers["user-agent"]) &&
regmatch(lower_case(headers["user-agent"]), CHALLENGE_AGENT)
# else
# ifdef CHALLENGE_ACCOUNTS
# define CHALLENGE_CHECK CHALLENGE_ACCOUNTS
# else
# ifdef CHALLENGE_MATCH
# define CHALLENGE_CHECK CHALLENGE_MATCH
# endif
# endif
stringp(headers["cookie"]) && regmatch(headers["cookie"],
"challenge=complete&answer="+ md5(CHALLENGE_CHECK))
# endif
) {
CHALOG("completes");
htnotify(query, headers, "_challenge_accomplished_web",
"Challenge accomplished in [_nick_place] by [_web_on] coming from [_web_from].");
# ifdef CHALLENGE_REDIRECT
sTextPath(query["layout"], query["lang"], "html");
# ifdef CHALLENGE_REDIRECT_TITLE
# ifdef CHALLENGE_QUESTION
htok3(prot, 0, "Set-Cookie: psycplace=\"challenge=done\"; Path="+ item +"; Secure; Max-Age=9\n");
# else
htok();
# endif
// you can output a player iframe instead of a redirect...
w("_PAGES_frame_redirect", 0,
([ "_uniform_page" : CHALLENGE_REDIRECT,
"_title_page" : CHALLENGE_REDIRECT_TITLE,
"_nick_place" : MYNICK ]) );
return 1;
# else
return htredirect(prot, CHALLENGE_REDIRECT, "Download or redirect initiated", 0, "Content-Disposition: attachment\nSet-Cookie: psycplace=\"challenge=done\"; Path="+ item +"; Secure; Max-Age=9\n");
# endif
# else
# ifdef HTGET
return HTGET;
# else
return ::htget(prot, query, headers, qs, data, noprocess);
# endif
# endif
}
# if defined(CHALLENGE_MATCH) || defined(CHALLENGE_ACCOUNTS)
if (stringp(query["answer"])) {
unless (headers["cookie"]) {
CHALOG("disabled");
hterror(prot, R_PAYMENTREQ, "To protect against abuse in this nasty world this function needs just temporarily enabled cookies. There are no de-anonymizing purposes involved. Or did you just lowercase my name in the URL?");
htnotify(query, headers, "_challenge_disabled_web",
"[_nick_place] sees no cookies by [_web_on] from [_web_from].");
return 1;
}
string acct;
if (regmatch(headers["cookie"], "challenge=given")) {
if (query["answer"] &&
# ifdef CHALLENGE_ACCOUNTS
(acct = CHALLENGE_ACCOUNTS->consult(query["answer"]))
# else
regmatch(lower_case(query["answer"]), CHALLENGE_MATCH)
# endif
) {
// lazy me could have used referer here ;)
string nu = stringp(query["parameters"]) &&
strlen(query["parameters"]) &&
query["parameters"] != "0" ?
item +"?"+ query["parameters"] : item;
CHALOG(acct? ("authenticates as "+ acct): "reloads");
htredirect(prot, nu, "Reload, please", 0, "Set-Cookie: psycplace=\"challenge=complete&answer="+ md5(CHALLENGE_CHECK) +"\"; Path="+ item +"; Secure; Max-Age=99\n");
if (acct) htnotify(query, headers, "_challenge_authenticated_web",
"[_web_on] authenticated for [_nick_place] coming from [_web_from].", acct);
return 1;
} else {
CHALOG("fails");
htnotify(query, headers, "_challenge_failed_web",
"[_nick_place] sees [_web_on] from [_web_from] fail the challenge.");
}
}
} else
# endif
{
CHALOG("challenges");
htnotify(query, headers, "_challenge_presented_web",
"[_nick_place] challenges [_web_on] coming from [_web_from].");
// (query [_web_query], cookie [_web_cookie]).");
}
// If you have trouble reloading the HTML template
// look out for both 'ht' and 'html' textdbs!
sTextPath(query["layout"], query["lang"], "html");
// using a non-psyced cookie here so that you can't construct a
// url that allows other people to bypass the challenge.
htok3(prot, 0, "Set-Cookie: psycplace=\"challenge=given\"; Path="+ item +"; Secure; Max-Age=999\n");
# ifndef CHALLENGE_REDIRECT_TITLE
# define CHALLENGE_REDIRECT_TITLE "Challenge for "+ MYNICK
# endif
w("_PAGES_group_challenge", 0,
([ "_challenge" : htquote(CHALLENGE_QUESTION),
// if the user failed the challenge,
// we maintain the original qs for next attempt:
"_parameters" : query["parameters"] || qs,
"_uniform_logo" : HT_LOGO,
"_title_page" : CHALLENGE_REDIRECT_TITLE,
"_nick_place" : MYNICK ]) );
// printf("%O vs %O\n", query, headers);
return 1;
}
#endif
#ifdef HISTORY
# if defined(HISTORY_METHOD) || defined(HISTORY_MAY_LOG)
mayLog(mc) {
# ifdef HISTORY_MAY_LOG
HISTORY_MAY_LOG
# endif
# ifdef HISTORY_METHOD
return abbrev(HISTORY_METHOD, mc);
# endif
}
# endif
#endif
#ifdef ON_ERROR
error(source, mc, data, vars) {
ON_ERROR
}
#endif
#ifdef ON_COMMAND
cmd(command, args, privilege, source, vars) {
ON_COMMAND
return ::cmd(command, args, privilege, source, vars);
}
#endif
#if defined(PASS_IRC) || defined(ON_CONNECT)
logon(failure) {
int rc = ::logon(failure);
unless (rc) return 0;
# ifdef PASS_IRC
emit("PASS :"+ PASS_IRC +"\r\n");
# endif
# ifdef ON_CONNECT
ON_CONNECT
# endif
return rc;
}
#endif
#ifdef NEWS_PUBLISH
publish(link, headline, channel) {
unless (NEWS_PUBLISH(link, headline, channel))
::publish(link, headline, channel);
}
#endif