/*
* NAME: connection.c
* DESCRIPTION: manage a generic network connection
*/
# define DEBUG 0
inherit "/std/core";
inherit "/std/string";
inherit "/std/data";
# if DEBUG
inherit "/std/vartext";
# else
# define var2str(x) ""
# endif
# include <objects.h>
# include <moo/verb.h>
# include <moo/config.h>
# include <moo/command.h>
# include <moo/perms.h>
# define F_DESTRUCTING 0x01 /* to preserve this object in close() */
# define F_PROGRAMMING 0x02 /* set if we are in the middle of .program */
# define F_BACKDOOR 0x04 /* enabled backdoor eval? */
# define F_PREFIXED 0x08 /* seen PREFIX label? */
# define F_PROMPTED 0x10 /* seen prompt yet? */
# define F_BOOTSTRAP 0x20 /* currently bootstrapping? */
# define F_LPCONLY 0x40 /* not associated with a MOO object? */
# define F_TIMELIMIT 0x80 /* connection will timeout? */
static string ip_number; /* TCP/IP address of this connection */
static string ip_name; /* TCP/IP hostname of this connection */
private int port_number; /* TCP/IP remote port number */
static int pfd; /* pseudo file-descriptor */
private int time_connect; /* time at connect */
private int time_input; /* time at last input */
private int max_object; /* for remembering the number of objects */
private object listener; /* associated port object (usually #0) */
private object player; /* associated player object */
static int player_id; /* player object number (or negative num) */
private int timeout_handle; /* for removing call_out(connect_timeout) */
private int flags; /* status flags */
static mixed *susp_data; /* passing read information to a task */
private string oob_prefix; /* out-of-band command prefix */
private object input_to_obj; /* passing read information to LPC */
private string input_to_func; /* LPC function */
private string prefix, suffix; /* PREFIX, SUFFIX strings */
private string prompt; /* prompt string */
/*
* NAME: create()
* DESCRIPTION: called when object is created
*/
static
void create(void)
{
::create();
oob_prefix = CONFIG->query(CF_OOB_PREFIX);
}
void notify(string msg);
/*
* NAME: set_listener()
* DESCRIPTION: change our port listener object (usually #0)
*/
void set_listener(object ob)
{
listener = ob;
}
/*
* NAME: get_listener()
* DESCRIPTION: return our port listener object (usually #0)
*/
object get_listener(void)
{
return listener;
}
/*
* NAME: set_prefix()
* DESCRIPTION: modify or delete this connection's output prefix
*/
void set_prefix(string str)
{
prefix = strlen(str) ? str : 0;
}
/*
* NAME: set_suffix()
* DESCRIPTION: modify or delete this connection's output suffix
*/
void set_suffix(string str)
{
suffix = strlen(str) ? str : 0;
}
/*
* NAME: get_delimiters()
* DESCRIPTION: return the current output prefix and suffix
*/
string *get_delimiters(void)
{
return ({ prefix, suffix });
}
/*
* NAME: set_prompt()
* DESCRIPTION: change this connection's prompt
*/
void set_prompt(string str)
{
prompt = strlen(str) ? str : 0;
}
/*
* NAME: get_prompt()
* DESCRIPTION: return this connection's current prompt
*/
string get_prompt(void)
{
return prompt ? prompt : "";
}
/*
* NAME: set_noecho()
* DESCRIPTION: to be overridden as necessary
*/
int set_noecho(void)
{
return E_INVARG;
}
/*
* NAME: set_bootstrap()
* DESCRIPTION: the server is currently bootstrapping
*/
void set_bootstrap(int bool)
{
if (bool)
flags |= F_BOOTSTRAP;
else
flags &= ~F_BOOTSTRAP;
}
/*
* NAME: set_backdoor()
* DESCRIPTION: allow this connection to use the built-in eval
*/
void set_backdoor(int bool)
{
if (bool)
flags |= F_BACKDOOR;
else
flags &= ~F_BACKDOOR;
}
/*
* NAME: prefix()
* DESCRIPTION: send this connection's current output prefix, if any
*/
void prefix(void)
{
if (prefix)
notify(prefix);
flags |= F_PREFIXED;
}
void send(string msg);
/*
* NAME: prompt()
* DESCRIPTION: send this connection's current prompt, if any
*/
void prompt(void)
{
if (prompt && ! (flags & F_PROMPTED))
send(prompt);
flags |= F_PROMPTED;
}
/*
* NAME: suffix()
* DESCRIPTION: send this connection's current ouput suffix, if any
*/
void suffix(void)
{
if (suffix && (flags & F_PREFIXED))
notify(suffix);
flags &= ~F_PREFIXED;
if (! (flags & F_DESTRUCTING))
prompt();
}
/*
* NAME: finish()
* DESCRIPTION: called after command processing is finished (or an error)
*/
void finish(void)
{
suffix();
}
/*
* NAME: parse_line()
* DESCRIPTION: parse a line of input using MOO command parsing semantics
*/
private
string *parse_line(string line)
{
int i, sz, special;
int in_space, in_quote, start_word;
string *words, argstr;
if (! (sz = strlen(line)))
return ({ "" });
for (i = 0; i < sz && (line[i] == ' ' || line[i] == '\t'); ++i);
if (i < sz)
{
switch (line[i])
{
case '\"':
special = 1, line = subst(line, i, 1, "say ");
break;
case ':':
special = 1, line = subst(line, i, 1, "emote ");
break;
case ';':
special = 1, line = subst(line, i, 1, "eval ");
break;
}
}
in_space = 1;
in_quote = 0;
start_word = -1;
words = ({ });
for (i = 0, sz = strlen(line); i < sz; ++i)
{
if (in_space)
{
if (line[i] == ' ')
continue;
in_space = 0;
start_word = i;
/* pass through */
}
if (line[i] == '\\')
{
line = subst(line, i, 1, "");
--sz;
continue;
}
if (line[i] == '\"')
{
line = subst(line, i, 1, "");
--sz; --i;
in_quote = (! in_quote);
continue;
}
if (line[i] == ' ' && (! in_quote))
{
words += ({ line[start_word .. i - 1] });
in_space = 1;
if (! argstr)
argstr = line[i + 1 ..];
continue;
}
}
if (! in_space)
words += ({ line[start_word ..] });
if (! argstr)
argstr = "";
if (! special && (sz = strlen(argstr)) && argstr[0] == ' ')
{
/* not special -- strip leading blanks */
for (i = 0; i < sz && argstr[i] == ' '; ++i);
argstr = argstr[i ..];
}
return ({ argstr }) + words;
}
/*
* NAME: filter_strings()
* DESCRIPTION: pull out strings from an array and lowercase them
*/
private
string *filter_strings(MOOVAL var)
{
int i, j, sz;
MOOVAL *list;
string *strs;
if (! LSTP(var))
return ({ });
list = LSTVAL(var);
strs = allocate(sz = sizeof(list));
for (i = 0, j = -1; i < sz; ++i)
if (STRP(list[i]))
strs[++j] = tolower(STRVAL(list[i]));
return strs[.. j];
}
/*
* NAME: fetch_names()
* DESCRIPTION: return an array of nearby objects and names
*/
private
mixed *fetch_names(void)
{
int i;
object ob, *list;
string **names;
list = player->get_contents();
if (ob = player->get_location())
list += ob->get_contents();
for (names = allocate(i = sizeof(list)); i--; )
{
ob = list[i];
names[i] = filter_strings(ob->safe_get_property("aliases")) +
({ tolower(ob->get_name()) });
}
return ({ list, names });
}
/*
* NAME: match_object()
* DESCRIPTION: locate objects based on description
*/
private
int match_object(string descrip, object *list, string **names)
{
int i, j, sz, count, match, len;
if (! strlen(descrip))
return MATCH_NOTHING;
if (sscanf(descrip, "#%d", i) && MOOOBJ(i))
return i;
descrip = tolower(descrip);
if (descrip == "me")
return OBJNUM(player);
if (descrip == "here")
{
object ob;
ob = player->get_location();
return ob ? OBJNUM(ob) : -1;
}
sz = sizeof(list);
for (i = 0; i < sz; ++i)
for (j = sizeof(names[i]); j--; )
if (descrip == names[i][j])
{
if (count++)
return MATCH_AMBIGUOUS;
match = OBJNUM(list[i]);
break;
}
if (count)
return match;
for (len = strlen(descrip), i = 0; i < sz; ++i)
for (j = sizeof(names[i]); j--; )
if (descrip == leftstr(names[i][j], len))
{
if (count++)
return MATCH_AMBIGUOUS;
match = OBJNUM(list[i]);
break;
}
if (count)
return match;
return MATCH_FAIL;
}
/*
* NAME: parse_cmd()
* DESCRIPTION: finish parsing a MOO command
*/
private
mixed *parse_cmd(string *args)
{
mixed *ret;
string *split, **names;
object *list;
split = global->match_preposition(args);
ret = fetch_names();
list = ret[0];
names = ret[1];
return ({ match_object(split[0], list, names),
split[1] == "" ? MATCH_NOTHING : global->prep_code(split[1]),
match_object(split[2], list, names) }) + split;
}
/*
* NAME: server_msg()
* DESCRIPTION: display a server message to the connection
*/
static
void server_msg(string message)
{
if (CONFIG->query(CF_SERVER_MSGS))
notify(message);
}
/*
* NAME: destruct()
* DESCRIPTION: destroy this object (perhaps from call_out)
*/
static
void destruct(void)
{
destruct_object(this_object());
}
/*
* NAME: boot()
* DESCRIPTION: terminate this user's connection
*/
varargs
void boot(string message)
{
if (message)
server_msg(message);
flags |= F_DESTRUCTING;
finish();
destruct();
}
/*
* NAME: get_id()
* DESCRIPTION: return the pfd/object id of this connection
*/
int get_id(void)
{
return player_id;
}
/*
* NAME: get_connection_name()
* DESCRIPTION: return a descriptive string for this connection
*/
string get_connection_name(void)
{
return (string) pfd + " from " +
((ip_name && strlen(ip_name)) ? ip_name : ip_number);
}
/*
* NAME: description()
* DESCRIPTION: return a description string for this connection
*/
string description(void)
{
return global->user_description(player_id) + " on " + get_connection_name();
}
/*
* NAME: connect_timeout()
* DESCRIPTION: user is taking too long
*/
static
void connect_timeout(void)
{
global->log_msg("TIMEOUT: " + description());
boot("*** Timed-out waiting for login. ***");
}
/*
* NAME: remove_timelimit()
* DESCRIPTION: stop limiting the user's idleness
*/
private
void remove_timelimit(void)
{
if (flags & F_TIMELIMIT)
{
remove_call_out(timeout_handle);
flags &= ~F_TIMELIMIT;
}
}
/*
* NAME: set_timelimit()
* DESCRIPTION: limit the user's idleness
*/
private
void set_timelimit(void)
{
int seconds;
remove_timelimit();
if (seconds = CONFIG->query(CF_CONNECT_TIMEOUT))
{
timeout_handle = call_out("connect_timeout", seconds);
flags |= F_TIMELIMIT;
}
}
/*
* NAME: connect_player()
* DESCRIPTION: (re)connect a player
*/
private
int connect_player(object ob, int id, int created)
{
object other_conn;
remove_timelimit();
time_connect = time();
player = ob;
if (other_conn = global->get_connection_obj(player))
{
global->redirect(this_object(), player_id, player);
other_conn->boot("*** Redirecting connection to new port ***");
server_msg("*** Redirecting old connection to this port ***");
player_id = id;
return 1; /* reconnected */
}
else
{
global->connect(this_object(), player_id, player);
server_msg(created ? "*** Created ***" : "*** Connected ***");
player_id = id;
return 0; /* connected */
}
}
/*
* NAME: connect_lpc()
* DESCRIPTION: attach this connection to an LPC object
*/
static
void connect_lpc(object obj)
{
remove_timelimit();
time_connect = time();
input_to_obj = obj;
input_to_func = "receive_message";
flags |= F_LPCONLY;
global->connect_lpc(this_object(), player_id, obj);
server_msg("*** Connected to " + object_name(obj) + " ***");
obj->open();
}
/*
* NAME: do_login_command()
* DESCRIPTION: process a line of input for an unlogged-in connection
*/
void do_login_command(JS_PROTO, string *args, string line)
{
object ob;
string verb;
int i;
JS_BEGIN;
max_object = global->get_max_object();
if (! listener)
return;
JS_PREP(1);
RET = listener->call_verb(JS_DATA(1), "do_login_command",
server_vars(player_id, listener, "do_login_command",
line, LSTVAL(STRLIST2MOO(args))...));
JS_END;
if (player_id >= 0 ||
(! STRP(RET) &&
(! OBJP(RET) ||
! (ob = MOOOBJ(OBJVAL(RET))) ||
! ob->is_player())))
return;
if (STRP(RET)) /* LPC object */
{
object obj;
catch(obj = load_object(STRVAL(RET)));
if (obj == 0)
return;
connect_lpc(obj);
return;
}
/* logged in */
if (OBJVAL(RET) > max_object) /* user created */
{
connect_player(ob, OBJVAL(RET), 1);
verb = "user_created";
}
else /* regular (re)connection */
{
verb = connect_player(ob, OBJVAL(RET), 0) ?
"user_reconnected" : "user_connected";
}
if (! listener)
return;
JS_PREP(2);
RET = listener->call_verb(JS_DATA(2), verb,
server_vars(OBJVAL(RET), listener, verb, 0, RET));
JS_END;
JS_END;
}
/*
* NAME: input_to()
* DESCRIPTION: prepare to return the next input line to LPC
*/
int input_to(object where, string what)
{
if (susp_data || input_to_obj)
return 0;
input_to_obj = where;
input_to_func = what;
return 1;
}
/*
* NAME: seed_read()
* DESCRIPTION: prepare to return the next input line for a read()
*/
int seed_read(JS_PROTO, int binary, mixed *jumpstack)
{
if (susp_data || input_to_obj)
return E_INVARG;
susp_data = ({ JS_DATA(bjump), binary, jumpstack });
return E_NONE;
}
/*
* NAME: unseed()
* DESCRIPTION: the suspended read() task was killed
*/
void unseed(void)
{
susp_data = 0;
}
/*
* NAME: builtin_eval()
* DESCRIPTION: evaluate MOO code unconditionally (backdoor)
*/
void builtin_eval(JS_PROTO, string code)
{
object vobj;
mixed *ast;
int i, sz, flags;
string err;
JS_BEGIN;
ast = parser->main(code + ";");
if (! ast[0])
{
ast = ast[1];
for (i = 0, sz = sizeof(ast); i < sz; ++i)
notify(ast[i]);
notify(sz + " error" + (sz == 1 ? "." : "s."));
return;
}
vobj = global->compile_verb(ast[1], 0);
/* don't call vobj->ref(); let it self-destruct after executing */
flags = IF_DEBUG | IF_WIZARD;
info = ({ flags, player_id,
({ OBJ(player_id), /* player */
OBJ(-1), /* this */
OBJ(player_id), /* caller */
LST( ({ }) ), /* args */
STR(""), /* argstr */
STR(""), /* verb */
OBJ(-1), /* dobj */
STR(""), /* dobjstr */
STR(""), /* prepstr */
OBJ(-1), /* iobj */
STR(""), /* iobjstr */
STD_VARS }),
0, "", -1, player_id, global->take_task(),
0, vobj, 0, global->get_max_depth(), 0 });
JS_PREP(1);
RET = vobj->main(JS_DATA(1));
JS_END;
debug("stack == " + var2str(stack));
notify("=> " + moo2str(RET, 1));
JS_END;
}
/*
* NAME: do_oob_command()
* DESCRIPTION: process an out-of-band command
*/
void do_oob_command(JS_PROTO, string *args, string line)
{
JS_BEGIN;
if (listener)
{
JS_PREP(1);
RET = listener->call_verb(JS_DATA(1), "do_out_of_band_command",
server_vars(player_id, listener,
"do_out_of_band_command",
line, LSTVAL(STRLIST2MOO(args))...));
JS_END;
}
JS_END;
}
/*
* NAME: program()
* DESCRIPTION: initiate programming of a verb
*/
private
void program(string obj, string verb)
{
mixed *ret;
object ob, *list;
int objnum;
string **names;
ret = fetch_names();
list = ret[0];
names = ret[1];
objnum = match_object(obj, list, names);
if (! (ob = MOOOBJ(objnum)))
{ notify("I don't see \"" + obj + "\" here."); return; }
notify("** Warning: `.program' is not supported.");
notify("Now ignoring program for " + ob->get_name() +
":" + verb + "(x). Use \".\" to end.");
flags |= F_PROGRAMMING;
}
/*
* NAME: program_line()
* DESCRIPTION: receive a line of MOO code
*/
private
void program_line(string line)
{
if (line == ".")
{
flags &= ~F_PROGRAMMING;
notify("Stopped programming.");
prompt();
}
}
/*
* NAME: do_read()
* DESCRIPTION: pass a line of input to a suspended read() task
*/
static
void do_read(string line)
{
mixed *stack, *info, *js;
int binary;
stack = susp_data[0];
info = susp_data[1];
binary = susp_data[4];
js = susp_data[5];
susp_data = 0;
global->task_started(info[I_TASKID]);
stack[SP + 1] = line ? (binary ? BUF(line) : STR(line)) : ERR(E_INVARG);
debug("stack == " + var2str(stack));
call_other(js[1], js[2], js[3]...);
}
/*
* NAME: do_input_to()
* DESCRIPTION: pass input to an LPC object
*/
private
mixed do_input_to(string line)
{
object where;
string what;
where = input_to_obj;
what = input_to_func;
if (! (flags & F_LPCONLY))
{
input_to_obj = 0;
input_to_func = 0;
}
return call_other(where, what, line);
}
/*
* NAME: do_huh()
* DESCRIPTION: a command was not recognized
*/
int do_huh(JS_PROTO, object room, string verb, mixed *parsed)
{
JS_BEGIN;
JS_PREP(1);
RET = room->call_verb(JS_INIT, "huh",
({ OBJ(player_id), /* player */
OBJ_OBJNUM(room), /* this */
OBJ(player_id), /* caller */
STRLIST2MOO(parsed[C_ARGS]), /* args */
STR(parsed[C_ARGSTR]), /* argstr */
STR(verb), /* verb */
OBJ(parsed[C_DOBJ]), /* dobj */
STR(parsed[C_DOBJSTR]), /* dobjstr */
STR(parsed[C_PREPSTR]), /* prepstr */
OBJ(parsed[C_IOBJ]), /* iobj */
STR(parsed[C_IOBJSTR]), /* iobjstr */
STD_VARS }) );
JS_END;
return ! STWP(RET);
JS_END;
}
/*
* NAME: do_command()
* DESCRIPTION: give the db a chance to process this command
*/
int do_command(JS_PROTO, string *args, string line)
{
JS_BEGIN;
if (! listener)
return 0;
JS_PREP(1);
if (catch(RET = listener->call_verb(JS_DATA(1), "do_command",
server_vars(player_id, listener, "do_command",
line, LSTVAL(STRLIST2MOO(args))...))))
return 0;
JS_END;
return TRUTHOF(RET);
JS_END;
}
/*
* NAME: want_binary()
* DESCRIPTION: return true iff we want to process input as binary data
*/
static
int want_binary(void)
{
return susp_data && susp_data[4];
}
/*
* NAME: process_data()
* DESCRIPTION: handle raw binary input
*/
static
void process_data(string data)
{
time_input = time();
flags &= ~F_PROMPTED;
if (flags & F_TIMELIMIT)
set_timelimit();
/* susp_data != 0 because want_binary() was called before we got here */
do_read(data);
}
/*
* NAME: sanitize()
* DESCRIPTION: make a string contain only valid MOO string characters
*/
private
string sanitize(string str)
{
int i, c;
for (i = strlen(str); i--; )
if (((c = str[i]) < ' ' && c != '\t') || c > '~')
str = str[.. i - 1] + str[i + 1 ..];
return str;
}
/*
* NAME: process_line()
* DESCRIPTION: handle a single line of input
*/
static
void process_line(string line)
{
object room, ob;
string *args, verb, lverb, argstr;
mixed *parsed;
int i;
time_input = time();
flags &= ~F_PROMPTED;
if (flags & F_TIMELIMIT)
set_timelimit();
/* check for LPC input_to() */
if (input_to_obj || (flags & F_LPCONLY))
{
if (input_to_obj && (do_input_to(line) || (flags & F_LPCONLY)))
return;
if (flags & F_LPCONLY)
{
boot("*** LPC connection broken ***");
return;
}
}
line = sanitize(line);
/* check for out-of-band command */
if (oob_prefix && strlen(line) >= strlen(oob_prefix) &&
line[.. strlen(oob_prefix) - 1] == oob_prefix)
{
args = parse_line(line);
do_oob_command(JS_INIT, args[1 ..], line);
return;
}
/* check for suspended read() */
if (susp_data)
{
do_read(line);
return;
}
/* check for .program text */
if (flags & F_PROGRAMMING)
{
program_line(line);
return;
}
/* check for ;; backdoor */
if ((flags & F_BACKDOOR) && strlen(line) >= 2 && line[0 .. 1] == ";;")
{
builtin_eval(JS_INIT, line[2 ..]);
return;
}
args = parse_line(line);
argstr = args[0];
args = args[1 ..];
/* check for un-logged-in connection */
if (! player)
{
do_login_command(JS_INIT, args, line);
return;
}
/* try in-db command parsing */
if (do_command(JS_INIT, args, line))
return;
/* check for empty line */
if (! sizeof(args))
return;
verb = args[0];
/* check for built-in verbs */
if (verb == "PREFIX")
{
set_prefix(argstr);
return;
}
if (verb == "SUFFIX")
{
set_suffix(argstr);
return;
}
if (verbname_match(".pr*ogram", verb) && programmerp(player_id))
{
string obj, vname;
if (sizeof(args) != 2)
{
notify("Usage: .program object:verb");
return;
}
if (sscanf(args[1], "%s:%s", obj, vname) != 2)
{
notify("You must specify a verb; " +
"use the format object:verb.");
return;
}
program(obj, vname);
return;
}
/* regular command; do parsing */
parsed = ({ args[1 ..], argstr }) + parse_cmd(args);
prefix();
lverb = tolower(verb);
if (player->command(lverb, verb, parsed, player, 0))
return;
room = player->get_location();
if (room && room->command(lverb, verb, parsed, player, 0))
return;
ob = MOOOBJ(parsed[C_DOBJ]);
if (ob && ob->command(lverb, verb, parsed, player, 0))
return;
ob = MOOOBJ(parsed[C_IOBJ]);
if (ob && ob->command(lverb, verb, parsed, player, 0))
return;
if (! room || ! do_huh(JS_INIT, room, verb, parsed))
notify(CONFIG->query(CF_HUH_FAILED_MSG));
}
/*
* NAME: resolved_name()
* DESCRIPTION: called to receive this connection's host name when it is known
*/
void resolved_name(string name)
{
ip_name = name;
}
/*
* NAME: init()
* DESCRIPTION: setup initial connection data
*/
static
void init(string msg)
{
time_connect = 0;
time_input = time();
ip_number = query_ip_number(this_object());
ip_name = DNS->get_name(ip_number, "resolved_name");
pfd = global->assign_pfd(this_object());
player_id = global->accept(this_object(), msg);
}
/*
* NAME: open()
* DESCRIPTION: called by DGD when connection is opened
*/
static
void open(void)
{
init("ACCEPT");
if (flags & F_BOOTSTRAP)
boot("*** The server is currently bootstrapping (try again later) ***");
else if (flags & F_BACKDOOR)
notify("*** Backdoor access for this connection enabled ***");
else
{
set_timelimit();
process_line("");
}
}
/*
* NAME: client_booted()
* DESCRIPTION: called by close() when the client has closed the connection
*/
void client_booted(JS_PROTO)
{
JS_BEGIN;
global->log_msg("CLIENT DISCONNECTED: " + description());
if (player && listener)
{
JS_PREP(1);
RET = listener->call_verb(JS_DATA(1), "user_client_disconnected",
server_vars(player_id, listener,
"user_client_disconnected",
0, OBJ(player_id)));
JS_END;
}
JS_END;
}
/*
* NAME: cancel_susp()
* DESCRIPTION: rid susp_data
*/
static
void cancel_susp(void)
{
if (susp_data)
call_out("do_read", 0, 0);
}
/*
* NAME: close()
* DESCRIPTION: called by DGD when connection is closed / object destructed
*/
static
void close(void)
{
cancel_susp();
call_out("destruct", 1);
if ((flags & F_LPCONLY) && input_to_obj)
input_to_obj->close();
if (! (flags & F_DESTRUCTING))
client_booted(JS_INIT);
}
/*
* NAME: get_connected_seconds()
* DESCRIPTION: return number of seconds this user has been connected
*/
int get_connected_seconds(void)
{
return time_connect ? time() - time_connect : -1;
}
/*
* NAME: get_idle_seconds()
* DESCRIPTION: return number of seconds this user has been idle
*/
int get_idle_seconds(void)
{
return time() - time_input;
}
/*
* NAME: set_port_number()
* DESCRIPTION: called to configure the remote TCP/IP port for this connection
*/
void set_port_number(int num)
{
port_number = num;
}
/*
* NAME: get_port_number()
* DESCRIPTION: return this connection's remote port number
*/
int get_port_number(void)
{
return port_number;
}