// 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 #include #include #include #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_MATCH #include htget(prot, query, headers, qs, data, noprocess) { if (query["challenge"] == "complete") return ::htget(prot, query, headers, qs, data, noprocess); string item = headers[item] || "/@"+ MYNICK; if (query["challenge"] == "given" && # if __EFUN_DEFINED__(regmatch) stringp(query["answer"]) && regmatch(lower_case(query["answer"]), CHALLENGE_MATCH, RE_MATCH_SUBS) # else query["answer"] == CHALLENGE_MATCH # endif ) { htredirect(prot, item, "Reload, please", 0, "Set-Cookie: psyced=\"challenge=complete\"; path="+ item +";\n"); return 1; } sTextPath(query["layout"], query["lang"], "html"); // could add a timeout here... htok3(prot, 0, "Set-Cookie: psyced=\"challenge=given\"; path="+ item +";\n"); w("_PAGES_group_challenge", 0, ([ "_challenge" : htquote(CHALLENGE_QUESTION), "_nick_place" : MYNICK ]) ); printf("%O (%O) in %O\n", query, qs, 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