/
ColdCore-3.0a9.02/
ColdCore-3.0a9.02/src/
new object $daemon: $network;

var $daemon connection = 0;
var $daemon current_port = 0;
var $daemon current_ports = 0;
var $daemon listen = 0;
var $daemon next_connection = 0;
var $root created_on = 809051864;
var $root defined_settings = #[["connection", #[['get, ['get_connection]], ['set, ['set_connection]], ['parse, ['parse_connection_setting]]]], ["listen", #[['get, ['get_listen]], ['set, ['set_listen]], ['parse, ['parse_listen]], ['format, ['format_listen]]]]];
var $root flags = ['methods, 'code, 'core, 'variables];
var $root inited = 1;
var $root managed = [$daemon];
var $root manager = $daemon;

driver method .connect() {
    arg remote, local, socket;
    var conn;
    
    if ($sys.host_denied(remote)) {
        close_connection();
        return;
    }
    if (!valid(next_connection))
        next_connection = connection.new_connection();
    conn = next_connection;
    reassign_connection(conn);
    next_connection = connection.new_connection();
    conn.start(remote, local, socket, (current_ports[1])[1]);
};

root method .core_daemon() {
    .stop_listening();
    if (this() != definer())
        .set_setting("listen", $daemon, tostr(((.get_setting("listen", $daemon))[1])[1]));
};

public method .current_port() {
    return (current_ports[1])[1];
};

public method .current_ports() {
    return current_ports;
};

public method .format_listen() {
    arg value, @noresolv;
    var p, out;
    
    out = [];
    for p in (value) {
        if (listlen(p) > 1)
            out += [((noresolv ? (p[2]) : ($dns.hostname(p[2]))) + ":") + (p[1])];
        else
            out += [p[1]];
    }
    return out.join(", ");
};

protected method .get_connection() {
    arg @args;
    
    return connection;
};

public method .get_listen() {
    arg @args;
    
    return listen || [];
};

public method .parse_connection_setting() {
    arg value, @args;
    var obj;
    
    obj = (> $object_lib.to_dbref(value) <);
    if (!(obj.is($connection)))
        throw(~perm, "Connection object must be descended from $connection.");
    return obj;
};

public method .parse_listen() {
    arg value, @args;
    var out, p, m, port, ip;
    
    if (!value)
        throw(~parse, "Invalid listen setting, must be a list of ports or host:ports.");
    out = map p in (value.explode_english_list()) to ((> .parse_port(p) <));
    if (!out)
        throw(~parse, "Invalid listen setting, must be a list of ports or host:ports.");
    return out;
};

public method .parse_port() {
    arg str;
    var m, host, port, ip;
    
    if ((m = str.regexp("^([^:]+):(.*)$"))) {
        [host, port] = m;
        host = host.trim();
        port = port.trim();
        if (host) {
            ip = $dns.ip(host);
            if (ip == "-1")
                throw(~parse, "Unable to resolv hostname: " + host);
            host = ip;
        }
    } else {
        port = str;
        host = "";
    }
    if (!(port.is_numeric()))
        throw(~parse, "Invalid port: " + port);
    port = toint(port);
    if ((port <= 0) || (port > 65535))
        throw(~parse, ("Invalid port " + port) + " (out of range, must be 0 .. 65535)");
    if (host)
        return [port, host];
    else
        return [port];
};

protected method .set_connection() {
    arg name, definer, value, @args;
    
    connection = value;
};

protected method .set_listen() {
    arg name, definer, value;
    
    listen = value;
};

public method .shutdown() {
    arg @args;
    
    (> .perms(caller(), $sys) <);
    (> .stop_listening() <);
};

public method .start_listening() {
    arg @ports;
    var p, last;
    
    (> .perms(sender()) <);
    (| .stop_listening() |);
    ports ?= listen;
    current_ports = [];
    catch any {
        for p in (ports) {
            last = p;
            bind_port(@p);
            current_ports += [p];
        }
    } with {
        for p in (current_ports)
            (| unbind_port(p[1]) |);
        current_ports = [];
        throw(error(), (traceback()[1])[2], last);
    }
    next_connection = connection.new_connection();
};

public method .startup(): forked  {
    arg @args;
    var match, port, rx, str, o, ports, msg;
    
    (> .perms(caller(), 'system) <);
    rx = tostr(.objname());
    rx = ("-(p)" + substr(rx, 1, ("_" in rx) - 1)) + "=(.*)";
    if (find str in (args) where ((match = regexp(str, rx)))) {
        [o, ports] = match;
        ports = .parse_port(ports);
        if (strcmp(o, "p") == 0)
            ports = listen + ports;
    } else {
        ports = listen;
    }
    catch any {
        (> .start_listening(@ports) <);
        if (listlen(ports) > 1)
            msg = " on ports ";
        else
            msg = " on port ";
        $sys.log(((("** Starting " + this()) + msg) + (.format_listen(ports, 1))) + " **");
    } with {
        switch (error()) {
            case ~bind:
                if ((traceback()[1])[3])
                    $sys.log(("** Unable to bind to port " + ((((traceback()[1])[3]).reverse()).join(":"))) + " **");
                else
                    $sys.log(("** Unable to bind " + this()) + " **");
            default:
                $sys.log($parse_lib.traceback(traceback()));
        }
    }
};

public method .stop_listening() {
    var p;
    
    (> .perms(sender()) <);
    if (valid(connection))
        (> connection.daemon_shutdown() <);
    (| clear_var('next_connection) |);
    for p in (current_ports || [])
        (| unbind_port(p[1]) |);
    (| clear_var('current_ports) |);
};