lima-1.0b5/
lima-1.0b5/driver/
lima-1.0b5/driver/ChangeLog.old/
lima-1.0b5/driver/Win32/
lima-1.0b5/driver/compat/
lima-1.0b5/driver/include/
lima-1.0b5/driver/testsuite/
lima-1.0b5/driver/testsuite/clone/
lima-1.0b5/driver/testsuite/command/
lima-1.0b5/driver/testsuite/data/
lima-1.0b5/driver/testsuite/etc/
lima-1.0b5/driver/testsuite/include/
lima-1.0b5/driver/testsuite/inherit/
lima-1.0b5/driver/testsuite/inherit/master/
lima-1.0b5/driver/testsuite/log/
lima-1.0b5/driver/testsuite/single/
lima-1.0b5/driver/testsuite/single/tests/compiler/
lima-1.0b5/driver/testsuite/single/tests/efuns/
lima-1.0b5/driver/testsuite/single/tests/operators/
lima-1.0b5/driver/testsuite/u/
lima-1.0b5/driver/tmp/
lima-1.0b5/etc/
lima-1.0b5/lib/WWW/help/
lima-1.0b5/lib/cmds/
lima-1.0b5/lib/cmds/create/
lima-1.0b5/lib/cmds/player/attic/
lima-1.0b5/lib/contrib/bboard/
lima-1.0b5/lib/contrib/boards/
lima-1.0b5/lib/contrib/marriage/
lima-1.0b5/lib/contrib/roommaker/
lima-1.0b5/lib/contrib/transient_effect/
lima-1.0b5/lib/daemons/channel/
lima-1.0b5/lib/daemons/imud/
lima-1.0b5/lib/data/
lima-1.0b5/lib/data/config/
lima-1.0b5/lib/data/links/
lima-1.0b5/lib/data/news/
lima-1.0b5/lib/data/players/
lima-1.0b5/lib/data/secure/
lima-1.0b5/lib/domains/
lima-1.0b5/lib/domains/std/2.4.5/maze1/
lima-1.0b5/lib/domains/std/2.4.5/npc/
lima-1.0b5/lib/domains/std/2.4.5/post_dir/
lima-1.0b5/lib/domains/std/2.4.5/sub/
lima-1.0b5/lib/domains/std/camera/
lima-1.0b5/lib/domains/std/config/
lima-1.0b5/lib/domains/std/cult/
lima-1.0b5/lib/domains/std/effects/
lima-1.0b5/lib/domains/std/misc/
lima-1.0b5/lib/domains/std/monsters/
lima-1.0b5/lib/domains/std/recorder/
lima-1.0b5/lib/domains/std/rooms/
lima-1.0b5/lib/domains/std/rooms/beach/
lima-1.0b5/lib/domains/std/rooms/labyrinth/
lima-1.0b5/lib/domains/std/school/
lima-1.0b5/lib/domains/std/school/O/
lima-1.0b5/lib/domains/std/spells/
lima-1.0b5/lib/domains/std/spells/stock-mage/
lima-1.0b5/lib/domains/std/spells/stock-priest/
lima-1.0b5/lib/help/
lima-1.0b5/lib/help/admin/
lima-1.0b5/lib/help/hints/General_Questions/
lima-1.0b5/lib/help/hints/Pirate_Quest/
lima-1.0b5/lib/help/player/
lima-1.0b5/lib/help/player/bin/
lima-1.0b5/lib/help/player/quests/
lima-1.0b5/lib/help/wizard/
lima-1.0b5/lib/help/wizard/coding/guilds/
lima-1.0b5/lib/help/wizard/coding/rooms/
lima-1.0b5/lib/help/wizard/lib/daemons/
lima-1.0b5/lib/help/wizard/lib/lfun/
lima-1.0b5/lib/help/wizard/lib/std/
lima-1.0b5/lib/help/wizard/mudos_doc/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/interactive/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/parsing/
lima-1.0b5/lib/help/wizard/mudos_doc/concepts/
lima-1.0b5/lib/help/wizard/mudos_doc/driver/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/arrays/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/buffers/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/compile/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/filesystem/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/floats/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/functions/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/general/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/mappings/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/mixed/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/numbers/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/parsing/
lima-1.0b5/lib/help/wizard/mudos_doc/lpc/constructs/
lima-1.0b5/lib/help/wizard/mudos_doc/lpc/types/
lima-1.0b5/lib/include/driver/
lima-1.0b5/lib/log/
lima-1.0b5/lib/obj/admtool/
lima-1.0b5/lib/obj/admtool/internal/
lima-1.0b5/lib/obj/admtool/mudinfo/
lima-1.0b5/lib/obj/admtool/secure/
lima-1.0b5/lib/obj/secure/
lima-1.0b5/lib/obj/secure/cmd/
lima-1.0b5/lib/obj/secure/mailers/
lima-1.0b5/lib/obj/secure/shell/
lima-1.0b5/lib/obj/secure/shell/classes/
lima-1.0b5/lib/obj/tasktool/
lima-1.0b5/lib/obj/tasktool/internal/
lima-1.0b5/lib/open/
lima-1.0b5/lib/secure/
lima-1.0b5/lib/secure/cgi/
lima-1.0b5/lib/secure/modules/
lima-1.0b5/lib/secure/simul_efun/
lima-1.0b5/lib/std/adversary/
lima-1.0b5/lib/std/adversary/advancement/
lima-1.0b5/lib/std/adversary/armor/
lima-1.0b5/lib/std/adversary/blows/
lima-1.0b5/lib/std/adversary/death/
lima-1.0b5/lib/std/adversary/formula/
lima-1.0b5/lib/std/adversary/health/
lima-1.0b5/lib/std/adversary/pulse/
lima-1.0b5/lib/std/adversary/wield/
lima-1.0b5/lib/std/classes/event_info/
lima-1.0b5/lib/std/container/
lima-1.0b5/lib/std/living/
lima-1.0b5/lib/std/modules/contrib/
lima-1.0b5/lib/std/patterns/
lima-1.0b5/lib/std/race/
lima-1.0b5/lib/std/race/restricted/
lima-1.0b5/lib/std/room/
lima-1.0b5/lib/tmp/
lima-1.0b5/lib/trans/
lima-1.0b5/lib/trans/admincmds/
lima-1.0b5/lib/trans/obj/
lima-1.0b5/lib/wiz/
/* Do not remove the headers from this file! see /USAGE for more info. */

