/*
 * csopendi.c -- open connection to CSDPS agent
 *
 * (c) Copyright 1990-1994 Adobe Systems Incorporated.
 * All rights reserved.
 * 
 * Permission to use, copy, modify, distribute, and sublicense this software
 * and its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notices appear in all copies and that
 * both those copyright notices and this permission notice appear in
 * supporting documentation and that the name of Adobe Systems Incorporated
 * not be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.  No trademark license
 * to use the Adobe trademarks is hereby granted.  If the Adobe trademark
 * "Display PostScript"(tm) is used to describe this software, its
 * functionality or for any other purpose, such use shall be limited to a
 * statement that this software works in conjunction with the Display
 * PostScript system.  Proper trademark attribution to reflect Adobe's
 * ownership of the trademark shall be given whenever any such reference to
 * the Display PostScript system is made.
 * 
 * ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR
 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
 * ADOBE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NON- INFRINGEMENT OF THIRD PARTY RIGHTS.  IN NO EVENT SHALL ADOBE BE LIABLE
 * TO YOU OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE, STRICT LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  ADOBE WILL NOT
 * PROVIDE ANY TRAINING OR OTHER SUPPORT FOR THE SOFTWARE.
 * 
 * Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems
 * Incorporated which may be registered in certain jurisdictions
 * 
 * Portions Copyright    Massachusetts Institute of Technology    1985, 1986
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  M.I.T. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *  
 * Author:  Adobe Systems Incorporated and MIT X Consortium
 */
 
#include <stdio.h>
#include <sys/param.h>		/* for MAXHOSTNAMELEN */
#define NEED_EVENTS
#include <X11/Xlibint.h>
#include <X11/Xos.h>
#include "cslibint.h"
#ifdef XXX
#include <X11/Xauth.h>
#include <X11/Xatom.h>

extern int _Xdebug;
extern Display *_XHeadOfDisplayList;

#ifndef lint
static int lock;	/* get rid of ifdefs when locking implemented */
#endif

#endif /* XXX */

#include <X11/X.h>
#include <X11/Xproto.h>
#include "DPSCAPClient.h"
#include <DPS/dpsNXargs.h>
#include "dpsNXprops.h"
#include "csfindNX.h"

#ifdef DPSLNKL
#include "dpslnkl.inc"
#endif /* DPSLNKL */


/* +++ Someday make this common with XDPS.c version */
#define DPY_NUMBER(dpy) ((dpy)->fd)

static xReq _dummy_request = {
	0, 0, 0
};

/*
 * First, a routine for setting authorization data
 */
static int xauth_namelen = 0;
static char *xauth_name = NULL;	 /* NULL means use default mechanism */
static int xauth_datalen = 0;
static char *xauth_data = NULL;	 /* NULL means get default data */

static OutOfMemory();
#ifdef XXX
void XSetAuthorization (name, namelen, data, datalen)
    int namelen, datalen;		/* lengths of name and data */
    char *name, *data;			/* NULL or arbitrary array of bytes */
{
    char *tmpname, *tmpdata;

    if (xauth_name) Xfree (xauth_name);	 /* free any existing data */
    if (xauth_data) Xfree (xauth_data);

    xauth_name = xauth_data = NULL;	/* mark it no longer valid */
    xauth_namelen = xauth_datalen = 0;

    if (namelen < 0) namelen = 0;	/* check for bogus inputs */
    if (datalen < 0) datalen = 0;	/* maybe should return? */

    if (namelen > 0)  {			/* try to allocate space */
	tmpname = Xmalloc ((unsigned) namelen);
	if (!tmpname) return;
	bcopy (name, tmpname, namelen);
    } else {
	tmpname = NULL;
    }

    if (datalen > 0)  {
	tmpdata = Xmalloc ((unsigned) datalen);
	if (!tmpdata) {
	    if (tmpname) (void) Xfree (tmpname);
	    return;
	}
	bcopy (data, tmpdata, datalen);
    } else {
	tmpdata = NULL;
    }

    xauth_name = tmpname;		/* and store the suckers */
    xauth_namelen = namelen;
    xauth_data = tmpdata;
    xauth_datalen = datalen;
    return;
}

#endif /* XXX */

/* 
 * Connects to a server, creates a Display object and returns a pointer to
 * the newly created Display back to the caller.
 */
