/*****************************************************************
 *  Stuff to create connections --- OS dependent
 *
 *      EstablishNewConnections, CreateWellKnownSockets, ResetWellKnownSockets,
 *      CloseDownConnection, CheckConnections, AddEnabledDevice,
 *	RemoveEnabledDevice, OnlyListToOneClient,
 *      ListenToAllClients,
 *
 *      (WaitForSomething is in its own file)
 *
 *****************************************************************/

#include <signal.h>
#include <time.h>
#undef NULL
#include "X.h"
#include "Xproto.h"
#include "Xos.h"			/* for strings, file, time */

#include <stdio.h>
#include "osstruct.h"
#include "opaque.h"
#include "dixstruct.h"
#include "osdep.h"
#include "config.h"

#ifdef DNETCONN
#include <netdnet/dn.h>
#endif /* DNETCONN */

char *display;			/* The display number */

#ifdef X_PRINT_PORT
struct tcpport _far PrinterPort;
static int PrinterSocket;
static int printfd;
#endif
static int printfd;
unsigned long near myaddr;
u_long CloseTimeOut = 30000;

OsCommPtr near MyPrivate[NPORTS];

static int near listen_socket(int port, struct tcpport *pport);
static void _near ErrorConnMax(OsCommPtr oc);

OsCommPtr near WellKnownOS[2];
static Bool GrabDone = FALSE;
OsCommPtr near oneClient;
static int tcpportReg;	    /* port with same byte order as server */

/* These are hacks because we know we only do MIT-MAGIC-COOKIE-1. */
int near MitMagicLen;
char near * near MitMagicData;
char near MitMagicName[18] = "MIT-MAGIC-COOKIE-1";

/*****************
 * CreateWellKnownSockets
 *    At initialization, create the sockets to listen on for new clients.
 *****************/

extern struct conf far Scon;

void
CreateWellKnownSockets()
{

    myaddr = Scon.myip;

    tcpportReg = atoi(display);
    tcpportReg += X_TCP_PORT;

    WellKnownOS[0] = (OsCommPtr)Xalloc(sizeof (*WellKnownOS[0]));
    if (!WellKnownOS[0])
	FatalError("No memory for socket\n");
    WellKnownOS[0]->flags = 0;
    WellKnownOS[0]->fd = listen_socket(tcpportReg, &WellKnownOS[0]->tcpport);

    WellKnownOS[1] = (OsCommPtr)Xalloc(sizeof (*WellKnownOS[1]));
    if (!WellKnownOS[1])
	FatalError("No memory for socket\n");
    WellKnownOS[1]->flags = 0;
    WellKnownOS[1]->fd = listen_socket(tcpportReg, &WellKnownOS[1]->tcpport);

#ifdef X_PRINT_PORT
    PrinterSocket = listen_socket(X_PRINT_PORT, &PrinterPort);
#endif
    signal (SIGINT, GiveUp);
    signal (SIGTERM, GiveUp);
    ResetHosts(display);
#ifdef XDMCP
    XdmcpInit ();
#endif
    EnableLocalHost();
}

void
ResetWellKnownSockets ()
{
    ResetHosts(display);
#ifdef XDMCP
    XdmcpReset ();
#endif
    EnableLocalHost();
    reset_socket();
}

/* If an alloc failed, WellKnownOS or WellKnownOS2 will be NULL.  This
 * function is called after some memory is freed.
 */
void near
reset_socket()
{

    if (WellKnownOS[1] == 0 &&
	(WellKnownOS[1] = (OsCommPtr)Xalloc(sizeof (*WellKnownOS[1]))))
	    WellKnownOS[1]->fd = listen_socket(tcpportReg, &WellKnownOS[1]->tcpport);
    if (WellKnownOS[0] == 0 &&
	(WellKnownOS[0] = (OsCommPtr)Xalloc(sizeof (*WellKnownOS[0]))))
	    WellKnownOS[0]->fd = listen_socket(tcpportReg, &WellKnownOS[0]->tcpport);
}

static char noroom[] = "Maximum number of clients reached";

/*****************************************************************
 * ClientAuthorized
 *
 *     	It is hoped that eventually one protocol will be agreed upon.  In the
 *        mean time, a server that implements a different protocol than the
 *        client expects, or a server that only implements the host-based
 *        mechanism, will simply ignore this information.
 *
 *****************************************************************/

