merentha_fluffos_v2/
merentha_fluffos_v2/bin/
merentha_fluffos_v2/fluffos-2.9-ds2.03/
merentha_fluffos_v2/fluffos-2.9-ds2.03/ChangeLog.old/
merentha_fluffos_v2/fluffos-2.9-ds2.03/Win32/
merentha_fluffos_v2/fluffos-2.9-ds2.03/compat/
merentha_fluffos_v2/fluffos-2.9-ds2.03/compat/simuls/
merentha_fluffos_v2/fluffos-2.9-ds2.03/include/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/clone/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/command/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/data/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/etc/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/include/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/inherit/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/inherit/master/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/log/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/single/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/single/tests/compiler/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/single/tests/efuns/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/single/tests/operators/
merentha_fluffos_v2/fluffos-2.9-ds2.03/testsuite/u/
merentha_fluffos_v2/fluffos-2.9-ds2.03/tmp/
merentha_fluffos_v2/fluffos-2.9-ds2.03/windows/
merentha_fluffos_v2/lib/cfg/
merentha_fluffos_v2/lib/cfg/races/
merentha_fluffos_v2/lib/cmds/abilities/
merentha_fluffos_v2/lib/cmds/actions/
merentha_fluffos_v2/lib/cmds/spells/
merentha_fluffos_v2/lib/daemon/include/
merentha_fluffos_v2/lib/daemon/services/
merentha_fluffos_v2/lib/doc/
merentha_fluffos_v2/lib/doc/building/
merentha_fluffos_v2/lib/doc/help/classes/
merentha_fluffos_v2/lib/doc/help/general/
merentha_fluffos_v2/lib/doc/help/races/
merentha_fluffos_v2/lib/doc/help/skills/
merentha_fluffos_v2/lib/doc/help/stats/
merentha_fluffos_v2/lib/doc/man/efuns/
merentha_fluffos_v2/lib/doc/man/lfuns/
merentha_fluffos_v2/lib/doc/news/
merentha_fluffos_v2/lib/doc/old/
merentha_fluffos_v2/lib/doc/old/concepts/
merentha_fluffos_v2/lib/doc/old/lpc/constructs/
merentha_fluffos_v2/lib/doc/old/lpc/types/
merentha_fluffos_v2/lib/domains/ROOMS/
merentha_fluffos_v2/lib/domains/obj/armour/
merentha_fluffos_v2/lib/domains/obj/monsters/
merentha_fluffos_v2/lib/domains/obj/other/
merentha_fluffos_v2/lib/domains/obj/weapons/
merentha_fluffos_v2/lib/realms/petrarch/
merentha_fluffos_v2/lib/save/daemons/
merentha_fluffos_v2/lib/save/rid/
merentha_fluffos_v2/lib/save/users/a/
merentha_fluffos_v2/lib/save/users/p/
merentha_fluffos_v2/lib/save/users/t/
merentha_fluffos_v2/lib/std/login/
merentha_fluffos_v2/lib/std/obj/
merentha_fluffos_v2/win32/
/*
** socket.c
**
** This object represents an open UDP/TCP socket using the MudOS
** socket facilities.
**
** 09-Feb-95. Deathblade. Created.
*/

#include <socket.h>
#define SKT_STYLE_LISTEN 1
#define SKT_STYLE_LISTEN_B       11
#define SKT_STYLE_LISTEN_M       21
#define SKT_STYLE_CONNECT        2
#define SKT_STYLE_CONNECT_B      12
#define SKT_STYLE_CONNECT_M      22
#define SKT_STYLE_UDP            3
#define SKT_STYLE_UDP_B          13
#define SKT_STYLE_INT_ACQUIRE    100

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

static int        style;
static int        fdOwned = -1;        /* no socket yet */
static function        read_func;
static function        close_func;

static mixed *        write_queue = ({ });
static int        blocked;

/* For debug purposes only */
static mixed addr;
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(), addr);
        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(), addr);
        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(), addr);
        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;
}


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

    if ( !clonep() )
        return;

//seteuid(getuid(previous_object()));

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;
        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_UDP:
        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:
        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;
    }
}

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 = clone_object("/daemon/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");
}

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));
}

nomask void read_callback(int fd, mixed message)
{
    /* ### workaround an internal driver bug */
    restore_variable("0");

SKTLOG("read_callback: self",this_object());
SKTLOG("read_callback: fd",fd);
    catch(evaluate(read_func, this_object(), message));
}

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));
}

nomask void close_callback(int fd)
{
SKTLOG("close_callback: self",this_object());
SKTLOG("close_callback: fd",fd);
SKTLOG("close_callback: close_func",close_func);
    if ( close_func )
    {
        catch(evaluate(close_func, this_object()));
    }

    destruct(this_object());
}

nomask void write_callback(int fd)
{
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).
    */
    blocked = 0;

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

        err = socket_write(fd, write_queue[0]);
SKTLOG("write_callback: err",err);

        /* ### workaround an internal driver bug */
        restore_variable("0");

        /* ### HACK: driver did not write anything, but it won't call again,
        ** ### so force it with a call_out()
        */
        if ( err == EEWOULDBLOCK )
        {
            blocked = 1;
            call_out((: write_callback, fd :), 1);
            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");
    }
}

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
    {
        err = socket_write(fdOwned, message);
SKTLOG("send: err",err);

        /* ### workaround an internal driver bug */
        restore_variable("0");

        /* ### note: test for EEALREADY for now... need newer driver */
        if ( err == EEALREADY )
        {
            blocked = 1;
            write_queue += ({ message });
            return;
        }
        /* ### same for OWB: means the driver hasn't queued it :- */
        /* ### set a call_out() to ensure we're called to deliver it */
        if ( err == EEWOULDBLOCK )
        {
            blocked = 1;
            write_queue += ({ message });
            call_out((: write_callback, fdOwned :), 1);
            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;
        }
    }

    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 )
            error("could not close: " + socket_error(err) + "\n");
    }

    destruct(this_object());
}

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

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