new object $connection: $network; var $connection active = 0; var $connection buffer = `[]; var $connection daemon = 0; var $connection foreign_addr = 0; var $connection host = ""; var $connection interface = 0; var $connection line_buffer = 0; var $connection local_addr = 0; var $connection local_port = 0; var $connection port = 0; var $connection read_block = []; var $connection remote_addr = 0; var $connection remote_name = 0; var $connection remote_port = 0; var $connection started_at = 0; var $connection tid = 0; var $connection timeout = 0; var $root created_on = 809051864; var $root defined_settings = #[["interface", #[['get, ['get_interface]], ['set, ['set_interface]], ['parse, ['parse_interface]]]]]; var $root flags = ['methods, 'code, 'core, 'variables]; var $root inited = 1; var $root managed = [$connection]; var $root manager = $connection; public method .abort_reading_block() { (> .perms(sender()) <); read_block = read_block.add('lines, 'aborted); .finish_reading_block(); }; public method .active() { return active; }; public method .active_since() { return active; }; public method .address() { arg @nolookup; var name; (> .perms(sender()) <); return remote_addr; if (!remote_name) { name = (| $dns.hostname(remote_addr) |); if (name) remote_name = name; else remote_name = remote_addr; } return remote_name; }; public method .change_connection_interface() { arg old, new; var i; (> .perms(caller(), $connection) <); if (old) sender().del_writer((| class(old) |) || old); sender().add_writer((| class(new) |) || new); }; public method .change_interface() { arg new; var old; (> .perms(sender()) <); if (interface) { old = interface; old.connection_going_away(.address(), remote_port); } interface = new; (.manager()).change_connection_interface(old, new); interface.connection_starting(.address(), remote_port); }; public method .close() { (sender() != this()) && (> .perms(sender()) <); (> $sys.destroy_sender() <); }; public method .cwritef() { arg fname; (> .perms(sender()) <); (> cwritef(fname) <); }; public method .daemon_shutdown() { var c; // called by $daemon.stop_listening() (> .perms(caller(), $daemon) <); for c in (.children()) (> c.close() <); (> interface.daemon_shutdown() <); }; driver method .disconnect() { .close(); }; protected method .do_timeout() { if (!timeout) return; .write(("Timeout (" + tostr(timeout)) + ")"); .close(); }; public method .finish_reading_block() { var task_id, lines; (> .perms(sender()) <); task_id = read_block.task_id(); lines = read_block.lines(); read_block = 0; $scheduler.resume(task_id, lines); }; protected method .get_ident() { return $ident_connection.get(remote_addr, remote_port, local_port); }; protected method .get_interface() { arg @args; return interface; }; root method .init_connection() { buffer = `[]; local_addr = (remote_addr = ""); line_buffer = []; timeout = 300; tid = -1; // remove all flags .set_flags([]); }; public method .interface() { (caller() == $daemon) || (> .perms(sender()) <); return interface; }; public method .interface_going_away() { if ((sender() != interface) && (sender() != (| class(interface) |))) throw(~perm, sender() + " is not the interface."); interface = 0; (> $sys.destroy_sender() <); }; public method .is_reading_block() { return read_block ? 1 : 0; }; public method .new_connection() { var new, i; (> .perms(caller(), $daemon) <); new = .spawn(); i = interface.new(new); new.add_writer(sender()); new.add_writer(interface); new.add_writer((| class(i) |) || i); new.add_writer(this()); new.new_interface(i); return new; }; public method .new_interface() { arg obj; (> .perms(sender()) <); interface = obj; }; protected method .open_connection() { arg host, port; (> open_connection(host, port) <); }; driver method .parse() { arg incoming; var lines, line, index; lines = buf_to_strings(buffer + incoming); index = listlen(lines); buffer = lines[index]; lines = delete(lines, index); line_buffer += lines; while (line_buffer) { line = line_buffer[1]; line_buffer = delete(line_buffer, 1); (| .parse_line(line) |); } }; public method .parse_interface() { arg value, @args; var obj; obj = (> $object_lib.to_dbref(value) <); return obj; }; protected method .parse_line() { arg line; if (read_block) { read_block = read_block.parse(line); line = .rehash_read_status(); if ((!line) && (line != "")) return; } if ((interface.parse_line(line)) == 'disconnect) (> .close() <); }; private method .push_buffered(): forked { var line; // called when a read() suspends the connection--to finish unbuffering i/o while (line_buffer) { line = line_buffer[1]; line_buffer = delete(line_buffer, 1); (| .parse_line(line) |); } }; protected method .rehash_read_status() { switch (read_block.status()) { case 'abort: .abort_reading_block(); case 'not_done: // do nothing case 'done: .finish_reading_block(); case 'pass_command: return read_block.command(); } return 0; }; protected method .set_active() { arg value; active = value; }; public method .set_daemon() { arg obj; (> .perms(sender()) <); daemon = obj; }; protected method .set_interface() { arg name, definer, value, @args; interface = value; }; public method .set_remote_port() { arg port; (> .perms(sender()) <); remote_port = port; }; public method .set_timeout() { arg seconds; (> .perms(sender(), interface, this()) <); timeout = seconds; if (!timeout) (| clear_var('timeout) |); }; public method .start() { arg remote, local, rport, lport; // Make this method 'fork' from the regular thread stack (> .perms(caller(), $daemon) <); active = time(); remote_addr = remote; remote_port = rport; local_addr = local; local_port = lport; // if (timeout) // $scheduler.add_task(timeout, 'do_timeout); interface.connection_starting(.address(), remote_port); }; public method .start_reading_block() { arg count; (> .perms(sender()) <); read_block = $read_parser.new_with(task_id(), count); (| .push_buffered() |); return (> $scheduler.suspend(this()) <); }; public method .timeout() { return timeout; }; root method .uninit_connection() { (| close_connection() |); active = 0; if (interface) (| interface.connection_going_away(remote_addr, remote_port) |); interface = 0; if (read_block) read_block = read_block.add('lines, 'disconnected); .finish_reading_block(); if (tid != (-1)) $scheduler.del_task(tid); tid = 0; }; public method .write() { arg what, @how; var elem, sep; sep = ('non_terminated in how) ? `[] : `[13, 10]; switch (type(what)) { case 'string: what = $buffer.from_strings([what], sep); case 'list: what = $buffer.from_strings(what, sep); case 'buffer: default: throw(~type, "Write: strings, list of strings and buffers."); } cwrite(what); }; public method .write_buffer() { arg buffer; if ((sender() != interface) && (sender() != (| class(interface) |))) throw(~perm, sender() + " cannot write this connection."); if (type(buffer) != 'buffer) throw(~type, "Argument must be a buffer."); cwrite(buffer); }; public method .write_string() { arg string; if ((sender() != interface) && (sender() != (| class(interface) |))) throw(~perm, sender() + " cannot write this connection."); if (type(string) != 'string) throw(~type, "Argument must be a string."); cwrite($buffer.from_string(string)); }; public method .write_strings() { arg strings; if ((sender() != interface) && (sender() != (| class(interface) |))) throw(~perm, sender() + " cannot write this connection."); if (type(strings) != 'list) throw(~type, "Argument must be a list of strings."); cwrite($buffer.from_strings(string)); };