/*
** socket.c
**
** This object represents an open UDP/TCP socket using the MudOS
** socket facilities.
**
** 09-Feb-95. Deathblade. Created.
** 05-Jan-96. Cowl. Added STREAM BINARY connect and listen styles
** 12-Jul-96. Rust. Added write callback.
*/

#include <mudlib.h>
#include <socket.h>
#include <driver/socket_err.h>
#include <log.h>

//#define SKTLOG(x,y)	write_file("/open/sktlog",sprintf("%s: %O\n",x,y))
#define SKTLOG(x,y)

/*
** If this is defined, then the specified privilege is needed to create
** an outbound connection
*/
#define REQUIRE_PRIV	"Mudlib:socket"

nosave private int	style;
nosave private int	fdOwned = -1;	/* no socket yet */
nosave private function	read_func;
nosave private function	close_func;
nosave private function write_func;

nosave private mixed *	write_queue = ({ });
nosave private int	blocked;

/* For debug purposes only */
nosave private mixed addr;

void set_write_callback(function f)
{
    write_func = f;
}

int stat_me()
{
    switch ( style )
    {
    case SKT_STYLE_LISTEN:
	printf("%O: listening at %O\n", this_object(), addr);
	printf("    read_func=%O  close_func=%O\n", read_func, close_func);
	break;

    case SKT_STYLE_CONNECT:
	printf("%O: connected to %O\n", this_object(), socket_address(fdOwned));
	printf("    read_func=%O  close_func=%O\n", read_func, close_func);
	break;

    case SKT_STYLE_UDP:
	printf("%O: UDP at %O\n", this_object(), socket_address(fdOwned));
	printf("    read_func=%O\n", read_func);
	break;

    case SKT_STYLE_LISTEN_M:
	printf("%O: (mud) listening at %O\n", this_object(), addr);
	printf("    read_func=%O  close_func=%O\n", read_func, close_func);
	break;

    case SKT_STYLE_CONNECT_M:
	printf("%O: (mud) connected to %O\n", this_object(), socket_address(fdOwned));
	printf("    read_func=%O  close_func=%O\n", read_func, close_func);
	break;

    case SKT_STYLE_INT_ACQUIRE:
	printf("%O: accepted connection from %s\n", this_object(),
	       socket_address(fdOwned));
	printf("    read_func=%O  close_func=%O\n", read_func, close_func);
	break;
    }

    if ( sizeof(write_queue) )
	printf("queue: %O\n", write_queue);

    return 1;
}


