/
lib/banish/
lib/d/coronos/
lib/d/coronos/w/alena/
lib/d/coronos/w/angel/
lib/d/coronos/w/angel/caves/
lib/d/coronos/w/angel/caves/monst/
lib/d/coronos/w/angel/city/chambers/
lib/d/coronos/w/angel/city/monst/
lib/d/coronos/w/angel/city/obj/
lib/d/coronos/w/angel/city/streets/
lib/d/coronos/w/angel/farms/plains/
lib/d/coronos/w/angel/monst/
lib/d/tempus/
lib/d/tempus/w/angel/
lib/d/tempus/w/kingbill/
lib/d/tempus/w/mirak/
lib/d/tempus/w/mirak/monst/
lib/d/tempus/w/mirak/obj/
lib/d/tempus/w/relgar/planes/baat/
lib/d/tempus/w/sarak/
lib/d/tempus/w/serepion/mon/
lib/d/tempus/w/valrejn/
lib/doc/
lib/doc/domains/
lib/doc/efun/
lib/include/fn_specs/
lib/info/
lib/inherit/base/
lib/log/
lib/log/mailbox/
lib/log/main/
lib/news/
lib/obj/party/
lib/objects/componen/
lib/open/
lib/open/party/
lib/open/paste/
lib/open/spells/
lib/open/valrejn/
lib/players/
lib/players/alena/
lib/players/alena/obj/
lib/players/alena/open/
lib/players/alena/private/
lib/players/angel/
lib/players/angel/obj/
lib/players/ash/
lib/players/biggs/
lib/players/biggs/food/
lib/players/biggs/gobkeep/
lib/players/biggs/mnstr/
lib/players/biggs/town/caves/
lib/players/biggs/town/tower/
lib/players/biggs/wpns/
lib/players/calris/
lib/players/deathurg/
lib/players/deathurg/open/
lib/players/deathurg/private/thief/
lib/players/dogberry/
lib/players/dogberry/library/
lib/players/dogberry/open/
lib/players/epsilon/
lib/players/epsilon/private/
lib/players/farewell/
lib/players/hippo/
lib/players/hippo/open/
lib/players/hippo/tools/
lib/players/jimpa/
lib/players/josh/
lib/players/josh/room/
lib/players/josh/room/mage/dungeon/
lib/players/josh/room/mage/dungeon/obj/
lib/players/josh/wep/
lib/players/kingbill/
lib/players/metatron/
lib/players/miette/
lib/players/mirak/
lib/players/mirak/open/
lib/players/parsilan/
lib/players/relgar/
lib/players/relgar/private/
lib/players/sarak/
lib/players/sarak/bugs/
lib/players/sarak/feelings/
lib/players/sarak/magical/
lib/players/sarak/minotaur/island/
lib/players/sarak/open/
lib/players/sarak/private/
lib/players/serepion/
lib/players/serepion/open/
lib/players/serepion/private/
lib/players/spike/
lib/players/spike/open/
lib/players/spike/private/
lib/players/spike/seaworld/
lib/players/valrejn/
lib/players/valrejn/open/
lib/players/valrejn/private/
lib/players/virus/
lib/players/wrath/
lib/players/wrath/arm/
lib/players/wrath/mon/
lib/players/wrath/room/
lib/players/wrath/room/entry/
lib/players/wrath/room/zolgath/
lib/players/wrath/weap/
lib/players/zil/
lib/room/
lib/room/city/arena/
lib/room/city/creator/
lib/room/city/garden/monst/
lib/room/city/library/
lib/room/city/library/open/books/
lib/room/city/shop/
lib/room/death/
lib/room/death/open/
lib/room/island/
lib/room/keeps/
lib/room/registry/
lib/room/ships/crew/
lib/room/ships/open/
lib/room/ships/open/types/bounty/
lib/room/ships/open/types/nebula/
lib/room/ships/open/types/phoenix/
lib/secure/udp_cmd_/
lib/skills/
lib/skills/fighter/
lib/skills/psionici/
lib/skills/thief/
lib/usr/
lib/usr/creators/
lib/usr/no_banis/
lib/usr/players/
/*
 * UDP port handling code. Version 0.4.3
 * Written by Nostradamus for Zebedee.
 * Developed from an original concept by Alvin@Sushi.
 */

