object $sys: $root;
var $root created_on = 796268969;
var $root defined_settings = #[["loggers", #[['get, ['loggers]], ['set, ['set_loggers]], ['parse, ['parse_itemlist, 'parse_object]], ['format, ['format_itemlist]]]], ["deny-hosts", #[['get, ['deny_hosts]], ['set, ['set_deny_hosts]], ['parse, ['parse_itemlist, 'parse_deny_host]], ['format, ['format_itemlist]]]], ["deny-users", #[['get, ['deny_users]], ['set, ['set_deny_users]], ['parse, ['parse_itemlist, 'parse_deny_user]], ['format, ['format_itemlist]]]], ["backup-interval", #[['get, ['get_backup_interval]], ['set, ['set_backup_interval]], ['parse, ['parse_backup_interval]], ['format, ['fmt_backup_interval]]]], ["validate-email-addresses", #[['parse, ['is_boolean]], ['format, ['format_boolean]], ['get, ['validate_email_addresses]], ['set, ['set_validate_email_addresses]]]], ["initial-quota", #[['get, ['get_initial_quota]], ['set, ['set_initial_quota]], ['parse, ['parse_initial_quota]], ['format, ['fmt_initial_quota]]]], ["new-user-class", #[['get, ['get_new_user_class]], ['set, ['set_new_user_class]], ['parse, ['parse_new_user_class]], ['format, ['format_object]]]], ["anonymous-user-class", #[['get, ['get_anon_user_class]], ['set, ['set_anon_user_class]], ['parse, ['parse_anon_user_class]], ['format, ['format_object]]]], ["startup-objects", #[['get, ['get_startup_objects]], ['set, ['set_startup_objects]], ['parse, ['parse_itemlist, 'parse_startup_object]], ['format, ['format_itemlist]]]], ["writable-core", #[['get, ['writable_core]], ['set, ['set_writable_core]], ['parse, ['is_boolean]], ['format, ['format_boolean]]]]];
var $root flags = ['methods, 'code, 'core, 'variables];
var $root inited = 1;
var $root managed = [$sys];
var $root manager = $sys;
var $root quota_exempt = 1;
var $sys admins = [];
var $sys agents = [$root, $daemon];
var $sys backup = #[['interval, 3600], ['last, 0], ['next, 0]];
var $sys bindings = #[['atomic, $sys], ['create, $sys], ['backup, $sys], ['shutdown, $sys], ['set_heartbeat, $sys], ['cancel, $scheduler], ['task_info, $scheduler], ['execute, $sys], ['bind_function, $sys], ['unbind_function, $sys], ['bind_port, $daemon], ['unbind_port, $daemon], ['open_connection, $connection], ['reassign_connection, $daemon], ['fopen, $file], ['fstat, $file], ['fchmod, $file], ['fmkdir, $file], ['frmdir, $file], ['files, $file], ['fremove, $file], ['frename, $file], ['fclose, $file], ['fseek, $file], ['feof, $file], ['fwrite, $file], ['fread, $file], ['fflush, $file], ['chparents, $root], ['destroy, $root], ['dblog, $sys], ['add_var, $root], ['del_var, $root], ['variables, $root], ['list_method, $root], ['add_method, $root], ['del_method, $root], ['method_bytecode, $root], ['methods, $root], ['rename_method, $root], ['set_method_access, $root], ['set_method_flags, $root], ['data, $root], ['del_objname, $root], ['set_objname, $root], ['suspend, $scheduler], ['resume, $scheduler], ['set_user, $user], ['config, $sys]];
var $sys core_version = "3.0a9.02";
var $sys deny_hosts = [];
var $sys deny_users = [];
var $sys loggers = [$daemon, $user, $connection, $dns];
var $sys starting = #[['quota, 76800], ['new_user_class, $admin], ['anonymous_user_class, $guest]];
var $sys startup = #[['objects, [$login_daemon, $http_daemon, $smtp_daemon, $world, $dns, $lag_watcher]], ['heartbeat_interval, 2]];
var $sys system = [$sys, $root];
var $sys touched = 0;
var $sys validate_email_addresses = 0;
var $sys writable_core = 0;
private method ._loop_for_core() {
arg code;
var obj;
$root.add_method(code, '___coretmp);
for obj in ($root.descendants()) {
obj.___coretmp();
pause();
}
$root.del_method('___coretmp);
};
public method ._status(): native;
public method .add_method() {
arg code, name, @evalonly;
var line;
(> .perms(sender()) <);
line = (("SYSTEM: ." + tostr(name)) + "() ") + (evalonly ? "EVAL" : "MODIFIED");
line = (line + " by ") + (sender().namef('ref));
.log(line);
return (> pass(code, name, @evalonly) <);
};
public method .add_to_system() {
arg obj;
if (!(.is_admin(sender())))
throw(~perm, "Sender is not an admin.");
if (!((obj in admins) || (obj in agents)))
throw(~perm, "Object is not an agent or admin.");
system = system.union([obj]);
};
public method .admins() {
return admins;
};
public method .agents() {
return agents;
};
public method .atomic() {
arg @args;
(> .perms(sender(), 'system) <);
return (> atomic(@args) <);
};
public method .backup() {
return backup;
};
driver method .backup_done() {
var elapsed;
elapsed = time() - (backup['started]);
backup = backup.del('started);
catch any {
$channel_ui._broadcast('System, "Backup completed, elapsed time " + ($time.elapsed(elapsed, 'long)));
$channel_ui._broadcast('System, "Executing filesystem cleanup.. ");
pause();
pause();
.execute("backup", []);
}
};
private method .clean_database() {
var obj, p, c, cmd, other;
// cleanup some of $root's messiness
for obj in ($root.descendants()) {
(| obj.clean_root() |);
refresh();
}
other = (((($command_cache.children()).setremove($user_interfaces)).mmap('descendants)).flatten()).compress();
// purge all command caches
for obj in ($has_commands.descendants()) {
(| obj.purge_caches() |);
refresh();
}
// check user info (move'em home etc)
for obj in ($user.descendants()) {
if ((| (obj.home()) != (obj.location()) |))
(| obj.move_to(obj.home()) |);
refresh();
}
// validate all locations and location content's
for obj in ($physical.descendants()) {
(| obj.validate_contents() |);
if (obj.has_ancestor($located)) {
if ((!valid(obj.location())) || (!(obj in ((obj.location()).contents()))))
obj.move_to((| obj.home() |) || $lost_and_found);
}
refresh();
}
};
root method .clear_definer_vars(): nooverride {
arg definer, objs;
var code, v, a, meth, errs, d;
code = [];
for v in (definer.variables())
code += [("(| clear_var('" + v) + ") |);"];
meth = tosym("_root_tmp_" + time());
if (!(definer.add_method(code, meth))) {
for d in (objs) {
pause();
(| d.(meth)() |);
}
(| definer.del_method(meth) |);
}
};
root method .core_sys(): nooverride {
deny_hosts = (deny_users = []);
touched = 0;
system = [$sys, $root];
backup = #[['interval, 3600], ['last, 0], ['next, 0]];
validate_email_addresses = 0;
starting = #[['quota, 76800], ['new_user_class, $admin], ['anonymous_user_class, $guest]];
startup = #[['objects, [$login_daemon, $http_daemon, $smtp_daemon, $world, $dns, $lag_watcher]], ['heartbeat_interval, 2]];
agents = [$root, $daemon];
admins = [];
writable_core = 0;
};
private method .create() {
arg parents, name, manager;
var new;
new = create(parents);
catch any {
new.set_objname(name);
new.initialize();
new.change_manager(manager);
} with {
// Failed to initialize the child; destroy it.
(| new.destroy() |);
rethrow(error());
}
return new;
};
public method .create_user() {
arg name, password, email, @type;
var user;
if ((!(| .perms(caller(), $login_interface) |)) && (!(| .perms(sender(), 'system) |)))
throw(~perm, "Caller and Sender are not allowed to call this method.");
[(type ?= 'new_user_class)] = type;
catch any {
user = (starting[type]).spawn(name);
user.set_name(name);
if (type == 'new_user_class)
user.set_password(password);
user.change_manager(user);
user.set_user_info("rl-email", $user_info, email);
} with {
// Failed to initialize the child; destroy it.
(| user.destroy() |);
rethrow(error());
}
return user;
};
public method .del_from_system() {
arg obj;
if (!(.is_admin(sender())))
throw(~perm, "Sender is not an admin.");
if (!((obj in admins) || (obj in agents)))
throw(~perm, "Object is not an agent or admin.");
system = system.setremove(obj);
};
public method .deny_hosts() {
arg @args;
return deny_hosts;
};
public method .deny_users() {
arg @args;
return deny_users;
};
public method .destroy_sender() {
// potential problem spot, but sometimes its needed
// add core definer items to the list, if you want them to call it
(> .perms(caller(), $exit, $connection, $connection_interface) <);
(> sender().destroy() <);
};
public method .dirty() {
return touched > (backup['last]);
};
public method .do_backup() {
arg @args;
var line, who, how, name, dirty;
(> .perms(sender(), 'system) <);
dirty = .dirty();
[(who ?= sender()), (how ?= 'request)] = args;
if (!valid(who))
who = sender();
backup = backup.add('next, time() + (backup['interval]));
backup = backup.add('last, time());
if ((how == 'interval) && (!dirty))
return;
catch any {
name = who.namef('ref);
.log(("BACKUP (" + name) + ") ");
line = (("Backup started at " + ($time.format("%r"))) + " by ") + name;
$channel_ui._broadcast('System, line);
}
// double pause will hopefully let people know about it before it happens
pause();
pause();
backup = backup.add('started, time());
(> backup() <);
};
public method .do_shutdown() {
arg time, why;
var increments, line, name, mins;
if ((!($sys.is_admin(sender()))) || (definer() != this()))
throw(~perm, "Sender is not an admin.");
increments = [600, 300, 180, 60];
while (increments && (time < (increments[1])))
increments = increments.delete(1);
name = sender().namef('xref);
.log(("*** SHUTDOWN called by " + name) + " ***");
if (why) {
why = ("*** REASON: " + why) + " ***";
.log(why);
}
while (1) {
if (!increments) {
$channel_ui._broadcast('all, "*** SYSTEM SHUTDOWN ***");
if (why)
$channel_ui._broadcast('all, why);
break;
}
line = "*** SYSTEM SHUTDOWN IN ";
mins = (increments[1]) / 60;
line = ((line + tostr(mins)) + " MINUTE") + ((mins == 1) ? "" : "S");
line = ((line + " CALLED BY ") + name) + " ***";
$channel_ui._broadcast('all, line);
if (why)
$channel_ui._broadcast('all, why);
$scheduler.sleep(increments[1]);
increments = increments.delete(1);
}
pause();
pause();
return .shutdown();
};
public method .execute() {
arg script, args, @background;
(> .perms(sender(), 'system) <);
(> execute(script, args, @background) <);
};
private method .finish_core() {
// cleanup heartbeat
set_heartbeat(0);
.add_method(TMP_HEARTBEAT_CODE, 'heartbeat);
.del_var('TMP_HEARTBEAT_CODE);
// ok, finish up
catch any {
dblog("** corifying remaining objects");
$root.corify();
dblog("** cleaning database..");
.clean_database();
dblog("** shutting down..");
shutdown();
} with {
dblog("traceback: " + traceback());
}
};
public method .fmt_backup_interval() {
arg data;
return $time.to_english(data);
};
public method .fmt_initial_quota() {
arg data;
return data.to_bytes();
};
public method .get_anon_user_class() {
arg @args;
return starting['anonymous_user_class];
};
public method .get_backup_interval() {
arg @args;
return backup['interval];
};
public method .get_initial_quota() {
arg @args;
return starting['quota];
};
public method .get_new_user_class() {
arg @args;
return starting['new_user_class];
};
public method .get_starting() {
arg what;
return starting[what];
};
public method .get_startup() {
arg what;
return startup[what];
};
public method .get_startup_objects() {
arg @args;
return startup['objects];
};
driver method .heartbeat() {
if (sender() != 0)
throw(~perm, "Sender is not the server.");
(| $scheduler.pulse() |);
if (time() > (backup['next]))
.do_backup(this(), 'interval);
};
public method .host_denied() {
arg remote;
var t;
for t in (deny_hosts) {
if (match_begin(remote, t) != 0)
return 1;
}
return 0;
};
private method .init_database() {
var obj, p, c, cmd, other;
// get back caches
other = (((($command_cache.children()).setremove($user_interfaces)).mmap('descendants)).flatten()).compress();
for obj in (other) {
(| obj.rehash_caches() |);
refresh();
}
// create location caches
for obj in ((| $location.descendants() |) || []) {
for p in (obj.contents()) {
(| obj.add_object_to_remote_cache(p) |);
refresh();
}
refresh();
}
};
private method .initialize_core() {
var obj;
(| .clean_database() |);
// reset child indices
._loop_for_core(["child_index = 0;"]);
};
public method .is_admin() {
arg obj;
return (obj == $sys) || (obj in admins);
};
public method .is_system() {
arg obj;
return obj in system;
};
public method .log() {
arg text;
var l;
if ((!sender()) in loggers) {
if ((!(| .perms(sender(), 'system) |)) && (!(| .perms(caller(), 'system) |)))
throw(~perm, "Only system objects can log.");
}
if (type(text) == 'list) {
for l in (text)
.log(l);
} else {
dblog((("[" + ($time.format("%d %h %y %H:%M"))) + "] ") + text);
}
};
public method .loggers() {
arg @args;
return loggers;
};
private method .make_core() {
arg ver;
var obj, d, o, top, x, admin, tmp, name;
// core rooms should be +core, and cant be destroyed
// traverse the list inverseley, less unseen heirarchial shuffling
dblog("** Starting Core Extraction " + ctime());
d = $root.descendants();
top = listlen(d);
core_version = ver;
dblog(("** " + top) + " objects total, tidying up core objects...");
// tidy up core objects first
for obj in (d) {
refresh();
if (obj.has_flag('core)) {
tmp++;
catch any {
obj.change_manager(obj);
for o in (obj.writers('literal)) {
refresh();
obj.del_writer(o);
}
} with {
dblog(("** TIDY " + obj) + " traceback: ");
for o in ($parse_lib.traceback(traceback()))
dblog("** " + o);
}
}
}
// nuke anything not core
dblog(("** " + tmp) + " core objects, destroying non-core objects...");
for x in [1 .. top] {
refresh();
obj = d[(top - x) + 1];
if (!valid(obj)) {
// dblog(".. Skipping invalid object " + obj);
continue;
}
if (!(obj.has_flag('core))) {
if (obj.children()) {
dblog("** ABORTING: A NON CORE OBJECT HAS CORE CHILDREN: ");
dblog((("** " + obj) + " => ") + (obj.children()));
shutdown();
}
catch any {
// dblog(".. Destroying " + obj);
obj.destroy();
} with {
dblog(("** " + obj) + ".destroy() traceback: ");
for o in ($parse_lib.traceback(traceback()))
dblog("** " + o);
}
}
}
// shutdown this task so that references can have
// a chance to clear out, on destroyed objects.
dblog("** done, suspending for new task **");
// do this the hard way, to be secure
set_heartbeat(1);
.add_var('TMP_HEARTBEAT_CODE, .list_method('heartbeat));
.add_method([".finish_core();"], 'heartbeat);
};
public method .new_admin() {
if (caller() != $admin)
throw(~perm, "Caller is not $admin.");
admins = setadd(admins, sender());
};
public method .next_objnum(): native;
public method .old_admin() {
if (caller() != $admin)
throw(~perm, "Caller is not $admin.");
admins = setremove(admins, sender());
system = setremove(system, sender());
};
public method .parse_anon_user_class() {
arg value, @args;
value = (> $object_lib.to_dbref(value) <);
if (!(value.is($user)))
throw(~perm, (value.namef('ref)) + " is not a $user object.");
if (!(value.has_flag('command_cache)))
throw(~perm, (value.namef('ref)) + " is not a command cache object.");
return value;
};
public method .parse_backup_interval() {
arg value, @args;
value = (> $time.from_english(value) <);
if (value < 300)
throw(~perm, "You cannot set your backup interval to less than 5 minutes");
return value;
};
public method .parse_deny_host() {
arg value, @args;
var parts;
// DNS Hostnames are not used due to the extreme slowdown it would
// cause to _ALL_ services
if (match_regexp(value, "[^0-9.]"))
throw(~type, "Invalid Internet Host IP address or subnet: " + value);
parts = explode(value, ".");
if (listlen(parts) < 2)
throw(~type, "Do you really want to deny an entire class A network?");
if (listlen(parts) > 4)
throw(~type, "Invalid IP Address: " + (parts.join(".")));
return parts.join(".");
};
public method .parse_deny_user() {
arg value, @args;
var obj;
if (value && ((value[1]) == "$")) {
obj = (> $object_lib.to_dbref(value) <);
if (!(obj.is($user)))
throw(~perm, (obj.namef('ref)) + " is not a user.");
} else {
obj = (> $user_db.search(value) <);
}
return obj;
};
public method .parse_initial_quota() {
arg value, @args;
value = (> value.to_bytes() <);
if (value < 1024)
throw(~perm, "You cannot set your initial quota to less than 1 kb");
return value;
};
public method .parse_new_user_class() {
arg value, @args;
value = (> $object_lib.to_dbref(value) <);
if (!(value.is($user)))
throw(~perm, (value.namef('ref)) + " is not a $user object.");
if (!(value.has_flag('command_cache)))
throw(~perm, (value.namef('ref)) + " is not a command cache object.");
return value;
};
public method .parse_startup_object() {
arg value, action, @args;
value = (> $object_lib.to_dbref(value) <);
if (action == 'del) {
if (!(value in (startup['objects])))
throw(~failed, ("The object '" + value) + "' is not set, and thus cannot be removed");
return value;
}
return value;
};
root method .sender_going_away() {
admins = admins.setremove(sender());
agents = agents.setremove(sender());
system = system.setremove(sender());
};
public method .server_info() {
arg what, @long;
var tmp;
switch (what) {
case 'up_time:
return time() - (startup['time]);
case 'startup_time:
return startup['time];
case 'server_hostname:
return $dns.hostname("");
case 'server_ip:
return $dns.ip("");
case 'last_backup:
return backup['last];
case 'driver_version:
tmp = .version();
return (((((((long ? "Cold Genesis " : "") + (tmp[1])) + ".") + (tmp[2])) + "p") + (tmp[3])) + "-") + ((listlen(tmp) == 3) ? "NEED TO UPGRADE" : (tmp[4]));
case 'core_version:
return (long ? "ColdCore " : "") + core_version;
default:
throw(~unknown, "Unknown flag.");
}
};
protected method .set_anon_user_class() {
arg name, definer, value;
starting = starting.add('anon_user_class, value);
};
protected method .set_backup_interval() {
arg name, definer, value;
backup = backup.add('interval, value);
};
protected method .set_deny_hosts() {
arg name, definer, value;
switch (value[1]) {
case 'set:
deny_hosts = value[2];
case 'add:
deny_hosts = setadd(deny_hosts, value[2]);
case 'del:
deny_hosts = setremove(deny_hosts, value[2]);
default:
throw(~type, "Unknown action: " + (value[1]));
}
};
protected method .set_deny_users() {
arg name, definer, value;
switch (value[1]) {
case 'set:
deny_users = value[2];
case 'add:
deny_users = setadd(deny_users, value[2]);
case 'del:
deny_users = setremove(deny_users, value[2]);
default:
throw(~type, "Unknown action: " + (value[1]));
}
};
protected method .set_initial_quota() {
arg name, definer, value;
starting = starting.add('quota, value);
};
protected method .set_loggers() {
arg name, definer, value;
switch (value[1]) {
case 'set:
loggers = value[2];
case 'add:
loggers = setadd(loggers, value[2]);
case 'del:
loggers = setremove(loggers, value[2]);
default:
throw(~type, "Unknown action: " + (value[1]));
}
};
protected method .set_new_user_class() {
arg name, definer, value;
starting = starting.add('new_user_class, value);
};
public method .set_starting() {
arg what, value;
var valid;
(> .perms(sender(), 'system) <);
valid = starting.keys();
if ((!what) in valid)
throw(~type, "Key must be one of " + toliteral(valid));
starting = starting.add(what, value);
};
public method .set_startup() {
arg what, value;
var valid;
(> .perms(sender(), 'system) <);
valid = startup.keys();
if ((!what) in valid)
throw(~type, "Key must be one of " + toliteral(valid));
startup = startup.add(what, value);
};
protected method .set_startup_objects() {
arg name, definer, value;
switch (value[1]) {
case 'set:
startup = startup.add('objects, value[2]);
case 'add:
startup = startup.add('objects, setadd(startup['objects], value[2]));
case 'del:
startup = startup.add('objects, setremove(startup['objects], value[2]));
default:
throw(~type, "Unknown action: " + (value[1]));
}
};
protected method .set_validate_email_addresses() {
arg name, definer, value;
validate_email_addresses = value;
};
protected method .set_writable_core() {
arg name, definer, value;
writable_core = value;
};
public method .shutdown() {
var opt, str, obj;
(> .perms(sender(), $sys) <);
// tell startup objects that we are closing shop
for obj in (startup['objects]) {
.log(("Calling " + obj) + ".shutdown()");
catch any
(> obj.shutdown() <);
with
.log($parse_lib.traceback(traceback()));
}
touched = 0;
return shutdown();
};
driver method .signal() {
arg signal;
var line;
// the driver will send the following signals:
// HUP -- driver will drop out of atomic mode and will flush i/o
// USR2 -- driver does nothing
// USR1 -- driver aborts all currently executing tasks, not suggested
// QUIT -- For both of these the driver will shutdown after current
// INT tasks finish their current execution frame. Note: do not
// preempt tasks at this point as they will not resume.
line = ("** Received Signal: " + signal) + " **";
(| $channel_ui._broadcast('System, line) |);
if (sig in ['QUIT, 'INT]) {
(| $channel_ui._broadcast('all, "******************************") |);
(| $channel_ui._broadcast('all, "** IMMINENT SERVER SHUTDOWN **") |);
(| $channel_ui._broadcast('all, "******************************") |);
}
};
public method .spawn_selfmanager() {
arg name, @writers;
var obj, w;
// Differs from .spawn_sender in that the object created manages itself.
// Add to this list as necessary:
(> .perms(sender(), $mail_message) <);
obj = (> .create([sender()], name, this()) <);
for w in (writers)
obj.add_writer(w);
obj.change_manager(obj);
return obj;
};
public method .spawn_sender() {
arg suffix, manager;
var namestr;
(> .perms(caller(), $root, $sys) <);
namestr = (tostr(sender().objname()) + "_") + suffix;
return .create([sender()], tosym(namestr), manager);
};
driver method .startup() {
arg args;
var opt, str, obj, f, ver, firsttime, doinit;
set_heartbeat(0);
// clean db?
if ("-clean" in args) {
catch any {
dblog("** Cleaning Database..");
(> .clean_database() <);
dblog("** Done.");
if (dict_contains(startup, 'time))
startup = dict_del(startup, 'time);
} with {
.log($parse_lib.traceback(traceback()));
}
doinit = "-init" in args;
} else {
doinit = ("-init" in args) || (!dict_contains(startup, 'time));
}
// init?
if (doinit) {
catch any {
dblog("** Initializing Database..");
(> .init_database() <);
dblog("** Done.");
} with {
.log($parse_lib.traceback(traceback()));
}
}
// done?
if ("-quit" in args) {
dblog("** Shutting down.");
.shutdown();
return;
}
// make core?
if ((opt = find opt in (args) where (opt.match_begin("-makecore=")))) {
ver = regexp(args[opt], "-MAKECORE=(.*)$")[1];
dblog(("** Calling .make_core(\"" + ver) + "\")..");
.make_core(ver);
return;
}
// Standard startup..
startup = startup.add('time, time());
backup = backup.add('next, time() + (backup['interval]));
catch any {
// get back the heartbeat
set_heartbeat(startup['heartbeat_interval]);
// Bind functions for security
for f in (bindings) {
catch any
bind_function(@f);
with
dblog((("** Unable to bind function " + (f[1])) + "() to ") + (f[2]));
}
// tell objects who should know, that we are online
if (type(args) != 'list)
args = [];
for obj in (startup['objects]) {
.log(("Calling " + obj) + ".startup()");
catch any
(> obj.startup(@args) <);
with
.log($parse_lib.traceback(traceback()));
}
} with {
.log(("** Startup ERROR at " + ctime()) + ":");
.log(toliteral(traceback()));
.log($parse_lib.traceback(traceback(), -1, ""));
}
};
public method .startup_dnsserv() {
if (sender() != $dns)
(> .perms(sender(), 'system) <);
execute("dnsstartup", [], 1);
};
public method .status() {
return (> ._status() <) + [backup['interval], backup['last], backup['next]];
};
public method .system() {
return system;
};
public method .task_info() {
arg @args;
(> .perms(sender(), 'system) <);
return (> task_info(@args) <);
};
public method .touch() {
arg @nocorelock;
if ((!nocorelock) && ((sender().has_flag('core)) && (!writable_core)))
throw(~perm, sender() + " is a core object, and the core isn't writable.");
touched = time();
};
public method .touched() {
return touched;
};
public method .user_denied() {
arg user;
return user in deny_users;
};
public method .validate_email_addresses() {
arg @args;
return validate_email_addresses;
};
public method .version(): native;
public method .writable_core() {
arg @args;
return writable_core;
};
bind_native .status() ._status();