//### socket_connect() doesn't take funcptr yet...
/* private */ protected nomask void read_callback(int fd, mixed message)
{
SKTLOG("read_callback: self",this_object());
SKTLOG("read_callback: fd",fd);
    catch(evaluate(read_func, this_object(), message));
}

private nomask void read_udp_callback(int fd, mixed message, string address)
{
SKTLOG("read_udp_callback: self",this_object());
SKTLOG("read_udp_callback: fd",fd);
SKTLOG("read_udp_callback: read_func",read_func);
    catch(evaluate(read_func, this_object(), message, address));
}

private nomask void close_callback(int fd)
{
SKTLOG("close_callback: self",this_object());
SKTLOG("close_callback: fd",fd);
SKTLOG("close_callback: close_func",close_func);

    /* this descriptor is closed. don't try to close again. */
    fdOwned = -1;

    if ( close_func )
    {
	catch(evaluate(close_func, this_object()));
    }

    destruct();
}

//### socket_connect() doesn't take a funcptr yet
/* private */ protected nomask void write_callback(int fd)
{
    mixed tmp;
SKTLOG("write_callback: self",this_object());
SKTLOG("write_callback: fd",fd);
SKTLOG("write_callback: # elem",sizeof(write_queue));

    /*
    ** No longer blocked (can accept new data).
    */
    if ( !sizeof(write_queue) && write_func && blocked )
    {
//        write_queue = ({ evaluate(write_func, this_object()) });
        evaluate(write_func, this_object());
    }

    blocked = 0;

    while ( sizeof(write_queue) > 0 )
    {
	int err;

	err = socket_write(fd, write_queue[0]);

	if ( err == EEALREADY )
	{
            // write_callback will get called automatically.
            blocked = 1;
	    return;
	}
        if ( err == EEWOULDBLOCK )
        {
            // write_callback needs to get called manually.
            blocked = 1;
            call_out("write_callback",1,fd);
            return;
        }

	/*
	** Remove the item from the queue.  It has been written.
	*/
	write_queue = write_queue[1..];

	if ( err == EECALLBACK )
	{
	    /* done for now... wait for the next callback */
	    blocked = 1;
	    return;
	}
	if ( err < 0 )
	{
	    error("could not write: " + socket_error(err) + "\n");
	}
	else if ( write_func )
	{
            tmp = evaluate(write_func, this_object());
            if ( sizeof(tmp) )
		write_queue += ({ tmp });
	}
    }
}

/* private */ nomask void release_callback(int fdToAcquire)
{
    int err;

SKTLOG("release_callback: self",this_object());
    fdOwned = fdToAcquire;
SKTLOG("release_callback: fdOwned",fdOwned);
    err = socket_acquire(fdOwned,
			 (: read_callback :),
			 (: write_callback :),
			 (: close_callback :));
SKTLOG("release_callback: err",err);

    if ( err < 0 )
	error("could not release: " + socket_error(err) + "\n");

    /*
    ** Deliver a 0 indicating a new connection (and providing self)
    */
    catch(evaluate(read_func, this_object(), 0));
}

//### socket_listen doesn't take funcptrs yet...
/* private */ protected nomask void listen_callback(int fd)
{
    object	s;
    int		err;
    
SKTLOG("listen_callback: self",this_object());
SKTLOG("listen_callback: fd",fd);
    fd = socket_accept(fd, (: read_callback :), (: write_callback :));
    s = new(SOCKET, SKT_STYLE_INT_ACQUIRE, read_func, close_func);
SKTLOG("listen_callback: new sock",s);
    err = socket_release(fd, s, "release_callback");
SKTLOG("listen_callback: err",err);
    if ( err < 0 )
	error("could not release: " + socket_error(err) + "\n");
}