#include <master.cfg>
#include <udp.h>
#undef DATE

#ifdef ZEBEDEE
#include <defs.h>
#elif !defined(DATE)
#define DATE		ctime(time())[4..15]
#endif

#define MAX_PACKET_LEN	1024

#define UNKNOWN		0
#define UP		time()
#define DOWN		(-time())

#define DELIMITER	"|"

private mapping hosts, pending_data, incoming_packets;
private int packet_id;

void set_host_list();
varargs int send_udp(string mudname, mapping data, int expect_reply);

#ifdef DEBUG

void debug(string msg) {
    object ob;

    if (ob = find_player("nostradamus"))
	tell_object(ob, "Debug: " + msg);
}

#endif /* DEBUG */

#if !defined(COMPAT_FLAG) || defined(ZEBEDEE)
void create() {
#else
void reset(mixed arg) {
    if (arg)
	return;
#endif
    pending_data = ([ ]);
    incoming_packets = ([ ]);
    set_host_list();
}

void set_host_list() {
    mixed data;

    hosts = ([ ]);
    if (data = read_file(HOST_FILE)) {
	int i;

	data = explode(data, "\n") - ({ "" });
	for(i = sizeof(data); i--; ) {
	    if (data[i][0] == '#')
		continue;
	    data[i] = explode(data[i], ":");
	    hosts[lower_case(data[i][HOST_NAME])] = ({
		capitalize(data[i][HOST_NAME]),
		data[i][HOST_IP],
		to_int(data[i][HOST_UDP_PORT]),
		data[i][HOST_COMMANDS..],
		UNKNOWN
	    });
	    send_udp(data[i][HOST_NAME], ([ REQUEST: PING ]), 1);
	}
    }
}

private mixed decode_packet(string packet) {
    string *data;
    mapping ret;
    string class, info;
    int i;

    data = explode(packet, DELIMITER);
    if (data[0][0..strlen(PACKET)] == PACKET + ":") {
	int id, n;

	if (sscanf(data[0][strlen(PACKET)+1..],
	"%s:%d:%d/%d", class, id, i, n) != 4)
	    return 0;
	class = lower_case(class) + ":" + id;
	if (pointerp(incoming_packets[class])) {
	    incoming_packets[class][i-1] = packet[strlen(data[0])+1..];
	    if (member_array(0, incoming_packets[class]) == -1) {
		ret = decode_packet(implode(incoming_packets[class], ""));
		incoming_packets = m_delete(incoming_packets, class);
		return ret;
	    }
	} else {
	    incoming_packets[class] = allocate(n);
	    incoming_packets[class][i-1] = packet[strlen(data[0])+1..];
	    if (!pending_data[class])
		call_out("incoming_time_out", TIME_OUT, class);
	}
	return 1;
    }
    ret = ([ ]);
    for(i = 0; i < sizeof(data); i++) {
	if (sscanf(data[i], "%s:%s", class, info) != 2)
	    return 0;
	switch(class) {
	    case DATA:
		info = implode(data[i..], DELIMITER)[strlen(DATA)+1..];
		break;
	    case REQUEST:
	    case SENDER:
	    case RECIPIENT:
		info = lower_case(info);
		break;
	}
	if (info[0] == '$')
	    ret[class] = info[1..];
	else if ((string)(ret[class] = (int)info) != info)
	    ret[class] = info;
	if (class == DATA)
	    return ret;
    }
    return ret;
}

private int valid_request(mapping data) {
    mapping host_data;
    
    if (!data[NAME] || !(host_data = hosts[lower_case(data[NAME])])) {
	log_file(LOG_FILE, DATE + ": Unknown mud.\n");
	return 0;
    }
    if (data[HOST] != host_data[HOST_IP]) {
	log_file(LOG_FILE, DATE + ": Host mismatch.\n");
	return 0;
    }
    if (!data[REQUEST] ||
    (data[REQUEST] != REPLY && data[REQUEST] != PING &&
    member_array("*", host_data[HOST_COMMANDS]) == -1 &&
    member_array(data[REQUEST], host_data[HOST_COMMANDS]) == -1)) {
	log_file(LOG_FILE, DATE + ": Illegal command.\n");
	return 0;
    }
    return 1;
}

void receive_udp(string sender, string packet) {
    mapping data;
    string err;

#if 0
    if (!previous_object() ||
    file_name(previous_object()) != __MASTER_OBJECT__)
	return;
#endif
    if (!mappingp(data = decode_packet(packet))) {
	if (!data)
	    log_file(LOG_FILE, DATE + ": Received invalid packet.\nSender: " +
	    sender + "\nPacket:\n" + packet + "\n\n");
	return;
    }
    data[HOST] = sender;
    if (!valid_request(data)) {
	log_file(LOG_FILE, "Sender: " + sender + "\nPacket:\n" +
	packet + "\n\n");
	return;
    }
    switch(data[REQUEST]) {
	case PING:
	    hosts[lower_case(data[NAME])][HOST_STATUS] = UP;
	    send_udp(data[NAME], ([ REQUEST: REPLY, ID: data[ID] ]) );
	    break;
	case REPLY:
	    pending_data =
	    m_delete(pending_data, lower_case(data[NAME]) + ":" + data[ID]);
	    if (data[RECIPIENT]) {
		object ob;

		if (ob = find_player(data[RECIPIENT]))
		    tell_object(ob, "\n" + data[DATA]);
		else if (err = catch(
		data[RECIPIENT]->udp_reply(copy_mapping(data))))
		    log_file(LOG_FILE, DATE + ": Error in file: " +
		    data[RECIPIENT] + "\n" + err + "\n");
	    }
	    else if (data[DATA])
		log_file(LOG_FILE, DATE + ": Reply from " + data[NAME] +
		"\n" + data[DATA] + "\n\n");
	    break;
	default:
	{
	    int ret;

	    if ((err = catch(ret = call_other(
	    UDP_CMD_DIR + data[REQUEST], "udp_" + data[REQUEST],
	    copy_mapping(data)))) ||
	    !ret) {
		send_udp(data[NAME], ([
		    REQUEST: REPLY,
		    RECIPIENT: data[SENDER],
		    ID: data[ID],
		    DATA: "Root@" + LOCAL_NAME + ": " +
		    capitalize(data[REQUEST]) + " request failed.\n"
		]) );
		log_file(LOG_FILE, DATE + ": " +
		data[REQUEST] + " failed.\n" +
		(err ? err : "No error.") + "\n");
	    }
	    break;
	}
    }
    if (hosts[lower_case(data[NAME])])
	hosts[lower_case(data[NAME])][HOST_STATUS] = UP;
}

private int match_mud_name(string mudname, string match_str) {
    return mudname[0..strlen(match_str)-1] == match_str;
}

private string encode_packet(mapping data) {
    int i;
    mixed indices, tmp;
    string ret;

    for(i = sizeof(indices = m_indices(data)); i--; ) {
	if (indices[i] == DATA || !data[indices[i]])
	    continue;
	if (pointerp(tmp = data[indices[i]]))
	    tmp = file_name(tmp);
	else if (stringp(tmp) && (tmp[0] == '$' ||
	(string)to_int(tmp) == (string)tmp))
	    tmp = "$" + tmp;
	if (ret)
	    ret += DELIMITER + indices[i] + ":" + tmp;
	else
	    ret = indices[i] + ":" + tmp;
    }
    if (ret) {
	if (data[DATA])
	    ret += DELIMITER + DATA + ":" + data[DATA];
	return ret;
    }
}

string *explode_packet(string packet, int len) {
    if (strlen(packet) <= len)
	return ({ packet });
    return ({ packet[0..len-1] }) + explode_packet(packet[len..], len);
}

varargs int send_udp(string mudname, mapping data, int expect_reply) {
    mixed host_data;
    string *packet_arr;
    string packet;
    int i;
    string file;

    if(previous_object() 
    && sscanf(file_name(previous_object()),UDP_CMD_DIR+"%s",file) != 1) {
      return 0; /* cmd from illegal object */
    }
    mudname = lower_case(mudname);
    if (!(host_data = hosts[mudname])) {
	string *names;

	if (sizeof(names = filter_array(
	m_indices(hosts), "match_mud_name", this_object(), mudname)) == 1)
	    host_data = hosts[names[0]];
	else {
	    write("Unknown mud: " + mudname + "\n");
	    return 0;
	}
    }
    if (data[REQUEST] != PING && data[REQUEST] != REPLY &&
    member_array("*", host_data[HOST_COMMANDS]) == -1 &&
    member_array(data[REQUEST], host_data[HOST_COMMANDS]) == -1) {
	write(capitalize(data[REQUEST]) + ": Command unavailable @" +
	host_data[HOST_NAME] + "\n");
	return 0;
    }
    if (expect_reply) {
	/* Don't use zero. */
	packet_id++;
	pending_data[lower_case(host_data[HOST_NAME]) + ":" + packet_id] =
	data + ([ NAME: host_data[HOST_NAME] ]);
	call_out("reply_time_out", TIME_OUT,
	lower_case(host_data[HOST_NAME]) + ":" + packet_id);
	data[ID] = packet_id;
    }
    data += ([ NAME: LOCAL_NAME, UDP_PORT: LOCAL_UDP_PORT ]);
    if (!(packet = encode_packet(data))) {
	if (expect_reply)
	    pending_data = m_delete(pending_data,
	    lower_case(host_data[HOST_NAME]) + ":" + packet_id);
	write("inetd: Illegal packet.\n");
	log_file(LOG_FILE, DATE + ": Illegal packet sent by " +
	file_name(previous_object()) + "\n\n");
	return 0;
    }
    if (strlen(packet) <= MAX_PACKET_LEN)
	packet_arr = ({ packet });
    else {
	string header;
	int max;

	header = PACKET + ":" + lower_case(LOCAL_NAME) + ":" +
	(expect_reply ? packet_id : ++packet_id) + ":";
	packet_arr = explode_packet(packet,
	MAX_PACKET_LEN - (strlen(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--; ) {
	if (!send_imp(
	host_data[HOST_IP], (int)host_data[HOST_UDP_PORT], packet_arr[i])) {
	    write("inetd: Error in sending packet.\n");
	    return 0;
	}
    }
    return 1;
}

private void reply_time_out(mixed id) {
    mapping data;

    if (data = pending_data[id]) {
	if (data[REQUEST] != PING) {
	    object ob;

	    if (data[SENDER] && (ob = find_player(data[SENDER])))
		tell_object(ob, "\ninetd: " + capitalize(data[REQUEST]) +
		" request to " + (data[RECIPIENT] ?
		capitalize(data[RECIPIENT]) + "@" + data[NAME] : data[NAME]) +
		" timed out.\n");
	}
	if (hosts[lower_case(data[NAME])])
	    hosts[lower_case(data[NAME])][HOST_STATUS] = DOWN;
	/* Should this be outside the if() ? */
	incoming_packets =
	m_delete(incoming_packets, lower_case(data[NAME]) + ":" + id);
    }
    pending_data = m_delete(pending_data, id);
}

private incoming_time_out(string id) {
    incoming_packets = m_delete(incoming_packets, id);
}

mixed query(string what) {
    switch(what) {
	case "hosts":
	    return hosts;
	case "pending":
	    return pending_data;
	case "incoming":
	    return incoming_packets;
    }
}