char * 
ClientAuthorized(client, proto_n, auth_proto, string_n, auth_string)
    ClientPtr client;
    char *auth_proto, *auth_string;
    unsigned short proto_n, string_n;
{
    OsCommPtr priv;
    Bool authorized = FALSE;

    if (MitMagicLen && proto_n == sizeof MitMagicName &&
	memcmp(auth_proto, MitMagicName, sizeof MitMagicName) == 0 &&
	string_n == MitMagicLen &&
	memcmp(auth_string, MitMagicData, MitMagicLen) == 0)
	    authorized = TRUE;

    priv = (OsCommPtr)client->osPrivate;

    if (!authorized &&
	InvalidHost((pointer)&priv->tcpport.outpkt.i.ipdest,
			sizeof (priv->tcpport.outpkt.i.ipdest))) {
	    return "Client is not authorized to connect to Server";
    }

    priv->conn_time = 0;

    /* At this point, if the client is authorized to change the access control
     * list, we should getpeername() information, and add the client to
     * the selfhosts list.  It's not really the host machine, but the
     * true purpose of the selfhosts list is to see who may change the
     * access control list.
     */
    return((char *)0);
}    

/*****************
 * EstablishNewConnections
 *    If anyone is waiting on listened sockets, accept them.
 *****************/

void near
EstablishNewConnections(poc)
OsCommPtr _near *poc;		/* which WellKnown socket */
{
    int newconn;                  /* fd of new client */
    ClientPtr client;
    u_long connect_time;
    OsCommPtr	port, oc = *poc;
    int i;

    newconn = oc->fd;

    connect_time = GetTimeInMillis();

    /* clean up */
    for (i = 0; i < NPORTS; i++)
	if (port = MyPrivate[i]) {
	    if (port->flags & OS_CLOSING && port->conn_time != 0 &&
		connect_time - port->conn_time >= CloseTimeOut) {
		    SoFree(i);
		    MyPrivate[i] = 0;
		    xfree(port);
	    }
	}

    /* kill off stragglers */
    for (i=1; i<currentMaxClients; i++)
    {
	if (client = clients[i])
	{
	    port = (OsCommPtr)client->osPrivate;
	    if (port && (port->conn_time != 0) &&
		(connect_time - port->conn_time) >= TimeOutValue)
		CloseDownClient(client);     
	}
    }

#ifdef notdef
    if (nClients >= MaxClients - 1) {
	/* If there are too many clients currently connected, then
	 * don't waste space by allocating another port as we are just
	 * going to refuse the X connection anyway.
	 * If we can't allocate any more space for the listening
	 * connection.  Let this client have it anyway and just shut
	 * this port down until we get more memory. */
	*poc = 0;
	if (WellKnownOS[0] == 0 && WellKnownOS[1] == 0)
	    port = (OsCommPtr)xalloc(sizeof (OsCommRec));
	else
	    port = 0;
    } else
#endif
	port = (OsCommPtr)xalloc(sizeof (OsCommRec));
	
    if (port)
	port->fd = listen_socket(tcpportReg, &port->tcpport);
    *poc = port;

    oc->conn_time = connect_time;
    oc->input = 0;
    oc->output = 0;
    oc->flags = 0;
    MyPrivate[newconn] = oc;

    if (client = NextAvailableClient((pointer)oc))
    {
	oc->client = client->index;
    }
    else
    {
	ErrorConnMax(oc);
	CloseDownFileDescriptor(oc);	/* this may free some memory */
	if (!WellKnownOS[0] || !WellKnownOS[1])
	    reset_socket();
	return;
    }
/*
printf("new connection %d\n", client->index);
*/
#ifdef XDMCP
    /* indicate to Xdmcp protocol that we've opened new client */
    XdmcpOpenDisplay(newconn, oc->tcpport.outpkt.i.ipdest);
#endif /* XDMCP */
}

/************
 *   ErrorConnMax
 *     Fail a connection due to lack of client or file descriptor space
 ************/

static void _near
ErrorConnMax(oc)
OsCommPtr	oc;
{
    xConnSetupPrefix csp;
    char byteOrder = 0;
    unsigned long time, now;
    int fd = oc->fd;

    /* Let's wait a half second for the client to send us at least its
     * byte order. */
    _disable();
    time = n_clicks();
    _enable();
    now = time;
    while (oc->tcpport.inbase == oc->tcpport.infree && now - time < 6L) {
	_disable();
	now = n_clicks();
	_enable();
	netsleep();
    }
    SoRead(fd, &byteOrder, 1);
    if (byteOrder != 'l' && byteOrder != 'B')
	return;
    csp.success = xFalse;
    csp.lengthReason = sizeof(noroom) - 1;
    csp.length = (sizeof(noroom) + 2) >> 2;
    csp.majorVersion = X_PROTOCOL;
    csp.minorVersion = X_PROTOCOL_REVISION;
    if (byteOrder == 'B') {
	csp.majorVersion = lswaps(csp.majorVersion);
	csp.minorVersion = lswaps(csp.minorVersion);
	csp.length = lswaps(csp.length);
    }
    SoWrite(fd, (char *)&csp, sz_xConnSetupPrefix);
    SoWrite(fd, noroom, csp.lengthReason + (-csp.lengthReason & 3));
}

