blob: a831d4694e0934d74ccb76f72cb724bb640eb4e2 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// master/misc.c -- Diverses: (T)Banish, Projektverwaltung, Levelaufstieg, ...
4//
5// $Id: misc.c 9467 2016-02-19 19:48:24Z Zesstra $
6
7#pragma strict_types,rtt_checks
8
9#include "/sys/functionlist.h"
10#include "/sys/lpctypes.h"
11#include "/sys/object_info.h"
12#include "/sys/interactive_info.h"
13
14#include "/secure/master.h"
15#include "/mail/post.h"
16#include "/sys/thing/language.h"
17#include "/sys/thing/description.h"
18
19// Fuer CIDR-Notatio im sbanish
20#include "/secure/master/cidr.c"
21
22static mixed *banished;
23static mapping tbanished, sbanished;
24static string *deputies;
25
26// TODO: muss ggf. Fakeobjekt erzeugen+uebergeben, wenn sender kein object
27protected void send_channel_msg(string channel,mixed sendername,string msg)
28{
29 object sender;
30 if (objectp(sendername))
31 sender=sendername;
32 else
33 {
34 // wenn kein Objekt uebergeben, erstmal schauen, ob ein Spielerobject
35 // existiert... Wenn ja, das nehmen.
36 sender = call_sefun("find_player", sendername)
37 || call_sefun("find_netdead", sendername);
38 if (!objectp(sender))
39 {
40 // sonst faken wir eins. *seufz*
41 sender=clone_object("/p/daemon/namefake");
42 sender->SetProp(P_NAME, sendername);
43 sender->SetProp(P_ARTICLE,0);
44 // Dieses Objekt zerstoert sich nach 3s automatisch.
45 }
46 }
47 CHMASTER->send(channel, sender, msg);
48}
49
50static string *explode_files(string file) {
51 string data;
52 mixed *exploded;
53 int i;
54
55 data=read_file(file);
56 if (!data || !stringp(data) || data == "") return ({});
57 exploded = efun::explode(data,"\n");
58 for (i=sizeof(exploded);i--;)
59 if (!stringp(exploded[i]) || exploded[i]=="" || exploded[i][0]=='#')
60 exploded[i]=0;
61 exploded-=({0});
62 printf("%-30s: %3d Objekt%s\n",file,i=sizeof(exploded),(i==1?"":"e"));
63 return exploded;
64}
65
66void UpdateTBanish();
67
68mixed QueryBanished(string str){
69 int i;
70
71 if (!str) return 0;
72 if (!pointerp(banished)) return 0;
73 for (i=sizeof(banished)-1;i>=0;i--)
74 if (sizeof(regexp(({str}),"^"+banished[i][0]+"$")))
75 {
76 if (!banished[i][1] || banished[i][1]=="")
77 return "Dieser Name ist gesperrt.";
78 else
79 return banished[i][1];
80 }
81 return 0;
82}
83
84mixed QueryTBanished(string str) {
85 int i;
86
87 if (!str || !mappingp(tbanished) || !(i=tbanished[str]))
88 return 0;
89
90 if (i == -1 || i > time())
91 return sprintf("Es gibt schon einen Spieler diesen Namens.\n"
92 +"Allerdings kann er/sie erst am %s wieder ins Mud kommen.\n",
93 (i == -1 ? "St. Nimmerleinstag" :
94 call_sefun("dtime",i)[0..16]));
95
96// Ansonsten: die Zeit ist abgelaufen, Spieler darf wieder...
97 m_delete(tbanished, str);
98 UpdateTBanish();
99 return 0;
100}
101
102void ReloadBanishFile(){
103 int i, t;
104 string s1, s2, *s;
105
106 banished = efun::explode( read_file("/secure/BANISH") || "", "\n" );
107 banished = banished - ({""});
108 for ( i = sizeof(banished); i--; ){
109 s = efun::explode( banished[i], " " );
110 s1 = s[0];
111 s2 = implode( s[1..], " " );
112 banished[i] = ({ s1, s2 });
113 }
114
115 if ( !mappingp(tbanished) ){
116 tbanished = ([]);
117
118 s = efun::explode( read_file("/secure/TBANISH") || "", "\n" );
119 s -= ({""});
120
121 for ( i = sizeof(s); i--; ){
122 sscanf( s[i], "%s:%d", s1, t );
123 tbanished += ([ s1 : t ]);
124 }
125 }
126
127 if ( !mappingp(sbanished) ){
128 sbanished = m_allocate(3, 2);
129
130 s = efun::explode( read_file("/secure/SITEBANISH") || "", "\n" );
131 s -= ({""});
132
133 for ( i = sizeof(s); i--; ) {
134 int int_ip;
135 sscanf( s[i], "%s:%d:%s", s1, t, s2 );
136 int_ip = IPv4_addr2int(s1);
137 m_add(sbanished, int_ip, t, s2);
138 }
139 }
140}
141
142int IsDeputy(mixed name)
143{
144 if ( IS_ARCH(name) )
145 return 1;
146
147 if ( objectp(name) )
148 name = getuid(name);
149
150 if ( member( deputies || ({}), name ) >= 0 )
151 return 1;
152
153 return 0;
154}
155
156
157varargs void BanishName( string name, string reason, int force )
158{
159 string *names;
160 int i;
161
162 if ( PO != TO && call_sefun("secure_level") < LORD_LVL
163 && !IsDeputy( call_sefun("secure_euid") ) )
164 return;
165
166 if ( !stringp(name) || !sizeof(name) )
167 return;
168
169 name = lower_case(name);
170
171 if ( !reason || !stringp(reason) )
172 reason = "";
173
174 if ( QueryBanished(name) && lower_case(reason) != "loeschen" ){
175 write("Der Name ist schon gebannt.\n");
176 return;
177 }
178
Zesstra85576452017-01-30 15:43:21 +0100179 if ( !force && find_userinfo(name))
180 {
MG Mud User88f12472016-06-24 23:31:02 +0200181 write("Es existiert bereits ein Spieler dieses Namens.\n");
182 return;
183 }
184
185/* if (!("/secure/login"->valid_name(name))) return;*/
186 if ( lower_case(reason) != "loeschen" ){
187 names = ({ name + " " + reason });
188
189 for ( i = sizeof(banished); i--; )
190 names += ({ banished[i][0] + " " + banished[i][1] });
191 }
192 else{
193 names = ({});
194
195 for ( i = sizeof(banished); i--; )
196 if ( banished[i][0] != name )
197 names += ({ banished[i][0] + " " + banished[i][1] });
198 }
199
200 names = sort_array( names, #'> );
201 rm("/secure/BANISH");
202 write_file( "/secure/BANISH", implode(names, "\n") );
203 write( "Okay, '"+capitalize(name)+"' ist jetzt "+
204 (lower_case(reason) == "loeschen" ? "nicht mehr " : "")+"gebanisht.\n" );
205 ReloadBanishFile();
206}
207
208void UpdateTBanish()
209{
210 int i;
211 string *names;
212
213 for (i=sizeof(names = sort_array(m_indices(tbanished), #'</*'*/))-1;i>=0;i--)
214 names[i] = sprintf("%s:%d", names[i], tbanished[names[i]]);
215
216 rm("/secure/TBANISH");
217 write_file("/secure/TBANISH", implode(names, "\n"));
218}
219
220void UpdateSBanish()
221{
222 int i;
223 mapping lines = m_allocate(sizeof(sbanished),0);
224
225 foreach(int ip, int tstamp, string user : sbanished) {
226 m_add(lines, sprintf("%s:%d:%s",
227 IPv4_int2addr(ip), tstamp, user));
228 }
229
230 write_file( "/secure/SITEBANISH",
231 implode( sort_array (m_indices(lines), #'<), "\n" ), 1);
232}
233
234int TBanishName(string name, int days)
235{
236 int t;
237
238 if ( (getuid(TI) != name) &&
239 !IsDeputy( call_sefun("secure_euid") ) )
240 return 0;
241
242 if (days && QueryTBanished(name)){
243 write("Der Name ist schon gebannt.\n");
244 return 0;
245 }
246
Zesstra85576452017-01-30 15:43:21 +0100247 if (!find_userinfo(name))
248 {
MG Mud User88f12472016-06-24 23:31:02 +0200249 write("Es existiert kein Spieler dieses Namens!\n");
250 return 0;
251 }
252
253 if (!days && member(tbanished, name))
254 m_delete(tbanished, name);
255 else {
256 if (!mappingp(tbanished))
257 tbanished = ([]);
258 if (days <= -1)
259 t = -1;
260 else
261 t = (time()/86400)*86400 + days*86400;
262 tbanished += ([ name : t ]);
263 }
264
265 UpdateTBanish();
266 return 1;
267}
268
269
270mixed QuerySBanished( string ip )
271{
272 int save_me, site;
273 string banished_meldung =
274 "\nSorry, von Deiner Adresse kamen ein paar Idioten, die "
275 "ausschliesslich\nAerger machen wollten. Deshalb haben wir "
276 "die Moeglichkeit,\neinfach neue Charaktere "
277 "anzulegen, kurzzeitig fuer diese Adresse gesperrt.\n\n"
278 "Falls Du bei uns spielen moechtest, schicke bitte eine Email "
279 "an\n\n mud@mg.mud.de\n\n"
280 "mit der Bitte, einen Charakter fuer Dich anzulegen.\n" ;
281
282 if ( !ip || !stringp(ip) || !mappingp(sbanished) || !sizeof(sbanished) )
283 return 0;
284
285 foreach(site, int tstamp: sbanished) {
286 if ( tstamp > 0 && tstamp < time() ) {
287 m_delete( sbanished, site );
288 save_me=1;
289 }
290 }
291 if (save_me)
292 UpdateSBanish();
293
294 if ( !sizeof(sbanished) )
295 return 0;
296
297 site = IPv4_addr2int(ip);
298 if (!site)
299 return 0;
300 // direkt drin?
301 if (member(sbanished, site))
302 return banished_meldung;
303 // oder Netz dieser IP gesperrt?
304 foreach(int locked_site : sbanished) {
305 if ((locked_site & site) == locked_site)
306 return banished_meldung;
307 }
308
309 return 0;
310}
311
312
313private int _sbanished_by( int key, string name )
314{
315 return sbanished[key, 1] == name;
316}
317
318
319mixed SiteBanish( string ip, int days )
320{
321 string *s, tmp, euid;
322 int t, level;
323
324 euid = call_sefun("secure_euid");
325 level = call_sefun("secure_level");
326
327 // Unter L26 gibt's gar nix.
328 if ( level <= DOMAINMEMBER_LVL )
329 return -1;
330
331 // Die Liste der gesperrten IPs anschauen darf jeder ab L26.
332 if ( !ip && !days )
333 return copy(sbanished);
334
335
336 if ( !stringp(ip) || !intp(days) )
337 return 0;
338
339 if ( days && QuerySBanished(ip) ){
340 write( "Diese Adresse ist schon gebannt.\n" );
341 return 0;
342 }
343
344 if ( !days ) {
345 int int_ip = IPv4_addr2int(ip);
346
347 if( member(sbanished, int_ip) ){
348 // Fremde Sitebanishs duerfen nur Deputies loeschen.
349 if ( sbanished[int_ip, 1] != euid && !IsDeputy(euid) )
350 return -1;
351
352 call_sefun("log_file", "ARCH/SBANISH",
353 sprintf( "%s: %s hebt die Sperrung der Adresse %s von %s "
354 + "auf.\n",
355 ctime(time()), capitalize(euid), ip,
356 capitalize(sbanished[int_ip, 1]) ) );
357
358 m_delete( sbanished, int_ip );
359 }
360 else
361 return 0;
362 }
363 else {
364 // Alles, was nicht Deputy ist, darf nur fuer einen Tag sbanishen.
365 if ( days != 1 && !IsDeputy(euid) )
366 return -1;
367
368 // Nur Deputies duerfen mehr als 10 Sperrungen vornehmen.
369 if ( sizeof(filter_indices(sbanished, #'_sbanished_by, euid)) >= 10
370 && !IsDeputy(euid) )
371 return -2;
372
373 int int_ip = IPv4_addr2int(ip);
374
375 if(!int_ip) {
376 write( "Ungueltige Adresse!\n" );
377 return 0;
378 }
379
380 // L26 duerfen exakt eine IP sperren, RMs ganze Class C-Subnetze
381 // und Deputies auch Class B-Subnetze.
382 int nsize=IPv4_net_size(ip);
383 if ( nsize > 1 && level < LORD_LVL
384 || nsize > 255 && !IsDeputy(euid) )
385 return -1;
386
387 if ( !mappingp(sbanished) )
388 sbanished = m_allocate(1, 2);
389
390 if ( days < 0 )
391 t = -1;
392 else
393 t = (time() / 86400) * 86400 + days * 86400;
394
395 m_add(sbanished, int_ip, t, euid);
396
397 call_sefun("log_file", "ARCH/SBANISH",
398 sprintf( "%s: %s sperrt die Adresse %s %s.\n",
399 ctime(time()), capitalize(euid),
400 ip,
401 days > 0 ? (days > 1 ? "fuer " + days + " Tage"
402 : "fuer einen Tag")
403 : "bis zum St. Nimmerleinstag" ) );
404 }
405
406 UpdateSBanish();
407 return 1;
408}
409
410static void CheckDeputyRights()
411{
412 object ob;
413 mixed *ginfo;
414
415 // Lese- und Schreibberechtigungen fuer die Rubrik 'polizei' setzen
416 call_other( "/secure/news", "???" );
417 ob = find_object("secure/news");
418 ginfo = ((mixed)ob->GetGroup("polizei"))[5..6];
419 ob->RemoveAllowed( "polizei", 0, ginfo[0], ginfo[1] );
420 ob->AddAllowed( "polizei", 0, deputies, deputies );
421 LoadDeputyFileList();
422}
423
424int ReloadDeputyFile()
425{
426 deputies = efun::explode( read_file("/secure/DEPUTIES") || "", "\n" );
427 deputies -= ({""});
428 deputies = map( deputies, #'lower_case/*'*/ );
429 call_out( "CheckDeputyRights", 2 );
430 return(1);
431}
432
433
434static int create_home(string owner, int level)
435{
436 string def_castle;
437 string dest, castle, wizard;
438 object player;
439 string *domains;
440 int i;
441
442 player = call_sefun("find_player",owner);
443 if (!player)
444 return -5;
445 domains=get_domain_homes(owner);
446 if (!sizeof(domains) && level >= DOMAINMEMBER_LVL)
447 {
448 tell_object(player,"Du gehoerst noch keiner Region an !\n");
449 return -6;
450 }
451 tell_object(player,"Du bist Mitarbeiter der folgenden Regionen:\n");
452 for (i=0;i<sizeof(domains);i++)
453 {
454 if (i) tell_object(player,", ");
455 tell_object(player,domains[i]);
456 }
457 tell_object(player,".\n");
458 update_wiz_level(owner, level);
459 wizard = "/players/" + owner;
460 castle = "/players/" + owner + "/workroom.c";
461 if (file_size(wizard) == -1) {
462 tell_object(player, "Verzeichnis " + wizard + " angelegt\n");
463 mkdir(wizard);
464 }
465 dest = object_name(environment(player));
466 def_castle = read_file("/std/def_workroom.c");
467 if (file_size(castle) > 0) {
468 tell_object(player, "Du HATTEST ja schon ein Arbeitszimmer !\n");
469 } else {
470 if (write_file(castle, def_castle))
471 {
472 tell_object(player, "Arbeitszimmer " + castle + " erzeugt.\n");
473 // Arbeitszimmer als Home setzen
474 player->SetProp(P_START_HOME,castle[0..<3]);
475 }
476 else
477 tell_object(player, "Arbeitszimmer konnte nicht erzeugt werden !\n");
478 }
479 return 1;
480}
481
482// Sendet dem befoerderten Magier eine Hilfemail zu.
483protected void SendWizardHelpMail(string name, int level) {
484
485 object pl=call_sefun("find_player",name);
486 if (!objectp(pl)) return;
487
488 string file=sprintf("%sinfo_ml%d", WIZ_HELP_MAIL_DIR, level);
489 // wenn kein Hilfetext fuer den Level da ist: raus
490 if (file_size(file) <= 0)
491 return;
492
493 string subject = read_file(file,1,1);
494 string text = call_sefun("replace_personal",
495 read_file(file,2), ({pl}));
496
497 mixed mail = ({"Merlin", "<Master>", name, 0, 0, subject,
498 call_sefun("dtime",time()),
499 MUDNAME+time(), text });
500 MAILDEMON->DeliverMail(mail, 0);
501}
502
503int allowed_advance_wizlevel(mixed ob)
504{
505 string what;
506
507 if (objectp(ob) && geteuid(ob)==ROOTID) return 1;
508
509 if (!stringp(ob))
510 what=efun::explode(object_name(ob),"#")[0];
511 else
512 what=ob;
513
514 if (what=="/secure/merlin") return 1;
515
516 return 0;
517}
518
519int advance_wizlevel(string name, int level)
520{
521 int answer;
522 mixed *user;
523
524 if (!allowed_advance_wizlevel(PO))
525 return -1;
526
527 if (level>80) return -2;
528
529 if (!find_userinfo(name)) return -3;
530
531 user=get_full_userinfo(name);
532
533 if (user[USER_LEVEL+1]>level) return -4;
534
535 if (user[USER_LEVEL+1]==level) return 1;
536
537 if (level>=10 && level<20)
538 {
539 update_wiz_level(name, level);
540 SendWizardHelpMail(name, level);
541 return 1;
542 }
543 if (level>=20 && user[USER_LEVEL+1]<21)
544 {
545 answer = create_home(name, level);
546 if ( answer > 0 ) {
547 answer = update_wiz_level(name, level);
548 SendWizardHelpMail(name, level);
549 }
550 return answer;
551 }
552
553 update_wiz_level(name, level);
554 SendWizardHelpMail(name, level);
555
556 return 1;
557}
558
559void restart_heart_beat(object heart_beat)
560{
561 if (heart_beat) heart_beat->_restart_beat();
562}
563
564int renew_player_object(mixed who)
565{
566 object newob;
567 object *obs, *obs2;
568 mixed err;
569 string ob_name;
570 object *armours, weapon;
571 object tp;
572 int i,active,j;
573
574 if (stringp(who))
575 {
576 who=call_sefun("find_player",who);
577 if (!who)
578 {
579 who=call_sefun("find_netdead",who);
580 if (!who)
581 return -1;
582 }
583 }
584 if (!objectp(who))
585 return -2;
586 if (!object_info(who, OI_ONCE_INTERACTIVE))
587 return -3;
588 if (who->QueryGuest())
589 {
590 printf("Can't renew guests!\n");
591 return -6;
592 }
593 active=interactive(who);
594 printf("OK, renewing %O\n",who);
595 seteuid(geteuid(who));
596 err=catch(newob=clone_object(query_player_object(getuid(who))); publish);
597 seteuid(getuid(TO));
598 if (err)
599 {
600 printf("%O\n",err);
601 return -4;
602 }
603 if (!newob)
604 return -5;
605 /* Ok, the object is here now ... lets go for it ... */
606 who->save_me(0);
607 /* SSL ip weiterreichen */
608 if( call_sefun("query_ip_number", who) != efun::interactive_info(who,II_IP_NUMBER) )
609 {
610 newob->set_realip( call_sefun("query_ip_number",who) );
611 }
zesstraf06f57a2016-07-06 20:36:05 +0200612 efun::configure_object(who, OC_COMMANDS_ENABLED, 0);
MG Mud User88f12472016-06-24 23:31:02 +0200613 efun::set_this_player(0);
614 armours=(object *)who->QueryProp(P_ARMOURS);
615 weapon=(object)who->QueryProp(P_WEAPON);
616
617 if ( previous_object() && object_name(previous_object()) == "/secure/merlin" )
618 send_channel_msg("Debug",
619 previous_object(),
620 sprintf("RENEWING: %O %O\n",newob,who));
621 else
622 send_channel_msg("Entwicklung",
623 previous_object(),
624 sprintf("RENEWING: %O %O\n",newob,who));
625
626 ob_name=explode(object_name(newob),"#")[0];
627 if (sizeof(ob_name)>11 && ob_name[0..11]=="/std/shells/")
628 ob_name=ob_name[11..];
629 ob_name=ob_name+":"+getuid((object)who);
630 if (active)
631 exec(newob,who);
632 if (active && (interactive(who)||!interactive(newob)))
633 {
634 send_channel_msg("Debug",previous_object(),
635 "ERROR: still active !\n");
636 newob->remove();
637 return 0;
638 }
639// newob->start_player(capitalize(getuid(who)),who->_query_my_ip());
640 funcall(
641 bind_lambda(
642 unbound_lambda( ({'x, 'y}),
643 ({#'call_other/*'*/,
644 newob,
645 "start_player",
646 'x, 'y
647 })
648 ), who
649 ),
650 capitalize(getuid(who)), who->_query_my_ip() );
651
652 newob->move(environment(who),M_TPORT|M_NOCHECK|M_NO_SHOW|M_SILENT
653 |M_NO_ATTACK);
654 obs=all_inventory(who);
655 foreach(object tob: all_inventory(who)) {
656 if (!tob->QueryProp(P_AUTOLOADOBJ))
657 {
658 // kein Autoloader...
659 foreach(object ob: deep_inventory(tob))
660 {
661 // aber enthaltene Autoloader entsorgen...
662 if (ob->QueryProp(P_AUTOLOADOBJ))
663 {
664 catch(ob->remove();publish);
665 if (ob) destruct(ob);
666 }
667 }
668 // objekt ohne die AL bewegen.
669 catch(tob->move(newob,M_NOCHECK);publish);
670 }
671 else {
672 // Inhalt von Autoloadern retten.
673 // neue instanz des ALs im neuen Objekt.
674 object new_al_instance = present_clone(tob, newob);
675 foreach(object ob: deep_inventory(tob)) {
676 if (ob->QueryProp(P_AUTOLOADOBJ)) {
677 // autoloader in Autoloadern zerstoeren...
678 catch(ob->remove(1);publish);
679 if (ob) destruct(ob);
680 }
681 // alle nicht autoloader in die AL-Instanz im neuen Objekt oder
682 // notfalls ins Inv.
683 else {
684 if (objectp(new_al_instance))
685 catch(ob->move(new_al_instance, M_NOCHECK);publish);
686 else
687 catch(ob->move(newob, M_NOCHECK);publish);
688 }
689 }
690 // Autoloader zerstoeren. Wird nicht vom Spielerobjekt im remove()
691 // gemacht, wenn nicht NODROP.
692 catch(tob->remove(1);publish);
693 if (tob) destruct(tob);
694 }
695 }
696 who->remove();
697 if ( objectp(who) )
698 destruct(who);
699 rename_object(newob,ob_name);
700 newob->__reload_explore();
701 tp=this_player();
702 efun::set_this_player(newob);
703 if (objectp(weapon))
704 weapon->DoWield();
705 if (pointerp(armours))
706 for (i=sizeof(armours)-1;i>=0;i--)
707 if (objectp(armours[i]))
708 armours[i]->do_wear("alles");
709 efun::set_this_player(tp);
710 //Rueckgabewert noetig, weil Funktion vom Typ 'int'
711 return(1);
712}
713
714mixed __query_variable(object ob, string var)
715{
716 if (!PO || !IS_ARCH(geteuid(PO)) || !this_interactive() ||
717 !IS_ARCH(this_interactive()) || getuid(ob)==ROOTID )
718 {
719 write("Du bist kein EM oder Gott!\n");
720 return 0;
721 }
722 if (efun::object_info(ob, OI_ONCE_INTERACTIVE) && (PO!=ob) &&
723 (var=="history" || var=="hist2"))
724 send_channel_msg("Snoop", previous_object(),
725 sprintf("%s -> %s (history).",
726 capitalize(getuid(PO)),capitalize(getuid(ob))));
727
728 call_sefun("log_file", "ARCH/QV",
729 sprintf("%s: %O inquires var %s in %O\n",
730 ctime(time()),this_interactive(),var,ob) );
731 mixed res = variable_list(ob, RETURN_FUNCTION_NAME|RETURN_FUNCTION_FLAGS|
732 RETURN_FUNCTION_TYPE|RETURN_VARIABLE_VALUE);
733 int index = member(res,var);
734 if (index > -1)
735 {
736 return ({res[index],res[index+1],res[index+2],res[index+3]});
737 }
738 return 0;
739}
740
741void RestartBeats()
742{
743 int i,size,counter;
744 object ob;
745 mixed *list;
746 string file,obname,fun;
747
748 "/secure/simul_efun"->StopCallOut(0);
749 write("CALL_OUT-Restart in progress !\n");
750 filter(users(),#'tell_object,"CALL_OUT-Restart in progress !\n");
751 size=file_size("log/call_out_stop");
752 if (size<=0)
753 return;
754 file="";
755 counter=0;
756 while (counter<size)
757 {
758 file+=read_bytes("log/call_out_stop",counter,
759 (size-(counter+=40000)>0?counter:size));
760 }
761 list=explode(file,"__(CUT HERE)__\n");
762 list=list[<1..];
763 list=explode(list[0],"\n")-({""});
764 for (i=sizeof(list)-1;i>=0;i--)
765 if (sscanf(list[i],"%s \"%s\"",obname,fun)==2 && ob=find_object(obname))
766 {
767 write(sprintf("%O -> %s\n",ob,fun));
768 catch(ob->__restart(fun);publish);
769 }
770 write("CALL_OUT-Restart completed !\n");
771 filter(users(),#'tell_object,"CALL_OUT-Restart completed !\n");
772 rename("log/call_out_stop","log/call_out_stop.old");
773}
774