/
CDC-1.2b/
CDC-1.2b/src/
parent $root
object $backdoor

var $root dbref 'backdoor
var $root child_index 27
var $root fertile 0
var $root manager $backdoor
var $root owned [$backdoor, $]
var $root owners [$]
var $root writable []
var $root readable []
var $root inited 1
var $backdoor buffer `[]
var $backdoor ip ""
var $backdoor hostname ""
var $backdoor line_buffer []
var $backdoor valid_hosts [""]
var $backdoor passwords #[["back1", "*"], ["back2", "*"]]
var $backdoor connected 1
var $backdoor programming 0
var $backdoor port 1169
var $backdoor who ""

method init_backdoor
    if (caller() != $root)
        throw(~perm, "Caller is not $root.");
    buffer = `[];
    ip = "";
    hostname = "";
    line_buffer = [];
    programming = #[];
    who = "";
.

method uninit_backdoor
    if (caller() != $root)
        throw(~perm, "Caller is not $root.");
    buffer = `[];
    ip = "";
    hostname = "";
    line_buffer = [];
    programming = [];
.

method parse
    arg incoming;
    var lines, line, index;
    
    // called by the server with an incoming buffer
    if (sender() || caller())
        throw(~perm, "Sender and caller are not the server");
    lines = buffer_to_strings(buffer_append(buffer, incoming));
    index = listlen(lines);
    buffer = lines[index];
    lines = delete(lines, index);
    line_buffer = [@line_buffer, @lines];
    while (line_buffer) {
        line = line_buffer[1];
        line_buffer = delete(line_buffer, 1);
        (| .parse_line(line) |);
    }
.

method parse_line
    arg line;
    var l;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    catch any {
        if (connected) {
            if (programming)
                ._handle_programming_line(line);
            else
                ._handle_command_line(line);
        } else {
            ._handle_login(line);
        }
    } with handler {
        for l in ($parse.traceback(traceback()))
            .echo(l);
    }
.

method quit_cmd
    arg [cmd];
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    .echo("Goodbye.");
    .backdoor_log(("Disconnect <" + (.address())) + ">");
    .close();
.

method eval_cmd
    arg cmd, [args];
    var code, result;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    code = (args.to_string()) + ";";
    result = .eval([code], this());
    if ((result[1]) == 'errors)
        .echo(result[2]);
    else
        .echo("=> " + toliteral(result[2]));
.

method eval_as_cmd
    arg cmd, [args];
    var result, code, obj;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    args = args.to_string();
    result = match_template("* eval *", args);
    if (!result) {
        .echo("Syntax: `as <object> eval <code>`");
        return;
    }
    obj = (| $object.to_dbref(result[1]) |);
    if (!obj) {
        .echo(("Unable to find object \"" + (result[1])) + "\".");
        return;
    }
    code = (result[3]) + ";";
    result = obj.eval([code]);
    if ((result[1]) == 'errors)
        .echo(result[2]);
    else
        .echo("as eval => " + toliteral(result[2]));
.

method echo
    arg what;
    var line;
    
    if (type(what) == 'list) {
        for line in (what)
            .echo(line);
    } else {
        ._echo(what);
    }
.

method eval_as_to_cmd
    arg cmd, [args];
    var result, code, obj, target;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    args = args.to_string();
    result = match_template("* as * eval *", args);
    if (!result) {
        .echo("Syntax: `definer <object> as <object> eval <code>`");
        return;
    }
    obj = (| $object.to_dbref(result[1]) |);
    target = (| $object.to_dbref(result[3]) |);
    if (!obj) {
        .echo(("Unable to find object \"" + (result[1])) + "\".");
        return;
    }
    if (!target) {
        .echo(("Unable to find object \"" + (result[3])) + "\".");
        return;
    }
    code = (result[5]) + ";";
    result = obj.eval([code], target);
    if ((result[1]) == 'errors)
        .echo(result[2]);
    else
        .echo("definer as eval => " + toliteral(result[2]));
.

method close
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    close_connection();
    if (sender() != definer())
        .destroy();
.

method set_password
    arg what, word;
    var x, i, a, cword;
    
    if (!($sys.is_admin(sender()))) {
        .log_backdoor("Invalid set password attempt by " + (sender().namef('xref)));
        throw(~perm, "Permission denied, only admins may set backdoor passwords.");
    }
    if (strlen(word) < 6)
        throw(~badpasswd, "Passwords must be at least 6 characters long.");
    for x in [1 .. strlen(word)] {
        if ((word[x]) in "1234567890")
            i = i + 1;
        else
            a = a + 1;
    }
    if ((a < 2) || (i < 2))
        throw(~badpasswd, "Passwords must contain at least 2 numeric characters and 2 non-numeric characters.");
    cword = crypt(word);
    if (cword in (passwords.values()))
        throw(~badpasswd, "That password already exists.");
    passwords = dict_add(passwords, what, cword);
.

method del_password
    arg what;
    
    if (!($sys.is_admin(sender()))) {
        .log_backdoor("Invalid password removal attempt by " + (sender().namef('xref)));
        throw(~perm, "Permission denied, only admins may adjust backdoor passwords.");
    }
    passwords = dict_del(passwords, what);
.

method address
    arg [args];
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    args = [@args, 'hostname][1];
    switch (args) {
        case 'ip:
            return ip;
        case 'hostname:
            return hostname;
    }
.

method set_address
    arg host;
    
    if ((sender() != this()) && (!(sender().has_ancestor(definer()))))
        throw(~perm, "Sender is not this.");
    ip = host;
    hostname = hostname(ip);
.

method connect
    arg host, socket;
    var c;
    
    if (sender() || caller())
        throw(~perm, "Sender is not the server.");
    if (!(host in valid_hosts)) {
        .backdoor_log("Connection from invalid host: " + host);
        .echo("Your host is not one of the valid hosts.");
        .close();
        return;
    }
    .backdoor_log("Connection from: " + host);
    c = .spawn();
    if ($sys.reassign_connection(c))
        c.set_address(host);
    else
        c.destroy();
.

method program
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    programming = #[['task_id, task_id()], ['code, []]];
    return $sys.suspend();
.

method check_passwords
    arg str;
    var line, pwd;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    line = explode(str);
    pwd = $backdoor.passwords();
    if ((!(| pwd[line[1]] |)) || ((listlen(line) != 4) || ((!(.check_encrypted(line[1], line[2]))) || ((!(.check_encrypted("back1", line[3]))) || (!(.check_encrypted("back2", line[4])))))))
        return 0;
    return 1;
.

method disconnect
    arg [args];
    
    if (sender() || caller())
        throw(~perm, "Sender is not the server.");
    .close();
.

method startup
    arg [args];
    var opt, curr_port;
    
    (> .perms(caller(), $sys) <);
    catch any {
        opt = "-pbackdoor" in args;
        curr_port = (| toint(args[opt + 1]) |) || port;
        $sys.log(("** Starting $backdoor on port " + tostr(curr_port)) + " **");
        $sys.bind_port(curr_port, this());
    } with handler {
        $sys.log($parse.traceback(traceback()));
    }
.

method passwords
    if (!(sender().has_ancestor(this())))
        throw(~perm, "Sender is not descended from this.");
    return passwords;
.

method backdoor_log
    arg str;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    $sys.log(((((.dbref()) + " (") + who) + "): ") + str);
.

method check_encrypted
    arg key, str;
    var pwd;
    
    pwd = ($backdoor.passwords())[key];
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    return crypt(str, substr(pwd, 1, 2)) == pwd;
.

method _echo
    arg what;
    
    // send off a string or buffer
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    (> echo(buffer_from_strings([what])) <);
.

method program_cmd
    arg cmd, what;
    var code, ref, ignore, errors;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    ref = $parse.full_reference(what);
    if (((ref[1]) != 'method) || (!(ref[3]))) {
        .echo("Invalid object.method() reference, ignoring code until \".\"");
        ignore = 1;
    }
    .echo(((("-- Enter text for " + ((ref[2]).dbref())) + ".") + tostr(ref[3])) + " --");
    code = .program();
    if (code == 'aborted) {
        .echo("** Aborted **");
    } else if (ignore) {
        .echo("Finished ignoring text.");
    } else {
        errors = (ref[2]).compile(code, ref[3]);
        if (errors)
            .echo(errors);
        else
            .echo("Method compiled.");
    }
.

method _handle_programming_line
    arg line;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    if (!line) {
        programming = programming.add_elem('code, "");
    } else if (line == ".") {
        $sys.resume(programming['task_id], programming['code]);
        programming = #[];
    } else if (line == "@abort") {
        $sys.cancel(programming['task_id]);
        programming = #[];
        .echo("** Aborted **");
    } else {
        programming = programming.add_elem('code, line);
    }
.

method _handle_command_line
    arg line;
    var cmds, cmd;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    
    //
    cmds = "list, program, quit, eval, as eval, def as eval";
    if (!line) {
        .echo("Available commands: " + cmds);
        return;
    }
    
    // because I'm lazy, check for ';'
    if ((line[1]) == ";")
        line = "eval " + substr(line, 2);
    .backdoor_log("Command: " + line);
    cmd = explode(line);
    switch (cmd[1]) {
        case "@program", ".program", "program":
            .program_cmd(@cmd);
        case "@quit", "quit":
            .quit_cmd(@cmd);
        case "eval":
            .eval_cmd(@cmd);
        case "as":
            .eval_as_cmd(@cmd);
        case "definer", "def":
            .eval_as_to_cmd(@cmd);
        case "list", "@list", "@nlist":
            .list_cmd(@cmd);
        default:
            .echo("Available commands: " + cmds);
    }
.

method _handle_login
    arg line;
    
    if (sender() != this())
        throw(~perm, "Sender is not the definer.");
    if (.check_passwords(line)) {
        connected = 1;
        who = (line.explode())[1];
        .backdoor_log("Valid login sequence.");
        .echo("Valid login sequence.");
    } else {
        .backdoor_log((("Invalid login sequence (" + ((| line[1] |) || "<none>")) + " ...) from: ") + (.address()));
        .echo("Invalid login sequence.");
        .close();
    }
.

method list_cmd
    arg cmd, what;
    var code, ref, ancestor, nums, line;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    ref = $parse.full_reference(what);
    nums = cmd == "@nlist";
    if (((ref[1]) != 'method) || (!(ref[3]))) {
        .echo("Invalid object.method() reference.");
        return;
    }
    ancestor = (ref[2]).find_method(ref[3]);
    code = ancestor.list_method(ref[3]);
    if (nums) {
        for line in [1 .. listlen(code)]
            code = replace(code, line, ((((line < 10) ? " " | "") + tostr(line)) + ": ") + (code[line]));
    } else {
        for line in [1 .. listlen(code)]
            code = replace(code, line, "  " + (code[line]));
    }
    .echo((("program " + ((ref[2]).dbref())) + ".") + tostr(ref[3]));
    .echo(code);
    .echo(".");
.