/*****************
 * CloseDownConnection
 *    Delete client from AllClients and free resources 
 *****************/

Bool
CloseDownConnection(client)
    ClientPtr client;
{
    OsCommPtr oc = (OsCommPtr)client->osPrivate;

    if (oc == NULL) {
	printf("client %d: osPrivate is NULL!\n", client->index);
	return FALSE;
    }
#ifdef XDMCP
    XdmcpCloseDisplay(oc->fd);
#endif
    oc->client = -1;
    client->osPrivate = 0;
    FlushClient(oc, (char *)0, 0);
    CloseDownFileDescriptor(oc);
    if (!WellKnownOS[0] || !WellKnownOS[1])
	reset_socket();
    return TRUE;
}

/*****************
 * OnlyListenToOneClient:
 *    Only accept requests from  one client.  Continue to handle new
 *    connections, but don't take any protocol requests from the new
 *    ones.
 *    Note also that there is no timeout for this in the protocol.
 *    This routine is "undone" by ListenToAllClients()
 *****************/

void
OnlyListenToOneClient(client)
    ClientPtr client;
{
    OsCommPtr oc = (OsCommPtr)client->osPrivate;

    if (!GrabDone) {
	oneClient = oc;
	GrabDone = TRUE;
    }
}

/****************
 * ListenToAllClients:
 *    Undoes OnlyListentToOneClient()
 ****************/

void
ListenToAllClients()
{
    if (GrabDone) {
	oneClient = 0;
	GrabDone = FALSE;
    }
}

static int near
listen_socket(port, pport)
int port;
struct tcpport _far *pport;
{
    struct sockaddr addr;
    int request;

    addr.port = htons(port);
    addr.addr = 0;

    request = Socket(SOCK_STREAM, &addr, 0, (struct port far *)pport);
    if (request < 0) {
	    printf("socket: error %d\n", request);
	    return -1;
    }
    if (SoBind(request, &addr) < 0) {
	    printf("Bind: error\n");
	    SoClose(request);
	    return -1;
    }
    if (SoListen(request) < 0) {
	    printf("SoListen: error\n");
	    SoClose(request);
	    return -1;
    }
    return request;
}

#ifdef X_PRINT_PORT
int near
PrintOpen()
{
	printfd = open("prn", O_WRONLY);
	if(printfd < 0) {
	    SoWrite(PrinterSocket, "Can't open printer.\r\n", 21);
	    close(printfd);
	    printfd = -1;
	    SoClose(PrinterSocket);
	    PrinterSocket = listen_socket(X_PRINT_PORT, &PrinterPort);
	    return 0;
	}
	return 1;
}

int near
PrintIt()
{
	char printer_buf[256];
	int print_bytes;

	print_bytes = SoRead(PrinterSocket, printer_buf, sizeof printer_buf);
	if (print_bytes <= 0) {
	    SoClose(PrinterSocket);
	    close(printfd);
	    printfd = -1;
	    PrinterSocket = listen_socket(X_PRINT_PORT, &PrinterPort);
	    return 0;
	}
	(void)write(printfd, printer_buf, print_bytes);
	return 1;
}
#endif

unsigned long _fastcall _near
ClientNetAddr(client)
ClientPtr client;
{
    return ((OsCommPtr)client->osPrivate)->tcpport.outpkt.i.ipdest;
}

#ifdef notdef
debug_print()
{
    OsCommPtr oc;
    int i;
    ConnectionOutputPtr oco;
    ConnectionInputPtr oci;
    xReq *request;

/*    PrintWindowTree(); */
    if (oneClient)
	printf("Only %d\n", oneClient->client);

    for (i = 0; i < NPORTS; i++) {
	if (!MyPrivate[i])
	    continue;
	oc = MyPrivate[i];
	printf("client %d fd %d ptr %lx flags=%x", oc->client, i, oc, oc->flags);
	if ((oco = oc->output) && oco->count) {
		printf(" output count %d ", oco->count);
	}
	if ((oci = oc->input) && oci->bufcnt) {
		printf(" input count %d ", oci->bufcnt);
		request = (xReq *)oci->buffer;
		printf(" request %d len %d",  request->reqType, request->length);
	}
	printf("\n");
    }
}
#endif