XExtData * 
DPSCAPOpenAgent(dpy, trueDisplayName)
	register Display *dpy;
	char *trueDisplayName;
{
	register Display *agent;
	char *agentHost = (char *)NULL;
	register int i;
	int j, k;			/* random iterator indexes */
	char display_name[256];	        /* pointer to display name */
	char licMethBuf[256];
	char *licMeth = licMethBuf;
	char *fullname = NULL;
	int idisplay;
	int iscreen;
	char *server_addr = NULL;
	int server_addrlen = 0;
	int conn_family;
	int transport, port;
	XExtData *ext;
	DPSCAPData my;
	extern int _XConnectDisplay();
	extern char *getenv();
	char hostname[MAXHOSTNAMELEN];

/*
 * Find an agent to talk to.
 */
#ifdef DPSLNKL
	extern unsigned ANXPFunc();
#ifdef PSUSEPN
	(void) sprintf(licMeth, "%s%s:%d",
		       LICENSE_METHOD_PREFIX,
		       ANXVENDOR, /* From dpslnkl.inc */
		       ANXPFunc());
#else /* PSUSEPN */
	(void) sprintf(licMeth, "%s%s",
                       LICENSE_METHOD_PREFIX,
                       ANXVENDOR); /* From dpslnkl.inc */
#endif /* PSUSEPN */	
#else /* DPSLNKL */
	licMeth = NULL;		/* We want an open service */
#endif /* DPSLNKL */	
	(void) N_XGetHostname(hostname, MAXHOSTNAMELEN);
	switch(XDPSNXFindNX(dpy, licMeth, &agentHost, &transport, &port)) {
	case findnx_not_found: {
	    /* try to start-up an NX? */
	    Bool autoLaunch;
    
	    XDPSGetNXArg(XDPSNX_AUTO_LAUNCH, (void **) &autoLaunch);
	    if (autoLaunch == True) {
	    int requestedTrans;
	    int requestedPort = 0;
	    char **args = NULL;
	    char *additionalArgs[2];
	    char transportArg[256];
    
	    (void) DPSWarnProc(NULL, "Auto-launching DPS NX agent.");
	    XDPSGetNXArg(XDPSNX_LAUNCHED_AGENT_TRANS, (void **) &requestedTrans);
	    if (requestedTrans == XDPSNX_USE_BEST) {
		XDPSNXSetClientArg(XDPSNX_LAUNCHED_AGENT_TRANS,
			    (void *)XDPSNX_TRANS_UNIX);
		requestedTrans = XDPSNX_TRANS_UNIX;
	    }
	    /* cons-up an arg. to pass to Agent we are forking */
    
	    additionalArgs[1] = (char *) NULL;
	    additionalArgs[0] = transportArg;
	    XDPSGetNXArg(XDPSNX_LAUNCHED_AGENT_PORT, (void **) &requestedPort);
	    if (requestedPort == XDPSNX_USE_BEST) {
		requestedPort = XDPSNXRecommendPort(requestedTrans);
		if (requestedPort < 0) {
		DPSWarnProc(NULL, "Auto-launcher can't get a port.");
		return(NULL);
		}
	    }
	    (void) sprintf(transportArg, "%s/%d",
			    (requestedTrans == XDPSNX_TRANS_DECNET ?
			    "dec" : "tcp"),
			    requestedPort);
	    args = additionalArgs;
	    /* ASSERT: first argument in additionalArgs must be
		transport/port, unless agent changes to take this
		anywhere */
	    if (StartXDPSNX(args) != Success) {
	        char tb[256], *fs, **as;
                (void) XDPSGetNXArg(XDPSNX_EXEC_FILE, (void **) &fs);
                (void) XDPSGetNXArg(XDPSNX_EXEC_ARGS, (void **) &as);
		(void) sprintf(tb, "FAILED to auto-launch:\n    %s", fs);
		if (as != NULL)
		    for (; *as != NULL; as++) {
		        if ((int) (strlen(*as) + 1 + (i = strlen(tb))) > 256-1) {
			    if (i > 256-1-4)
			        strcpy(&(tb[256-1-1-4]), " ...");
			    else
			        strcat(tb, " ...");
			    break;
			}
			(void) strcat(tb, " ");
		        (void) strcat(tb, *as);
		    }
	        DPSWarnProc(NULL, tb);
		return(NULL);
	    } else {
		(void) sprintf(display_name, "%s%s%d", hostname,
				(requestedTrans == XDPSNX_TRANS_DECNET ?
				"::" : ":"),
				requestedPort);
	    }
	    } else {		/* autoLaunch != True */
	    return(NULL);
	    }
	  }
          break;
	case findnx_found: {	/* XDPSNXFindNX() == Success */
	    (void) sprintf(display_name, "%s%s%d",
			 (transport == XDPSNX_TRANS_UNIX ?
			  "unix" : agentHost),
			 (transport == XDPSNX_TRANS_DECNET ? "::" : ":"),
			 port);
	    /* Free agentHost later */
	  }
          break;
	case findnx_error:
          return(NULL);
	  break;
	default:
          DPSFatalProc(NULL, "Illegal value returned by XDPSNXFindNX");
	  break;
      }


/*
 * Attempt to allocate a display structure. Return NULL if allocation fails.
 */
	if ((agent = (Display *)Xcalloc(1, sizeof(Display))) == NULL) {
		return(NULL);
	}

/*
 * Call the Connect routine to get the network socket. If -1 is returned, the
 * connection failed. The connect routine will set fullname to point to the
 * expanded name.
 */

	if ((agent->fd = DPSCAPConnect(display_name, &fullname, &idisplay,
					 &conn_family,
					 &server_addrlen, &server_addr)) < 0) {
		Xfree ((char *) agent);
		return(NULL);
	}
#ifdef XXX
/*
 * Look up the authorization protocol name and data if necessary.
 */
	if (xauth_name && xauth_data) {
	    conn_auth_namelen = xauth_namelen;
	    conn_auth_name = xauth_name;
	    conn_auth_datalen = xauth_datalen;
	    conn_auth_data = xauth_data;
	} else {
	    char dpynumbuf[40];		/* big enough to hold 2^64 and more */
	    (void) sprintf (dpynumbuf, "%d", idisplay);

	    authptr = XauGetAuthByAddr ((unsigned short) conn_family,
					(unsigned short) server_addrlen,
					server_addr,
					(unsigned short) strlen (dpynumbuf),
					dpynumbuf,
					(unsigned short) xauth_namelen,
					xauth_name);
	    if (authptr) {
		conn_auth_namelen = authptr->name_length;
		conn_auth_name = (char *)authptr->name;
		conn_auth_datalen = authptr->data_length;
		conn_auth_data = (char *)authptr->data;
	    } else {
		conn_auth_namelen = 0;
		conn_auth_name = NULL;
		conn_auth_datalen = 0;
		conn_auth_data = NULL;
	    }
	}
#ifdef HASDES
	/*
	 * build XDM-AUTHORIZATION-1 data
	 */
	if (conn_auth_namelen == 19 &&
	    !strncmp (conn_auth_name, "XDM-AUTHORIZATION-1", 19))
	{
	    static char    encrypted_data[192/8];
	    int	    i, j;
	    struct sockaddr_in	in_addr;
	    int	    addrlen;
	    long    now;

	    j = 0;
	    for (i = 0; i < 8; i++)
	    {
		encrypted_data[j] = conn_auth_data[i];
		j++;
	    }
	    addrlen = sizeof (in_addr);
	    getsockname (dpy->fd, (struct sockaddr *) &in_addr, &addrlen);
	    if (in_addr.sin_family == 2)
	    {
		encrypted_data[j] = in_addr.sin_addr.s_net; j++;
		encrypted_data[j] = in_addr.sin_addr.s_host; j++;
		encrypted_data[j] = in_addr.sin_addr.s_lh; j++;
		encrypted_data[j] = in_addr.sin_addr.s_impno; j++;
		encrypted_data[j] = (in_addr.sin_port >> 8) & 0xff; j++;
		encrypted_data[j] = (in_addr.sin_port) & 0xff; j++;
	    }
	    else
	    {
		encrypted_data[j] = 0xff; j++;
		encrypted_data[j] = 0xff; j++;
		encrypted_data[j] = 0xff; j++;
		encrypted_data[j] = 0xff; j++;
		i = getpid ();
		encrypted_data[j] = (i >> 8) & 0xff; j++;
		encrypted_data[j] = (i) & 0xff; j++;
	    }
	    time (&now);
	    for (i = 3; i >= 0; i--)
	    {
		encrypted_data[j] = (now >> (i * 8)) & 0xff;
		j++;
	    }
	    XdmcpEncrypt (encrypted_data, conn_auth_data + 8,
			  encrypted_data, 192/8);
	    conn_auth_data = encrypted_data;
	    conn_auth_datalen = 192 / 8;
	}
#endif /* HASDES */
	if (server_addr) (void) Xfree (server_addr);

#endif /* XXX */

/*
 * We succeeded at authorization, so let us move the data into
 * the display structure.
 */
	agent->lock_meaning	= NoSymbol;
#ifdef XXX
	/* this field is not present in post X11R5 */	
	agent->current		= None;
#endif	
	agent->event_vec[X_Error] = N_XUnknownWireEvent;
	agent->event_vec[X_Reply] = N_XUnknownWireEvent;
	agent->wire_vec[X_Error]  = N_XUnknownNativeEvent;
	agent->wire_vec[X_Reply]  = N_XUnknownNativeEvent;
	for (i = KeyPress; i < 128; i++) {
	    agent->event_vec[i] = N_XUnknownWireEvent;
	    agent->wire_vec[i] 	= N_XUnknownNativeEvent;
	}
	agent->cursor_font	= None;
	agent->last_req = (char *)&_dummy_request;

	/* Salt away the host:display string for later use.
	   Storage owned by agent, Xmalloc'd by connection
	   call above */
	agent->display_name = fullname;
 
	/* Set up the output buffers. */
	if ((agent->bufptr = agent->buffer = Xmalloc(BUFSIZE)) == NULL) {
	        OutOfMemory (dpy);
		return(NULL);
	}
	agent->bufmax = agent->buffer + BUFSIZE;


    /* Create extension data */

    my = DPSCAPCreate(dpy, agent);
    if (my == (DPSCAPData)NULL)
        {
        OutOfMemory(agent);
        return(NULL);
        }
    ext = (XExtData *)Xcalloc(1, sizeof(XExtData));
    ext->private_data = (char *)my;
    
    /* Parse names to get true display name */
    if (agentHost && strcmp(hostname, agentHost))
        {
	register char *s, *p;
	char *dxname;
	char nametmp[MAXHOSTNAMELEN];
	/* Agent is not on the same host as client, so fix
	   up the stupid abbreviations used for the display,
	   and whoever came up with the syntax should be shot. */
	dxname = DisplayString(dpy);
	for (s = dxname, p = nametmp; *s; ++s)
	    if (*s == ':')
	       break;
	    else
	       *p++ = *s;
	*p = '\0';
	if (nametmp[0] == '\0' 
	  || !strcmp(nametmp, "unix") 
	  || !strcmp(nametmp, "localhost"))
	    {
	    strcpy(trueDisplayName, hostname);
	    if (*s)
	        strcat(trueDisplayName, s);
	    else
	        strcat(trueDisplayName, ":0.0");
	    }
	else
	    strcpy(trueDisplayName, dxname);
	}
    else
        strcpy(trueDisplayName, DisplayString(dpy));
    if (agentHost)
        Xfree(agentHost);
    return(ext);
}


