/
lib/banish/
lib/d/
lib/doc/
lib/doc/domains/
lib/doc/efun/
lib/doc/examples/
lib/doc/examples/armour/
lib/doc/examples/contain/
lib/doc/examples/food/
lib/doc/examples/magic/
lib/doc/examples/monster/
lib/doc/examples/room/
lib/doc/examples/weapons/
lib/function/
lib/include/
lib/include/fn_specs/
lib/include/skills/
lib/info/
lib/inherit/base/
lib/log/
lib/manuals/312/
lib/news/
lib/obj/party/
lib/objects/components/
lib/open/
lib/open/library/
lib/open/party/
lib/players/
lib/players/zilanthius/
lib/room/
lib/room/city/arena/
lib/room/city/creator/
lib/room/city/garden/monst/
lib/room/city/obj/
lib/room/city/shop/
lib/room/death/
lib/room/registry/
lib/secure/
lib/secure/UDP_CMD_DIR/
lib/skills/
lib/skills/fighter/
lib/skills/thief/
lib/usr/
lib/usr/creators/
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;
    }
}