//### need a way to protect this from random writes
varargs nomask void send(mixed message, string address)
{
    int	err;

SKTLOG("send: self",this_object());
SKTLOG("send: fd",fdOwned);
SKTLOG("send: # elem",sizeof(write_queue));

    if ( address )
	err = socket_write(fdOwned, message, address);
    else if ( blocked )
    {
	/*
	** If we are blocked, then the socket doesn't want us to send
	** any more.  Place it on our queue for sending later.
	*/
	write_queue += ({ message });
    }
    else
    {
	while ( sizeof(message) )
	{
	    err = socket_write(fdOwned, message);

	    if ( err == EEALREADY )
	    {
		// write_callback will get called automatically.
		blocked = 1;
		write_queue += ({ message });
		return;
	    }
	    if ( err == EEWOULDBLOCK )
	    {
		// write_callback needs to get called manually.
		blocked = 1;
		write_queue += ({ message });
		call_out("write_callback", 1, fdOwned);
		return;
	    }
	    if ( err == EECALLBACK )
	    {
		/*
		** Socket took the message but is blocked until it can
		** write it out.  Set a flag so that we don't write any
		** more until we get the callback.
		*/
		blocked = 1;
		return;
	    }

	    message = "";
	    if ( write_func )
	    {
		message = evaluate(write_func, this_object());
	    }
	}
    }

    if ( err < 0 )
	error("could not write: " + socket_error(err) + "\n");
}

void remove()
{
    int err;

    if ( fdOwned >= 0 )
    {
SKTLOG("remove: self",this_object());
SKTLOG("remove: fdOwned",fdOwned);
	err = socket_close(fdOwned);
SKTLOG("remove: err",err);
	if ( err < 0 )
	    LOG_D->log(LOG_SOCKET,
		       "could not close: " + socket_error(err) + "\n");
    }

    destruct();
}

nomask mixed *address()
{
    string tmp;
    string host;
    int port;

    tmp = socket_address(fdOwned);
    sscanf(tmp, "%s %d", host, port);
    return ({ host, port });
}

nomask int local_port()
{
  string address;
  int port;
  sscanf(socket_address(fdOwned, 1), "%s %d", address, port);
  return port;
}

nomask string local_address() {
  string address;
  int port;
  sscanf(socket_address(fdOwned,1),"%s %d",address,port);
  return address;
}