/* OutOfMemory is called if malloc fails.  XOpenDisplay returns NULL
   after this returns. */

static OutOfMemory (dpy)
    Display *dpy;
    {
    DPSCAPCloseAgent(dpy);
    }

#ifdef NEEDFORNX
/* XFreeDisplayStructure frees all the storage associated with a 
 * Display.  It is used by XOpenDisplay if it runs out of memory,
 * and also by XCloseDisplay.   It needs to check whether all pointers
 * are non-NULL before dereferencing them, since it may be called
 * by XOpenDisplay before the Display structure is fully formed.
 * XOpenDisplay must be sure to initialize all the pointers to NULL
 * before the first possible call on this.
 */

static void
_XFreeDisplayStructure(dpy)
	register Display *dpy;
{
	if (dpy->screens) {
	    register int i;

            for (i = 0; i < dpy->nscreens; i++) {
		Screen *sp = &dpy->screens[i];

		if (sp->depths) {
		   register int j;

		   for (j = 0; j < sp->ndepths; j++) {
			Depth *dp = &sp->depths[j];

			if (dp->visuals) {
			   register int k;

			   for (k = 0; k < dp->nvisuals; k++)
			     _XFreeExtData (dp->visuals[k].ext_data);
			   Xfree ((char *) dp->visuals);
			   }
			}

		   Xfree ((char *) sp->depths);
		   }

		_XFreeExtData (sp->ext_data);
		}

	    Xfree ((char *)dpy->screens);
	    }
	
	if (dpy->pixmap_format) {
	    register int i;

	    for (i = 0; i < dpy->nformats; i++)
	      _XFreeExtData (dpy->pixmap_format[i].ext_data);
            Xfree ((char *)dpy->pixmap_format);
	    }

	if (dpy->display_name)
	   Xfree (dpy->display_name);
	if (dpy->vendor)
	   Xfree (dpy->vendor);

        if (dpy->buffer)
	   Xfree (dpy->buffer);
	if (dpy->atoms)
	   Xfree ((char *) dpy->atoms);
	if (dpy->keysyms)
	   Xfree ((char *) dpy->keysyms);
	if (dpy->modifiermap)
	   XFreeModifiermap(dpy->modifiermap);
	if (dpy->xdefaults)
	   Xfree (dpy->xdefaults);
	if (dpy->key_bindings)
	   _XFreeKeyBindings(dpy);

	while (dpy->ext_procs) {
	    _XExtension *ext = dpy->ext_procs;
	    dpy->ext_procs = ext->next;
	    if (ext->name)
		Xfree (ext->name);
	    Xfree ((char *)ext);
	}

	_XFreeExtData (dpy->ext_data);
        
	Xfree ((char *)dpy);
}
#endif /* NEEDFORNX */



void
DPSCAPCloseAgent(agent)
    Display *agent;
{
    if (!agent) return;
    N_XDisconnectDisplay(agent->fd);
    if (agent->display_name)
        Xfree(agent->display_name);
    if (agent->buffer)
        Xfree(agent->buffer);
    Xfree((char *)agent);
}
