blob: 02b0fcc6897f775c0a6fff02aca49b1640327276 [file] [log] [blame]
// MorgenGrauen MUDlib
//
// master/misc.c -- Diverses: (T)Banish, Projektverwaltung, Levelaufstieg, ...
//
// $Id: misc.c 9467 2016-02-19 19:48:24Z Zesstra $
#pragma strict_types,rtt_checks
#include "/sys/functionlist.h"
#include "/sys/lpctypes.h"
#include "/sys/object_info.h"
#include "/sys/interactive_info.h"
#include "/secure/master.h"
#include "/mail/post.h"
#include "/sys/thing/language.h"
#include "/sys/thing/description.h"
// Fuer CIDR-Notatio im sbanish
#include "/secure/master/cidr.c"
static mixed *banished;
static mapping tbanished, sbanished;
static string *deputies;
// TODO: muss ggf. Fakeobjekt erzeugen+uebergeben, wenn sender kein object
protected void send_channel_msg(string channel,mixed sendername,string msg)
{
object sender;
if (objectp(sendername))
sender=sendername;
else
{
// wenn kein Objekt uebergeben, erstmal schauen, ob ein Spielerobject
// existiert... Wenn ja, das nehmen.
sender = call_sefun("find_player", sendername)
|| call_sefun("find_netdead", sendername);
if (!objectp(sender))
{
// sonst faken wir eins. *seufz*
sender=clone_object("/p/daemon/namefake");
sender->SetProp(P_NAME, sendername);
sender->SetProp(P_ARTICLE,0);
// Dieses Objekt zerstoert sich nach 3s automatisch.
}
}
CHMASTER->send(channel, sender, msg);
}
static string *explode_files(string file) {
string data;
mixed *exploded;
int i;
data=read_file(file);
if (!data || !stringp(data) || data == "") return ({});
exploded = efun::explode(data,"\n");
for (i=sizeof(exploded);i--;)
if (!stringp(exploded[i]) || exploded[i]=="" || exploded[i][0]=='#')
exploded[i]=0;
exploded-=({0});
printf("%-30s: %3d Objekt%s\n",file,i=sizeof(exploded),(i==1?"":"e"));
return exploded;
}
void UpdateTBanish();
mixed QueryBanished(string str){
int i;
if (!str) return 0;
if (!pointerp(banished)) return 0;
for (i=sizeof(banished)-1;i>=0;i--)
if (sizeof(regexp(({str}),"^"+banished[i][0]+"$")))
{
if (!banished[i][1] || banished[i][1]=="")
return "Dieser Name ist gesperrt.";
else
return banished[i][1];
}
return 0;
}
mixed QueryTBanished(string str) {
int i;
if (!str || !mappingp(tbanished) || !(i=tbanished[str]))
return 0;
if (i == -1 || i > time())
return sprintf("Es gibt schon einen Spieler diesen Namens.\n"
+"Allerdings kann er/sie erst am %s wieder ins Mud kommen.\n",
(i == -1 ? "St. Nimmerleinstag" :
call_sefun("dtime",i)[0..16]));
// Ansonsten: die Zeit ist abgelaufen, Spieler darf wieder...
m_delete(tbanished, str);
UpdateTBanish();
return 0;
}
void ReloadBanishFile(){
int i, t;
string s1, s2, *s;
banished = efun::explode( read_file("/secure/BANISH") || "", "\n" );
banished = banished - ({""});
for ( i = sizeof(banished); i--; ){
s = efun::explode( banished[i], " " );
s1 = s[0];
s2 = implode( s[1..], " " );
banished[i] = ({ s1, s2 });
}
if ( !mappingp(tbanished) ){
tbanished = ([]);
s = efun::explode( read_file("/secure/TBANISH") || "", "\n" );
s -= ({""});
for ( i = sizeof(s); i--; ){
sscanf( s[i], "%s:%d", s1, t );
tbanished += ([ s1 : t ]);
}
}
if ( !mappingp(sbanished) ){
sbanished = m_allocate(3, 2);
s = efun::explode( read_file("/secure/SITEBANISH") || "", "\n" );
s -= ({""});
for ( i = sizeof(s); i--; ) {
int int_ip;
sscanf( s[i], "%s:%d:%s", s1, t, s2 );
int_ip = IPv4_addr2int(s1);
m_add(sbanished, int_ip, t, s2);
}
}
}
int IsDeputy(mixed name)
{
if ( IS_ARCH(name) )
return 1;
if ( objectp(name) )
name = getuid(name);
if ( member( deputies || ({}), name ) >= 0 )
return 1;
return 0;
}
varargs void BanishName( string name, string reason, int force )
{
string *names;
int i;
if ( PO != TO && call_sefun("secure_level") < LORD_LVL
&& !IsDeputy( call_sefun("secure_euid") ) )
return;
if ( !stringp(name) || !sizeof(name) )
return;
name = lower_case(name);
if ( !reason || !stringp(reason) )
reason = "";
if ( QueryBanished(name) && lower_case(reason) != "loeschen" ){
write("Der Name ist schon gebannt.\n");
return;
}
if ( !force && file_size(SAVEPATH+name[0..0]+"/"+name+".o") > 0 ){
write("Es existiert bereits ein Spieler dieses Namens.\n");
return;
}
/* if (!("/secure/login"->valid_name(name))) return;*/
if ( lower_case(reason) != "loeschen" ){
names = ({ name + " " + reason });
for ( i = sizeof(banished); i--; )
names += ({ banished[i][0] + " " + banished[i][1] });
}
else{
names = ({});
for ( i = sizeof(banished); i--; )
if ( banished[i][0] != name )
names += ({ banished[i][0] + " " + banished[i][1] });
}
names = sort_array( names, #'> );
rm("/secure/BANISH");
write_file( "/secure/BANISH", implode(names, "\n") );
write( "Okay, '"+capitalize(name)+"' ist jetzt "+
(lower_case(reason) == "loeschen" ? "nicht mehr " : "")+"gebanisht.\n" );
ReloadBanishFile();
}
void UpdateTBanish()
{
int i;
string *names;
for (i=sizeof(names = sort_array(m_indices(tbanished), #'</*'*/))-1;i>=0;i--)
names[i] = sprintf("%s:%d", names[i], tbanished[names[i]]);
rm("/secure/TBANISH");
write_file("/secure/TBANISH", implode(names, "\n"));
}
void UpdateSBanish()
{
int i;
mapping lines = m_allocate(sizeof(sbanished),0);
foreach(int ip, int tstamp, string user : sbanished) {
m_add(lines, sprintf("%s:%d:%s",
IPv4_int2addr(ip), tstamp, user));
}
write_file( "/secure/SITEBANISH",
implode( sort_array (m_indices(lines), #'<), "\n" ), 1);
}
int TBanishName(string name, int days)
{
int t;
if ( (getuid(TI) != name) &&
!IsDeputy( call_sefun("secure_euid") ) )
return 0;
if (days && QueryTBanished(name)){
write("Der Name ist schon gebannt.\n");
return 0;
}
if (file_size(SAVEPATH+name[0..0]+"/"+name+".o")<=0){
write("Es existiert kein Spieler dieses Namens!\n");
return 0;
}
if (!days && member(tbanished, name))
m_delete(tbanished, name);
else {
if (!mappingp(tbanished))
tbanished = ([]);
if (days <= -1)
t = -1;
else
t = (time()/86400)*86400 + days*86400;
tbanished += ([ name : t ]);
}
UpdateTBanish();
return 1;
}
mixed QuerySBanished( string ip )
{
int save_me, site;
string banished_meldung =
"\nSorry, von Deiner Adresse kamen ein paar Idioten, die "
"ausschliesslich\nAerger machen wollten. Deshalb haben wir "
"die Moeglichkeit,\neinfach neue Charaktere "
"anzulegen, kurzzeitig fuer diese Adresse gesperrt.\n\n"
"Falls Du bei uns spielen moechtest, schicke bitte eine Email "
"an\n\n mud@mg.mud.de\n\n"
"mit der Bitte, einen Charakter fuer Dich anzulegen.\n" ;
if ( !ip || !stringp(ip) || !mappingp(sbanished) || !sizeof(sbanished) )
return 0;
foreach(site, int tstamp: sbanished) {
if ( tstamp > 0 && tstamp < time() ) {
m_delete( sbanished, site );
save_me=1;
}
}
if (save_me)
UpdateSBanish();
if ( !sizeof(sbanished) )
return 0;
site = IPv4_addr2int(ip);
if (!site)
return 0;
// direkt drin?
if (member(sbanished, site))
return banished_meldung;
// oder Netz dieser IP gesperrt?
foreach(int locked_site : sbanished) {
if ((locked_site & site) == locked_site)
return banished_meldung;
}
return 0;
}
private int _sbanished_by( int key, string name )
{
return sbanished[key, 1] == name;
}
mixed SiteBanish( string ip, int days )
{
string *s, tmp, euid;
int t, level;
euid = call_sefun("secure_euid");
level = call_sefun("secure_level");
// Unter L26 gibt's gar nix.
if ( level <= DOMAINMEMBER_LVL )
return -1;
// Die Liste der gesperrten IPs anschauen darf jeder ab L26.
if ( !ip && !days )
return copy(sbanished);
if ( !stringp(ip) || !intp(days) )
return 0;
if ( days && QuerySBanished(ip) ){
write( "Diese Adresse ist schon gebannt.\n" );
return 0;
}
if ( !days ) {
int int_ip = IPv4_addr2int(ip);
if( member(sbanished, int_ip) ){
// Fremde Sitebanishs duerfen nur Deputies loeschen.
if ( sbanished[int_ip, 1] != euid && !IsDeputy(euid) )
return -1;
call_sefun("log_file", "ARCH/SBANISH",
sprintf( "%s: %s hebt die Sperrung der Adresse %s von %s "
+ "auf.\n",
ctime(time()), capitalize(euid), ip,
capitalize(sbanished[int_ip, 1]) ) );
m_delete( sbanished, int_ip );
}
else
return 0;
}
else {
// Alles, was nicht Deputy ist, darf nur fuer einen Tag sbanishen.
if ( days != 1 && !IsDeputy(euid) )
return -1;
// Nur Deputies duerfen mehr als 10 Sperrungen vornehmen.
if ( sizeof(filter_indices(sbanished, #'_sbanished_by, euid)) >= 10
&& !IsDeputy(euid) )
return -2;
int int_ip = IPv4_addr2int(ip);
if(!int_ip) {
write( "Ungueltige Adresse!\n" );
return 0;
}
// L26 duerfen exakt eine IP sperren, RMs ganze Class C-Subnetze
// und Deputies auch Class B-Subnetze.
int nsize=IPv4_net_size(ip);
if ( nsize > 1 && level < LORD_LVL
|| nsize > 255 && !IsDeputy(euid) )
return -1;
if ( !mappingp(sbanished) )
sbanished = m_allocate(1, 2);
if ( days < 0 )
t = -1;
else
t = (time() / 86400) * 86400 + days * 86400;
m_add(sbanished, int_ip, t, euid);
call_sefun("log_file", "ARCH/SBANISH",
sprintf( "%s: %s sperrt die Adresse %s %s.\n",
ctime(time()), capitalize(euid),
ip,
days > 0 ? (days > 1 ? "fuer " + days + " Tage"
: "fuer einen Tag")
: "bis zum St. Nimmerleinstag" ) );
}
UpdateSBanish();
return 1;
}
static void CheckDeputyRights()
{
object ob;
mixed *ginfo;
// Lese- und Schreibberechtigungen fuer die Rubrik 'polizei' setzen
call_other( "/secure/news", "???" );
ob = find_object("secure/news");
ginfo = ((mixed)ob->GetGroup("polizei"))[5..6];
ob->RemoveAllowed( "polizei", 0, ginfo[0], ginfo[1] );
ob->AddAllowed( "polizei", 0, deputies, deputies );
LoadDeputyFileList();
}
int ReloadDeputyFile()
{
deputies = efun::explode( read_file("/secure/DEPUTIES") || "", "\n" );
deputies -= ({""});
deputies = map( deputies, #'lower_case/*'*/ );
call_out( "CheckDeputyRights", 2 );
return(1);
}
static int create_home(string owner, int level)
{
string def_castle;
string dest, castle, wizard;
object player;
string *domains;
int i;
player = call_sefun("find_player",owner);
if (!player)
return -5;
domains=get_domain_homes(owner);
if (!sizeof(domains) && level >= DOMAINMEMBER_LVL)
{
tell_object(player,"Du gehoerst noch keiner Region an !\n");
return -6;
}
tell_object(player,"Du bist Mitarbeiter der folgenden Regionen:\n");
for (i=0;i<sizeof(domains);i++)
{
if (i) tell_object(player,", ");
tell_object(player,domains[i]);
}
tell_object(player,".\n");
update_wiz_level(owner, level);
wizard = "/players/" + owner;
castle = "/players/" + owner + "/workroom.c";
if (file_size(wizard) == -1) {
tell_object(player, "Verzeichnis " + wizard + " angelegt\n");
mkdir(wizard);
}
dest = object_name(environment(player));
def_castle = read_file("/std/def_workroom.c");
if (file_size(castle) > 0) {
tell_object(player, "Du HATTEST ja schon ein Arbeitszimmer !\n");
} else {
if (write_file(castle, def_castle))
{
tell_object(player, "Arbeitszimmer " + castle + " erzeugt.\n");
// Arbeitszimmer als Home setzen
player->SetProp(P_START_HOME,castle[0..<3]);
}
else
tell_object(player, "Arbeitszimmer konnte nicht erzeugt werden !\n");
}
return 1;
}
// Sendet dem befoerderten Magier eine Hilfemail zu.
protected void SendWizardHelpMail(string name, int level) {
object pl=call_sefun("find_player",name);
if (!objectp(pl)) return;
string file=sprintf("%sinfo_ml%d", WIZ_HELP_MAIL_DIR, level);
// wenn kein Hilfetext fuer den Level da ist: raus
if (file_size(file) <= 0)
return;
string subject = read_file(file,1,1);
string text = call_sefun("replace_personal",
read_file(file,2), ({pl}));
mixed mail = ({"Merlin", "<Master>", name, 0, 0, subject,
call_sefun("dtime",time()),
MUDNAME+time(), text });
MAILDEMON->DeliverMail(mail, 0);
}
int allowed_advance_wizlevel(mixed ob)
{
string what;
if (objectp(ob) && geteuid(ob)==ROOTID) return 1;
if (!stringp(ob))
what=efun::explode(object_name(ob),"#")[0];
else
what=ob;
if (what=="/secure/merlin") return 1;
return 0;
}
int advance_wizlevel(string name, int level)
{
int answer;
mixed *user;
if (!allowed_advance_wizlevel(PO))
return -1;
if (level>80) return -2;
if (!find_userinfo(name)) return -3;
user=get_full_userinfo(name);
if (user[USER_LEVEL+1]>level) return -4;
if (user[USER_LEVEL+1]==level) return 1;
if (level>=10 && level<20)
{
update_wiz_level(name, level);
SendWizardHelpMail(name, level);
return 1;
}
if (level>=20 && user[USER_LEVEL+1]<21)
{
answer = create_home(name, level);
if ( answer > 0 ) {
answer = update_wiz_level(name, level);
SendWizardHelpMail(name, level);
}
return answer;
}
update_wiz_level(name, level);
SendWizardHelpMail(name, level);
return 1;
}
void restart_heart_beat(object heart_beat)
{
if (heart_beat) heart_beat->_restart_beat();
}
int renew_player_object(mixed who)
{
object newob;
object *obs, *obs2;
mixed err;
string ob_name;
object *armours, weapon;
object tp;
int i,active,j;
if (stringp(who))
{
who=call_sefun("find_player",who);
if (!who)
{
who=call_sefun("find_netdead",who);
if (!who)
return -1;
}
}
if (!objectp(who))
return -2;
if (!object_info(who, OI_ONCE_INTERACTIVE))
return -3;
if (who->QueryGuest())
{
printf("Can't renew guests!\n");
return -6;
}
active=interactive(who);
printf("OK, renewing %O\n",who);
seteuid(geteuid(who));
err=catch(newob=clone_object(query_player_object(getuid(who))); publish);
seteuid(getuid(TO));
if (err)
{
printf("%O\n",err);
return -4;
}
if (!newob)
return -5;
/* Ok, the object is here now ... lets go for it ... */
who->save_me(0);
/* SSL ip weiterreichen */
if( call_sefun("query_ip_number", who) != efun::interactive_info(who,II_IP_NUMBER) )
{
newob->set_realip( call_sefun("query_ip_number",who) );
}
efun::configure_object(previous_object(), OC_COMMANDS_ENABLED, 0);
efun::set_this_player(0);
armours=(object *)who->QueryProp(P_ARMOURS);
weapon=(object)who->QueryProp(P_WEAPON);
if ( previous_object() && object_name(previous_object()) == "/secure/merlin" )
send_channel_msg("Debug",
previous_object(),
sprintf("RENEWING: %O %O\n",newob,who));
else
send_channel_msg("Entwicklung",
previous_object(),
sprintf("RENEWING: %O %O\n",newob,who));
ob_name=explode(object_name(newob),"#")[0];
if (sizeof(ob_name)>11 && ob_name[0..11]=="/std/shells/")
ob_name=ob_name[11..];
ob_name=ob_name+":"+getuid((object)who);
if (active)
exec(newob,who);
if (active && (interactive(who)||!interactive(newob)))
{
send_channel_msg("Debug",previous_object(),
"ERROR: still active !\n");
newob->remove();
return 0;
}
// newob->start_player(capitalize(getuid(who)),who->_query_my_ip());
funcall(
bind_lambda(
unbound_lambda( ({'x, 'y}),
({#'call_other/*'*/,
newob,
"start_player",
'x, 'y
})
), who
),
capitalize(getuid(who)), who->_query_my_ip() );
newob->move(environment(who),M_TPORT|M_NOCHECK|M_NO_SHOW|M_SILENT
|M_NO_ATTACK);
obs=all_inventory(who);
foreach(object tob: all_inventory(who)) {
if (!tob->QueryProp(P_AUTOLOADOBJ))
{
// kein Autoloader...
foreach(object ob: deep_inventory(tob))
{
// aber enthaltene Autoloader entsorgen...
if (ob->QueryProp(P_AUTOLOADOBJ))
{
catch(ob->remove();publish);
if (ob) destruct(ob);
}
}
// objekt ohne die AL bewegen.
catch(tob->move(newob,M_NOCHECK);publish);
}
else {
// Inhalt von Autoloadern retten.
// neue instanz des ALs im neuen Objekt.
object new_al_instance = present_clone(tob, newob);
foreach(object ob: deep_inventory(tob)) {
if (ob->QueryProp(P_AUTOLOADOBJ)) {
// autoloader in Autoloadern zerstoeren...
catch(ob->remove(1);publish);
if (ob) destruct(ob);
}
// alle nicht autoloader in die AL-Instanz im neuen Objekt oder
// notfalls ins Inv.
else {
if (objectp(new_al_instance))
catch(ob->move(new_al_instance, M_NOCHECK);publish);
else
catch(ob->move(newob, M_NOCHECK);publish);
}
}
// Autoloader zerstoeren. Wird nicht vom Spielerobjekt im remove()
// gemacht, wenn nicht NODROP.
catch(tob->remove(1);publish);
if (tob) destruct(tob);
}
}
who->remove();
if ( objectp(who) )
destruct(who);
rename_object(newob,ob_name);
newob->__reload_explore();
tp=this_player();
efun::set_this_player(newob);
if (objectp(weapon))
weapon->DoWield();
if (pointerp(armours))
for (i=sizeof(armours)-1;i>=0;i--)
if (objectp(armours[i]))
armours[i]->do_wear("alles");
efun::set_this_player(tp);
//Rueckgabewert noetig, weil Funktion vom Typ 'int'
return(1);
}
mixed __query_variable(object ob, string var)
{
if (!PO || !IS_ARCH(geteuid(PO)) || !this_interactive() ||
!IS_ARCH(this_interactive()) || getuid(ob)==ROOTID )
{
write("Du bist kein EM oder Gott!\n");
return 0;
}
if (efun::object_info(ob, OI_ONCE_INTERACTIVE) && (PO!=ob) &&
(var=="history" || var=="hist2"))
send_channel_msg("Snoop", previous_object(),
sprintf("%s -> %s (history).",
capitalize(getuid(PO)),capitalize(getuid(ob))));
call_sefun("log_file", "ARCH/QV",
sprintf("%s: %O inquires var %s in %O\n",
ctime(time()),this_interactive(),var,ob) );
mixed res = variable_list(ob, RETURN_FUNCTION_NAME|RETURN_FUNCTION_FLAGS|
RETURN_FUNCTION_TYPE|RETURN_VARIABLE_VALUE);
int index = member(res,var);
if (index > -1)
{
return ({res[index],res[index+1],res[index+2],res[index+3]});
}
return 0;
}
void RestartBeats()
{
int i,size,counter;
object ob;
mixed *list;
string file,obname,fun;
"/secure/simul_efun"->StopCallOut(0);
write("CALL_OUT-Restart in progress !\n");
filter(users(),#'tell_object,"CALL_OUT-Restart in progress !\n");
size=file_size("log/call_out_stop");
if (size<=0)
return;
file="";
counter=0;
while (counter<size)
{
file+=read_bytes("log/call_out_stop",counter,
(size-(counter+=40000)>0?counter:size));
}
list=explode(file,"__(CUT HERE)__\n");
list=list[<1..];
list=explode(list[0],"\n")-({""});
for (i=sizeof(list)-1;i>=0;i--)
if (sscanf(list[i],"%s \"%s\"",obname,fun)==2 && ob=find_object(obname))
{
write(sprintf("%O -> %s\n",ob,fun));
catch(ob->__restart(fun);publish);
}
write("CALL_OUT-Restart completed !\n");
filter(users(),#'tell_object,"CALL_OUT-Restart completed !\n");
rename("log/call_out_stop","log/call_out_stop.old");
}