void create(int skt_style, mixed p1, mixed p2, mixed p3)
{
    int err;

    if ( !clonep() )
	return;

SKTLOG("create: self",this_object());
    style = skt_style;
    //    addr = p1;

    switch ( style )
    {
    case SKT_STYLE_LISTEN:
	read_func = p2;
	close_func = p3;
	fdOwned = socket_create(1 /* STREAM */,
				(: read_callback :),
				(: close_callback :));
	if ( fdOwned < 0 )
	    error("could not create socket: " + socket_error(fdOwned) + "\n");
	if ( (err = socket_bind(fdOwned, p1)) < 0 )
	    error("could not bind socket: " + socket_error(err) + "\n");
	if ( (err = socket_listen(fdOwned, "listen_callback")) < 0 )
	    error("could not listen to socket: " + socket_error(err) + "\n");
SKTLOG("create: SKT_STYLE_LISTEN",fdOwned);
	break;

    case SKT_STYLE_CONNECT:
	read_func = p2;
	close_func = p3;
#ifdef REQUIRE_PRIV
	if ( !check_previous_privilege(REQUIRE_PRIV) )
	{
	    error("Insufficient privs to open an outgoing socket.\n");
	}
#endif
	fdOwned = socket_create(1 /* STREAM */,
				(: read_callback :),
				(: close_callback :));
	if ( fdOwned < 0 )
	    error("could not create socket: " + socket_error(fdOwned) + "\n");
	err = socket_connect(fdOwned, p1, "read_callback", "write_callback");
	if ( err < 0 )
	    error("could not listen to socket: " + socket_error(err) + "\n");
SKTLOG("create: SKT_STYLE_CONNECT",fdOwned);
SKTLOG("create: close_func",close_func);
	break;

    case SKT_STYLE_LISTEN_B:
        read_func = p2;
        close_func = p3;
        fdOwned = socket_create(3 /* STREAM BINARY */,
                                (: read_callback :),
                                (: close_callback :));
        if ( fdOwned < 0 )
            error("could not create socket: " + socket_error(fdOwned) + "\n");
        if ( (err = socket_bind(fdOwned, p1)) < 0 )
            error("could not bind socket: " + socket_error(err) + "\n");
        if ( (err = socket_listen(fdOwned, "listen_callback")) < 0 )
            error("could not listen to socket: " + socket_error(err) + "\n");
SKTLOG("create: SKT_STYLE_LISTEN_B",fdOwned);
        break;

    case SKT_STYLE_CONNECT_B:
#ifdef REQUIRE_PRIV
	if ( !check_previous_privilege(REQUIRE_PRIV) )
	{
	    error("Insufficient privs to open an outgoing socket.\n");
	}
#endif
        read_func = p2;
        close_func = p3;
        fdOwned = socket_create(3 /* STREAM BINARY */,
                                (: read_callback :),
                                (: close_callback :));
        if ( fdOwned < 0 )
            error("could not create socket: " + socket_error(fdOwned) + "\n");
        err = socket_connect(fdOwned, p1, "read_callback", "write_callback");
        if ( err < 0 )
            error("could not listen to socket: " + socket_error(err) + "\n");
SKTLOG("create: SKT_STYLE_CONNECT_B",fdOwned);
SKTLOG("create: close_func",close_func);
        break;

    case SKT_STYLE_UDP:
#ifdef REQUIRE_PRIV
	if ( !check_previous_privilege(REQUIRE_PRIV) )
	{
	    error("Insufficient privs to open a datagram socket.\n");
	}
#endif
	read_func = p2;
	fdOwned = socket_create(2 /* DATAGRAM */, (: read_udp_callback :));
	if ( fdOwned < 0 )
	    error("could not create socket: " + socket_error(fdOwned) + "\n");
	if ( (err = socket_bind(fdOwned, p1)) < 0 )
	    error("could not bind socket: " + socket_error(err) + "\n");
SKTLOG("create: SKT_STYLE_UDP",fdOwned);
	break;

    case SKT_STYLE_LISTEN_M:
	read_func = p2;
	close_func = p3;
	fdOwned = socket_create(0 /* MUD */,
				(: read_callback :),
				(: close_callback :));
	if ( fdOwned < 0 )
	    error("could not create socket: " + socket_error(fdOwned) + "\n");
	if ( (err = socket_bind(fdOwned, p1)) < 0 )
	    error("could not bind socket: " + socket_error(err) + "\n");
	if ( (err = socket_listen(fdOwned, "listen_callback")) < 0 )
	    error("could not listen to socket: " + socket_error(err) + "\n");
SKTLOG("create: SKT_STYLE_LISTEN_M",fdOwned);
	break;

    case SKT_STYLE_CONNECT_M:
#ifdef REQUIRE_PRIV
	if ( !check_previous_privilege(REQUIRE_PRIV) )
	{
	    error("Insufficient privs to open an outgoing socket.\n");
	}
#endif
	read_func = p2;
	close_func = p3;
	fdOwned = socket_create(0 /* MUD */,
				(: read_callback :),
				(: close_callback :));
	if ( fdOwned < 0 )
	    error("could not create socket: " + socket_error(fdOwned) + "\n");
	err = socket_connect(fdOwned, p1, "read_callback", "write_callback");
	if ( err < 0 )
	    error("could not listen to socket: " + socket_error(err) + "\n");
SKTLOG("create: SKT_STYLE_CONNECT_M",fdOwned);
SKTLOG("create: close_func",close_func);
	break;

    case SKT_STYLE_INT_ACQUIRE:
	read_func	= p1;
	close_func	= p2;
	break;
    }
}