blob: f0fd6a2fc9453aa2418d0bbe310e9d8ad9047a92 [file] [log] [blame]
/*
* UDP port handling code. Version 0.7a
* Written by Nostradamus for Zebedee.
* Developed from an original concept by Alvin@Sushi.
*/
#pragma strict_types
#pragma no_clone
#pragma no_shadow
#pragma no_inherit
#pragma verbose_errors
//#pragma pedantic
#pragma no_range_check
#pragma no_warn_deprecated
/*
#include <living/comm.h>
#define ZDEBUG(x) if (find_player("zesstra"))\
find_player("zesstra")->ReceiveMsg(x, \
MT_DEBUG|MSG_DONT_STORE,0,"IM: ",this_object())
*/
#define ZDEBUG(x)
#include <udp.h>
/* --- Configurable definitions. --- */
/* CD muds will probably need these include file. */
/* #include <std.h> */
/* #include "/config/sys/local.h" */
/* Public commands which will be accessible to any unlisted muds.
* PING, QUERY and REPLY are included by default. */
#define COMMANDS \
({ "channel", "finger", "tell", "who", "mail", "www", "htmlwho", "locate" })
//({ "channel", "finger", "ftp", "locate", "man", "tell", "who" })
/* Define this to the object that receives incoming packets and passes
* them to the inetd. Undefine for no _receive_udp() security.
* NOTE: The string must be of the format that object_name() returns. */
#define UDP_MASTER __MASTER_OBJECT__
/* How to set the euid for this object if using native mode.
* Ensure that it can read the INETD_HOSTS file. */
#define SET_EUID seteuid(getuid())
/* Define this if you are running another intermud package concurrently. */
/* #define RECEIVE_UDP_COMPAT(sender, packet) \
UDP_MANAGER->incoming_udp(sender, packet) // CD */
/* Define this if you are running another intermud package concurrently and
* have a compatability module for it. */
/* #define SEND_UDP_COMPAT(mudname, data, expect_reply) \
"/secure/udp_compat"->_send_udp(mudname, data, expect_reply) */
/* The maximum number of characters we can send in one packet.
* You may need to reduce this, but 512 should be safe. */
#define MAX_PACKET_LEN 1024
/* You shouldn't need to change anything below. */
#define USE_OLD_DELIMITER
//#define DELIMITER_COMPAT
#define USE_OLD_DATA_FORMAT
#ifdef ZEBEDEE
#include <defs.h>
#endif
#ifndef DATE
#define DATE ctime(time())[4..15]
#endif
/* --- End of Config. Do not alter anything below. --- */
#define UNKNOWN 0
#define UP time()
#define DOWN (-time())
#define NEW_DELIMITER "\n"
#ifdef USE_OLD_DELIMITER
#define DELIMITER "|"
#else
#define DELIMITER "\n"
#endif
#define OLD_DELIMITER "|"
#define HOSTFILE_DEL ":"
#define HOSTFILE_DEL2 ","
#define RETRY "_RETRY"
private nosave mapping hosts, pending_data, incoming_packets;
private nosave mapping static_host_list;
private nosave string *received_ids;
private nosave int packet_id;
void set_host_list();
varargs string _send_udp(string mudname, mapping data, int expect_reply);
#define DEBUG(msg)
void my_create() {
#ifndef COMPAT_FLAG
SET_EUID;
#endif
packet_id = 0;
pending_data = ([ ]);
incoming_packets = ([ ]);
hosts = ([ ]);
received_ids = ({ });
set_host_list();
if (!this_player())
call_out("startup", 1, 0);
}
#ifdef CREATE_FUN
CREATE_FUN() {
#elif !defined(COMPAT_FLAG) || defined(ZEBEDEE)
void create() {
#else
void reset(arg) {
if (arg)
return;
#endif
my_create();
}
status check_system_field(mapping data, string field) {
return data[SYSTEM] && member(data[SYSTEM], field) != -1;
}
mapping add_system_field(mapping data, string field) {
if (data[SYSTEM]) {
if (!check_system_field(data, field))
data[SYSTEM] += ({ field });
}
else
data[SYSTEM] = ({ field });
return data;
}
private mapping read_host_list(string file) {
mixed data = (read_file(file));
if (!data) {
log_file(INETD_LOG_FILE, "*Error in reading host file: "
+ file +"\n\n");
return 0;
}
data = old_explode(data, "\n");
mapping new_hosts = m_allocate(sizeof(data),1);
foreach(string line: data) {
if (line == "" || line[0] == '#')
continue;
mixed fields = old_explode(line, HOSTFILE_DEL);
if (sizeof(fields) < 5) {
log_file(INETD_LOG_FILE, sprintf(
"*Parse error in hosts file: %s, Line: %s\n", file, line));
continue;
}
string name = lower_case(fields[HOST_NAME]);
if (member(new_hosts, name))
continue; // mud already in list
string *local_cmds = old_explode(fields[LOCAL_COMMANDS],HOSTFILE_DEL2);
if (member(local_cmds,"*") != -1)
local_cmds = local_cmds - ({ "*" }) + COMMANDS;
new_hosts[name] = ({ capitalize(fields[HOST_NAME]),
fields[HOST_IP],
to_int(fields[HOST_UDP_PORT]),
local_cmds,
old_explode(fields[HOST_COMMANDS], HOSTFILE_DEL2),
UNKNOWN
});
/*
* Get existing host status from current active host lost as long as the
* IP and UDP ports are the same.
*/
if (hosts[name] &&
hosts[name][HOST_IP] == new_hosts[name][HOST_IP] &&
hosts[name][HOST_UDP_PORT] == new_hosts[name][HOST_UDP_PORT])
new_hosts[name][HOST_STATUS] = hosts[name][HOST_STATUS];
}
return new_hosts;
}
/*
* Read the INETD_HOSTS file and set the "hosts" mapping from it.
* Retain existing HOST_STATUS fields.
* Reads INETD_HOSTSS and INETD_HOSTS.dump
* Hosts from INETD_HOSTS are included in the static host list.
*/
void set_host_list() {
// read the static host file and the last host file dump.
mapping static_hosts = read_host_list(HOST_FILE);
if (!static_hosts)
return; // retain the old list(s)
// remember the static hosts
static_host_list = m_reallocate(static_hosts,0);
// read the last host file dump and add the static hosts. Then the static
// hosts have precedence over the ones from the dump.
hosts = (read_host_list(HOST_FILE".dump") || ([])) + static_hosts;
}
mixed get_hosts() {return copy(hosts);}
int dump_hosts_list() {
write_file(HOST_FILE+".dump", sprintf(
"#Hostlist dump from "MUDNAME", created %s\n", strftime()), 1);
foreach(string mudname, mixed tmp : hosts) {
// skip muds we did not hear from for 7 days.
if (tmp[HOST_STATUS] + 604800 < time())
continue;
write_file(HOST_FILE+".dump",
sprintf("%s:%s:%d:%s:%s\n",tmp[0],tmp[1],tmp[2],implode(tmp[3],","),
implode(tmp[4],",")));
}
return 1;
}
/*
* Make a PING request to all muds in the "hosts" mapping to set
* HOST_STATUS information.
* But don't ping all muds at once, because that may overflow the callout
* table during mud startup, when hundreds of objects make callouts.
*/
void startup(string *muds) {
if (!pointerp(muds))
muds=m_indices(hosts);
if (!sizeof(muds))
return;
string *part;
if (sizeof(muds) > 9)
part=muds[0..9];
else
part=muds;
foreach(string mud: part)
_send_udp(mud, ([ REQUEST: PING ]), 1);
muds -= part;
if (sizeof(muds))
call_out(#'startup, 4, muds);
}
/*
* Remove a buffered packet from the "incoming_packets" mapping.
*/
void remove_incoming(string id) {
m_delete(incoming_packets, id);
}
/*
* Decode a string from a UDP packet.
* Returns: The actual value of the argument (either int or string)
*/
string|int decode(string arg)
{
if (sizeof(arg) && arg[0] == '$')
return arg[1..];
else if ((arg & "0123456789") == arg)
return to_int(arg);
return arg;
}
/*
* Decode a UDP packet.
* Arguments: UDP packet as a string.
* Returns: The decoded information as a mapping, 1 for succes but no
* output (buffered packet), or 0 on error.
*/
mixed decode_packet(string packet, string delimiter) {
string *data;
mapping ret;
string info, tmp;
mixed class;
int i, id, n;
/* If this packet has been split, handle buffering. */
if (packet[0..sizeof(PACKET)] == PACKET + ":") {
if (sscanf(packet, PACKET + ":%s:%d:%d/%d" + delimiter + "%s",
class, id, i, n, tmp) != 5)
return 0;
class = lower_case(class) + ":" + id;
if (pointerp(incoming_packets[class])) {
incoming_packets[class][i-1] = tmp;
if (member(incoming_packets[class], 0) == -1) {
ret =
decode_packet(implode(incoming_packets[class], ""), delimiter);
remove_incoming(class);
return ret;
}
} else {
incoming_packets[class] = allocate(n);
incoming_packets[class][i-1] = tmp;
/* Is it possible to already have a timeout running here ?!? */
/* If no timeout is running then start one. */
if (!pending_data[class]) {
call_out("remove_incoming",
REPLY_TIME_OUT + REPLY_TIME_OUT * RETRIES, class);
} else {
DEBUG("\nINETD: *** Buffered packet Timeout already running! ***\n");
}
}
return 1;
}
ret = ([ ]);
for(i = 0, n = sizeof(data = old_explode(packet, delimiter)); i < n; i++) {
/* DATA fields can be denoted by a preceeding blank field. */
if (data[i] == "") {
tmp = DATA;
/* Test for illegal packet length (no DATA) */
if (++i >= n)
return 0;
info = data[i];
}
else if (sscanf(data[i], "%s:%s", tmp, info) != 2)
return 0;
switch((string)(class = decode(tmp))) {
case DATA:
#ifdef USE_EXTRACT
return ret + ([ DATA: decode(implode(
({ info }) + extract(data, i+1), delimiter)) ]);
#else
return ret + ([ DATA: decode(implode(
({ info }) + data[i+1..], delimiter)) ]);
#endif
case SYSTEM:
ret[class] = old_explode(info, ":");
continue;
default:
ret[class] = decode(info);
continue;
}
}
return ret;
}
/* Check wether a UDP packet was valid.
* Logs are made and "host" information is updated as appropriate.
* Arguments: Decoded UDP packet (mapping)
* Returns: 0 for valid packets, an error string otherwise.
*/
string valid_request(mapping data) {
mixed host_data;
string *muds;
string req;
int i;
if (!data[NAME] || !data[UDP_PORT])
return DATE + ": Illegal packet.\n";
if (host_data = hosts[lower_case(data[NAME])]) {
if (data[HOST] != host_data[HOST_IP]) {
if (data[NAME] == LOCAL_NAME)
return DATE + ": *** FAKE MUD ***\n";
log_file(INETD_LOG_FILE, DATE + ": Host change:\n" +
host_data[HOST_NAME] + ": " + host_data[HOST_IP] + " -> " +
data[HOST] + "\n\n");
host_data[HOST_IP] = data[HOST];
}
if (data[UDP_PORT] != host_data[HOST_UDP_PORT]) {
if (data[NAME] == LOCAL_NAME)
return DATE + ": *** FAKE MUD ***\n";
log_file(INETD_LOG_FILE, DATE + ": Port change:\n" +
host_data[HOST_NAME] + " (" + host_data[HOST_IP] + "): " +
host_data[HOST_UDP_PORT] + " -> " + data[UDP_PORT] + "\n\n");
host_data[HOST_UDP_PORT] = data[UDP_PORT];
}
} else {
if (lower_case(data[NAME]) == lower_case(LOCAL_NAME))
return DATE + ": *** FAKE MUD ***\n";
for(i = sizeof(muds = m_indices(hosts)); i--; ) {
host_data = hosts[muds[i]];
if (data[HOST] == host_data[HOST_IP] &&
data[UDP_PORT] == host_data[HOST_UDP_PORT]) {
log_file(INETD_LOG_FILE, DATE + ": Name change:\n" +
host_data[HOST_NAME] + " (" + host_data[HOST_IP] +
") -> " + data[NAME] + "\n\n");
host_data[HOST_NAME] = data[NAME];
hosts[lower_case(data[NAME])] = host_data;
m_delete(hosts, muds[i]);
i = -2;
break;
}
}
if (i != -2) {
host_data = hosts[lower_case(data[NAME])] = ({
data[NAME],
data[HOST],
data[UDP_PORT],
COMMANDS,
({ "*" }),
UP
});
log_file(INETD_LOG_FILE, DATE + ": New mud.\n" + data[NAME] + ":" +
data[HOST] + ":" + data[UDP_PORT] + "\n\n");
}
}
if (!(req = data[REQUEST]))
return DATE + ": System message.\n";
if (req != PING &&
req != QUERY &&
req != REPLY &&
member(host_data[LOCAL_COMMANDS], req) == -1)
{
/* This should probably send a system message too. */
_send_udp(host_data[HOST_NAME], ([
REQUEST: REPLY,
RECIPIENT: data[SENDER],
ID: data[ID],
DATA: "Invalid request @" + LOCAL_NAME + ": " +
capitalize(data[REQUEST]) + "\n"
]) );
return DATE + ": Invalid request.\n";
}
return 0;
}
/*
* Incoming UDP packets are sent to this function to be interpretted.
* The packet is decoded, checked for validity, HOST_STATUS is updated
* and the appropriate udp module called.
* Arguments: Senders IP address (string)
* UDP packet (string)
*/
void _receive_udp(string sender, string packet) {
mapping|int data;
string req, err, id;
#ifdef UDP_MASTER
if (!previous_object() ||
object_name(previous_object()) != UDP_MASTER) {
log_file(INETD_LOG_FILE, DATE + ": Illegal call of _receive_udp() by " +
object_name(previous_object()) + "\n\n");
return;
}
#endif
ZDEBUG(sprintf("%O -> %.500O\n",sender, packet));
if (
#ifdef DELIMITER_COMPAT
#ifdef USE_EXTRACT
(!(data = decode_packet(packet, NEW_DELIMITER))
#else
(!(data = decode_packet(packet, NEW_DELIMITER))
#endif
|| (data[HOST] = sender) && (err = valid_request(data))) &&
(!(data = decode_packet(packet, OLD_DELIMITER)) ||
(data[HOST] = sender) && (err = valid_request(data)))
#else
#ifdef USE_EXTRACT
!(data = decode_packet(packet, DELIMITER))
#else
!(data = decode_packet(packet, DELIMITER))
#endif
#endif
) {
if (!data)
#ifdef RECEIVE_UDP_COMPAT
RECEIVE_UDP_COMPAT(sender, packet);
#else
log_file(INETD_LOG_FILE, DATE + ": Received invalid packet.\nSender: " +
sender + "\nPacket:\n" + packet + "\n\n");
#endif
return;
}
#ifdef DELIMITER_COMPAT
if (!mappingp(data))
return;
if (err)
#else
if (!mappingp(data))
return;
data[HOST] = sender;
if (err = valid_request(data))
#endif
{
log_file(INETD_LOG_FILE, err + "Sender: " + sender + "\nPacket:\n" +
packet + "\n\n");
return;
}
hosts[lower_case(data[NAME])][HOST_STATUS] = UP;
if ((req = data[REQUEST]) == REPLY) {
mapping pending;
/* If we can't find the reply in the pending list then bin it. */
if (!(pending = pending_data[lower_case(data[NAME]) + ":" + data[ID]]))
return;
/* Set data[REQUEST] correctly, but still send to (req = REPLY) */
data[REQUEST] = pending[REQUEST];
#ifdef INETD_DIAGNOSTICS
data[PACKET_LOSS] = pending[PACKET_LOSS];
data[RESPONSE_TIME] = time() - pending[RESPONSE_TIME] + 1;
#endif
#if 0
/* channel replies may not include a recipient, and shouldn't have one set */
/* Restore the RECIPIENT in replies if none given and it is known. */
if (!data[RECIPIENT] && pending[SENDER])
data[RECIPIENT] = pending[SENDER];
#endif
m_delete(pending_data, lower_case(data[NAME]) + ":" + data[ID]);
}
else if (data[ID]) {
id = lower_case(data[NAME]) + ":" + data[ID];
if (member(received_ids, id) == -1)
{
received_ids += ({ id });
call_out("remove_received_id",
REPLY_TIME_OUT + REPLY_TIME_OUT * (RETRIES + 1), id);
}
else
add_system_field(data, REPEAT);
}
if (err = catch(
call_other(UDP_CMD_DIR + req, "udp_" + req, deep_copy(data));publish))
{
_send_udp(data[NAME], ([
REQUEST: REPLY,
RECIPIENT: data[SENDER],
ID: data[ID],
DATA: capitalize(req)+ " request failed @" + LOCAL_NAME + ".\n"
]) );
log_file(INETD_LOG_FILE, DATE + ": " + data[REQUEST] + " from " +
data[NAME] + " failed.\n" + err + packet + "\n\n");
}
}
int do_match(string mudname, string match_str) {
return mudname[0..sizeof(match_str)-1] == match_str;
}
#ifdef NO_CLOSURES
status greater(mixed a, mixed b) {
return a > b;
}
string *expand_mud_name(string name) {
return sort_array(
filter(m_indices(hosts), "do_match", this_object(), name),
"greater",
this_object()
);
}
#else
string *expand_mud_name(string name) {
return sort_array(
filter(m_indices(hosts), #'do_match, name),
#'>
);
}
#endif
string encode(mixed arg) {
// Objektnamen mit nicht-ASCII hier ist in allen Faellen doof, wenn sie
// koennen auch mit Transliteration nicht mehr verlustfrei zurueck
// konvertiert werden...
if (objectp(arg))
arg = object_name(arg);
else if (intp(arg))
arg = to_string(arg);
else
{
if (!stringp(arg))
arg = to_string(arg);
// faengt es mit $ an oder ist es ne zahl (als string, d.h. enthaelt nur
// 0-9)? Dann muss nen $ davor.
// TODO: Mhmm. Oder vielleicht doch per RegExp?
if ( (arg[0] == '$') || ((arg & "0123456789") == arg))
arg = "$" + arg;
}
return arg;
}
string encode_packet(mapping data) {
int i;
mixed indices;
string header, body, t1, t2;
string *ret;
status data_flag;
for(ret = ({ }), i = sizeof(indices = m_indices(data)); i--; ) {
if (indices[i] == DATA) {
data_flag = 1;
continue;
}
header = encode(indices[i]);
body = encode(data[indices[i]]);
// Eigentlich sollte encode auch die Wandlung in bytes uebernehmen und
// dabei nach ASCII wandeln und ggf. transliterieren. Aber sscanf
// braucht wieder strings, daher muss es spaeter passieren (direkt vor
// Senden).
if (sscanf(header, "%s:%s", t1, t2) ||
sscanf(header + body, "%s" + DELIMITER + "%s", t1, t2)
)
return 0;
ret += ({ header + ":" + body });
}
if (data_flag)
#ifdef USE_OLD_DATA_FORMAT
ret += ({ DATA + ":" + encode(data[DATA]) });
#else
ret += ({ "", encode(data[DATA]) });
#endif
return implode(ret, DELIMITER);
}
// Funktion explode_
// Die Funktion zerlegt den String packet in gleich lange Teilstrings
// der Laenge len und gibt die Teilstrings als Array zurueck. Der letzte
// Teilstring kann kuerzer sein als die anderen.
string *explode_packet(string packet, int len) {
int ptr, m_ptr,size;
string *result;
// Variablen initialisieren
m_ptr=ptr=0;
size=sizeof(packet);
result=({});
// Um Arrayadditionen zu vermeiden wird vorher allokiert. Die Division
// durch 0 ist nicht abgefangen, da bei len=0 was im Aufruf falch ist.
result=allocate((size/len+1));
while (ptr<size) {
result[m_ptr] = // Aktuellen Teilstring speichern
#ifdef USE_EXTRACT
extract(packet,ptr,ptr+len-1);
#else
packet[ptr..ptr+len-1];
#endif
ptr+=len;// Neuen Pointer im String berechnen
m_ptr++; // Neuen Pointer im Mapping berechnen. Hier nutze ich eine
// Variable mehr als noetig, weil das billiger ist, als jedes
// mal ptr=m_ptr*len auszurechnen. Lieber zwei Additionen als
// eine Multiplikation und eine Addtion.
}
// Wenn size/len aufgeht, ist das Result-Feld zu gross. Dann bleibt
// ein Element leer, das wird hier gestrippt. Das ist billiger als
// jedesmal auszurechnen, ob size/len aufgeht.
return result-({0});
}
varargs string _send_udp(string mudname, mapping data, int expect_reply) {
mixed host_data;
string *packet_arr;
string packet;
int i;
mudname = lower_case(mudname);
if (!(host_data = hosts[mudname])) {
string *names;
if (sizeof(names = expand_mud_name(mudname)) == 1)
host_data = hosts[mudname = names[0]];
else
#ifdef SEND_UDP_COMPAT
return (string)SEND_UDP_COMPAT(mudname, data, expect_reply);
#else
if (!sizeof(names))
return "Unbekannter Mudname: " + capitalize(mudname) + "\n";
else
return break_string("Mudname ("+capitalize(mudname)+
") nicht eindeutig, es passen: "+implode(map(names,#'capitalize),", ")+
".\n",78);
#endif
}
if (data[REQUEST] != PING &&
data[REQUEST] != QUERY &&
data[REQUEST] != REPLY &&
member(host_data[HOST_COMMANDS], "*") == -1 &&
member(host_data[HOST_COMMANDS], data[REQUEST]) == -1)
return capitalize(data[REQUEST]) + ": Command unavailable @" +
host_data[HOST_NAME] + "\n";
data[NAME] = LOCAL_NAME;
data[UDP_PORT] = LOCAL_UDP_PORT;
if (expect_reply) {
/* Don't use zero. */
data[ID] = ++packet_id;
/* Don't need deep_copy() as we are changing the mapping size. */
pending_data[mudname + ":" + packet_id] =
#ifdef INETD_DIAGNOSTICS
data + ([ NAME: host_data[HOST_NAME], RESPONSE_TIME: time() ]);
#else
data + ([ NAME: host_data[HOST_NAME] ]);
#endif
}
if (!(packet = encode_packet(data))) {
if (expect_reply)
pending_data = m_copy_delete(pending_data, mudname + ":" + packet_id);
log_file(INETD_LOG_FILE, DATE + ": Illegal packet sent by " +
object_name(previous_object()) + "\n\n");
return "inetd: Illegal packet.\n";
}
if (expect_reply)
call_out("reply_time_out", REPLY_TIME_OUT, mudname + ":" + packet_id);
if (sizeof(packet) <= MAX_PACKET_LEN)
packet_arr = ({ packet });
else {
string header;
int max;
/* Be careful with the ID. data[ID] could have been set up by RETRY */
header =
PACKET + ":" + lower_case(LOCAL_NAME) + ":" +
((expect_reply || data[REQUEST] != REPLY)&& data[ID] ?
data[ID] : ++packet_id) + ":";
/* Allow 8 extra chars: 3 digits + "/" + 3 digits + DELIMITER */
packet_arr = explode_packet(packet,
MAX_PACKET_LEN - (sizeof(header) + 8));
for(i = max = sizeof(packet_arr); i--; )
packet_arr[i] =
header + (i+1) + "/" + max + DELIMITER + packet_arr[i];
}
for(i = sizeof(packet_arr); i--; )
{
ZDEBUG(sprintf("%O <- %.500O\n",host_data[HOST_IP], packet_arr[i]));
// Transliterationen koennten in : oder DELIMETER
// resultieren, weswegen das (hier) nicht geht. Daher bleibt nur
// uebrig, alles nicht-ASCII wegzuwerfen.
if (!send_udp(host_data[HOST_IP], host_data[HOST_UDP_PORT],
to_bytes(packet_arr[i], "ASCII")))
return "inetd: Error in sending packet.\n";
}
return 0;
}
void reply_time_out(string id) {
mapping data;
if (data = pending_data[id]) {
object ob;
#ifdef INETD_DIAGNOSTICS
data[PACKET_LOSS]++;
#endif
if (data[RETRY] < RETRIES) {
mapping send;
data[RETRY]++;
/* We must use a copy so the NAME field in pending_data[id]
* isn't corrupted by _send_udp(). */
send = deep_copy(data);
send = m_copy_delete(send, RETRY);
#ifdef INETD_DIAGNOSTICS
send = m_copy_delete(send, PACKET_LOSS);
send = m_copy_delete(send, RESPONSE_TIME);
#endif
call_out("reply_time_out", REPLY_TIME_OUT, id);
_send_udp(data[NAME], send);
return;
}
data = m_copy_delete(data, RETRY);
#ifdef INETD_DIAGNOSTICS
data = m_copy_delete(data, RESPONSE_TIME);
#endif
catch(call_other(UDP_CMD_DIR + REPLY, "udp_" + REPLY,
add_system_field(data, TIME_OUT));publish);
/* It's just possible this was removed from the host list. */
if (hosts[lower_case(data[NAME])])
hosts[lower_case(data[NAME])][HOST_STATUS] = DOWN;
remove_incoming(lower_case(data[NAME]) + ":" + id);
}
pending_data = m_copy_delete(pending_data, id);
}
void remove_received_id(string id) {
received_ids -= ({ id });
}
varargs mixed query(string what, mixed extra1, mixed extra2) {
mixed data;
switch(what) {
case "commands":
return COMMANDS;
case "hosts":
return deep_copy(hosts);
case "pending":
return deep_copy(pending_data);
case "incoming":
return deep_copy(incoming_packets);
case "received":
return ({ }) + received_ids;
/* args: "valid_request", request, mudname */
case "valid_request":
if (data = hosts[extra2])
return member(data[HOST_COMMANDS], "*") != -1 ||
member(data[HOST_COMMANDS], extra1) != -1;
return 0;
}
return(0);
}