Generic connection object

    This object determines behavior for connections.

    Public methods:

	addr()				Get address of connection
	port()				Get port of connection

    Server methods:

        connect()                       Indicates onnection
        disconnect()                    Indicates disconnection
        parse(s)                        Indicates text line from user

    Private methods:

        echo_welcome()                  Echo welcome screen to user

    Commands:

        connect_cmd()                   Connect to an existing user
        create_cmd()                    Create a new user
        quit_cmd()                      Quit and log out

parent has_commands
object connection

var root name 'connection
var connection user 0
var connection addr 0
var connection port 0
var connection buffer 0

method init_connection
    if (caller() != $root)
        throw(~perm, "Caller is not $root.");
    user = 0;
    addr = 0;
    port = 0;
    buffer = `[];
.

method uninit_connection
    if (caller() != $root)
	throw(~perm, "Caller is not $root.");
    user.connection_gone(addr, port);
.

eval
    .initialize();
    .add_command("conn?ect *", 'connect_cmd);
    .add_command("create *", 'create_cmd);
    .add_command("@quit", 'quit_cmd);
.

method tell
    arg s;

    if (sender() != this() && sender() != user)
        throw(~perm, "Sender not this or the user.");
    echo(buffer_from_strings([s]));
.

method connect
    arg addr_arg, port_arg;

    if (sender() != 0)
        throw(~perm, "Sender not the server.");
    addr = addr_arg;
    port = port_arg;
    .echo_welcome();
    $sys.connection_starting();
.

method disconnect
    if (sender() != 0)
        throw(~perm, "Sender not the server.");
    .destroy();
.

method parse
    arg incoming;
    var lines, line;

    lines = buffer_to_strings(buffer_append(buffer, incoming));
    buffer = lines[listlen(lines)];
    for line in (sublist(lines, 1, listlen(lines) - 1))
        (| .parse_line(line) |);
.

method parse_line
    arg str;
    var cmd, logstr;

    if (caller() != definer() || sender() != this())
        throw(~perm, "Invalid access to private method.");

    logstr = addr + " " + toliteral(port);
    if (user)
        logstr = logstr + " " + toliteral(user.name()) + " (" + user.vr_name() + ")";
    logstr = logstr + ": " + str;
    $sys.log(logstr);

    // If we're logged in, forward the line to the player.
    if (user) {
        if (user.parse_line(str) == 'disconnect)
            disconnect();
        return;
    }

    // Display tracebacks, even though user may be clueless.
    catch any {
        cmd = .match_command(str);
        if (cmd)
            .(cmd[1])(@cmd[2]);
        else
            .echo_welcome();
    } with handler {
        for str in (traceback())
            .tell(str);
    }
.

method user
    return user;
.

method addr
    return addr;
.

method port
    return port;
.

method echo_welcome
    if (sender() != this() || caller() != definer())
        throw(~perm, "Invalid access to private method.");
    if (!(| echo_file("welcome") |))
        .tell("<The file 'welcome' is missing.>");
.

method connect_cmd
    arg dummy, words;
    var words, u, password, salt;

    if (sender() != this())
        throw(~perm, "Sender is not this.");

    // Get user name and password.
    words = explode(words);
    if (listlen(words) != 2) {
        .tell("Usage: connect <name> <password>");
        return;
    }

    // Look for a user with the given name.
    catch ~usernf {
        u = $user_db.find_user(words[1]);
    } with handler {
        .tell("The user \"" + words[1] + "\" does not exist.");
        return;
    }

    // Check if the password is correct.
    if (!u.check_password(words[2])) {
        .tell("That is the wrong password for " + words[1] + ".");
        return;
    }

    // Log the user in.
    user = u;
    user.connection_logged_in(addr, port);
.

method create_cmd
    arg dummy, words;
    var words;

    if (sender() != this())
        throw(~perm, "Sender is not this.");

    // Get user name and password.
    words = explode(words);
    if (listlen(words) != 2) {
        .tell("Usage: create <name> <password>");
        return;
    }

    // Create a new user object.
    catch any {
        user = $sys.create_user(words[1], words[2]);
    } with handler {
        switch (error()) {
            case ~spaces:
                .tell("Invalid user name " + toliteral(words[1]));
            case ~duplicate:
                .tell("The user " + toliteral(words[1]) + " already exists.");
            default:
                .tell("There was a problem creating your character:");
                rethrow(error());
        }
        return;
    }

    // Log the user in.
    user.connection_logged_in(addr, port);
.

method quit_cmd
    arg dummy;

    if (sender() != this())
        throw(~perm, "Sender is not this.");
    disconnect();
.

method user_going_away
    if (caller() != $user)
        throw(~perm, "Caller is not $user.");
    disconnect();
.