Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/secure/inetd.c b/secure/inetd.c
new file mode 100644
index 0000000..93b9145
--- /dev/null
+++ b/secure/inetd.c
@@ -0,0 +1,808 @@
+/*
+ * 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 2 days.
+//    if (tmp[HOST_STATUS] + 172800 < 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)
+ */
+mixed decode(string arg) {
+
+    if (sizeof(arg) && arg[0] == '$')
+        return arg[1..];
+#ifdef RESTRICTED_CASTS
+    if (to_string(to_int(arg)) == arg)
+        return to_int(arg);
+#else
+    if ((string)((int)arg) == arg)
+        return (int)arg;
+#endif
+    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 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) {
+    if (objectp(arg))
+        return object_name(arg);
+    if (stringp(arg) && sizeof(arg) &&
+        (arg[0] == '$' ||
+#ifdef RESTRICTED_CASTS
+    to_string(to_int(arg)) == (string)arg))
+#else
+    (string)to_int(arg) == (string)arg))
+#endif
+        return "$" + arg;
+    return to_string(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]]);
+        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]));
+      if (!send_udp(
+        host_data[HOST_IP], host_data[HOST_UDP_PORT], packet_arr[i]))
+            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);
+}
+