Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/secure/master/misc.c b/secure/master/misc.c
new file mode 100644
index 0000000..02b0fcc
--- /dev/null
+++ b/secure/master/misc.c
@@ -0,0 +1,772 @@
+// 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");
+}
+