123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
/*******************************************************************************

        copyright:      Copyright (c) 2009 Tango. All rights reserved

        license:        BSD style: $(LICENSE)

        version:        Nov 2009: Initial release

        author:         Lukas Pinkowski, Kris

*******************************************************************************/

module tango.net.device.LocalSocket;

private import tango.net.device.Socket;
private import tango.net.device.Berkeley;

/*******************************************************************************


*******************************************************************************/

version (Windows)
{
        pragma(msg, "not yet available for windows");
}

/*******************************************************************************

        A wrapper around the Berkeley API to implement the IConduit 
        abstraction and add stream-specific functionality.

*******************************************************************************/

class LocalSocket : Socket
{
        /***********************************************************************
        
                Create a streaming local socket

        ***********************************************************************/

        private this ()
        {
                super (AddressFamily.UNIX, SocketType.STREAM, ProtocolType.IP);
        }

        /***********************************************************************
        
                Create a streaming local socket

        ***********************************************************************/

        this (const(char)[] path)
        {
                this (new LocalAddress (path));
        }

        /***********************************************************************
        
                Create a streaming local socket

        ***********************************************************************/

        this (LocalAddress addr)
        {       
                this();
                super.connect (addr);
        }

        /***********************************************************************

                Return the name of this device

        ***********************************************************************/

        override immutable(char)[] toString()
        {
                return "<localsocket>";
        }
}

/*******************************************************************************


*******************************************************************************/

class LocalServerSocket : LocalSocket
{      
        /***********************************************************************

        ***********************************************************************/

        this (const(char)[] path, int backlog=32, bool reuse=false)
        {
                auto addr = new LocalAddress (path);
                native.addressReuse(reuse).bind(addr).listen(backlog);
        }

        /***********************************************************************

                Return the name of this device

        ***********************************************************************/

        override immutable(char)[] toString()
        {
                return "<localaccept>";
        }

        /***********************************************************************

        ***********************************************************************/

        Socket accept (Socket recipient = null)
        {
                if (recipient is null)
                    recipient = new LocalSocket;

                native.accept (*recipient.native);
                recipient.timeout = timeout;
                return recipient;
        }
}

/*******************************************************************************

*******************************************************************************/

class LocalAddress : Address
{
        align(1) struct sockaddr_un
        {
                ushort sun_family = AddressFamily.UNIX;
                char[108] sun_path;
        }
                        
        protected
        {
                sockaddr_un sun;
                const(char)[] _path;
                size_t _pathLength;
        }

        /***********************************************************************

            -path- path to a unix domain socket (which is a filename)

        ***********************************************************************/

        this (const(char)[] path)
        {
                assert (path.length < 108);
                
                sun.sun_path [0 .. path.length] = path;
                sun.sun_path [path.length .. $] = 0;
                
                _pathLength = path.length;
                _path = sun.sun_path [0 .. path.length];
        }

        /***********************************************************************

        ***********************************************************************/

        @property override final sockaddr* name () 
        { 
                return cast(sockaddr*) &sun; 
        }
        
        /***********************************************************************

        ***********************************************************************/

        @property override final const int nameLen ()
        { 
                return cast(int)(_pathLength + ushort.sizeof);
        }
        
        /***********************************************************************

        ***********************************************************************/

        @property override final AddressFamily addressFamily () 
        { 
                return AddressFamily.UNIX; 
        }
        
        /***********************************************************************

        ***********************************************************************/

        final override string toString ()
        {
                if (isAbstract)
                    return "unix:abstract=" ~ _path[1..$].idup;
                else
                   return "unix:path=" ~ _path.idup;
        }
        
        /***********************************************************************

        ***********************************************************************/

        @property final const const(char)[] path ()
        {
                return _path;
        }
        
        /***********************************************************************

        ***********************************************************************/

        @property final const bool isAbstract ()
        {
                return _path[0] == 0;
        }
}

/******************************************************************************

******************************************************************************/

debug (LocalSocket)
{
        import tango.io.Stdout;

        void main()
        {
                auto y = new LocalSocket ("foo");
                auto x = new LocalServerSocket ("foo");   
        }
}