/* $XConsortium: PProcess.c /main/29 1996/12/20 15:30:35 lehors $ */
/*

Copyright (C) 1996 X Consortium

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Soft-
ware"), to deal in the Software without restriction, including without
limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to
whom the Software is furnished to do so, subject to the following condi-
tions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT
SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABIL-
ITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

Except as contained in this notice, the name of the X Consortium shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization from
the X Consortium.

*/

#include "RxPlugin.h"
#include "XUrls.h"
#include "XAuth.h"
#include "XDpyName.h"
#include "Prefs.h"
#include <Xm/Xm.h>
#include <Xm/Protocols.h>

#include <limits.h>		/* for MAXHOSTNAMELEN */
/* and in case we didn't get it from the headers above */
#ifndef MAXHOSTNAMELEN
# define MAXHOSTNAMELEN 256
#endif

#ifdef XUSE_XTREGISTERWINDOW
extern void _XtRegisterWindow (Window window, Widget widget);
#define XtRegisterDrawable(d,win,wid) _XtRegisterWindow(win,wid)
extern void _XtUnregisterWindow (Window window, Widget widget);
#define UnregisterDrawable(win,wid) _XtUnregisterWindow(win,wid)
#else
#define UnregisterDrawable(win,wid) XtUnregisterDrawable(XtDisplay(wid),win)
#endif

/* timeout for authorizations */
#define DEFAULT_TIMEOUT 300
#define NO_TIMEOUT 0

/***********************************************************************
 * Try and do something sensible about window geometry
 ***********************************************************************/
static void
GetWindowGeometry(
    Display* dpy,
    Window win,
    Position* x,
    Position* y,
    Dimension* width,
    Dimension* height,
    Dimension* border_width,
    /* the following doesn't really belong here but it saves us from doing
       another XGetWindowAttributes later on */
    Colormap *cmap)
{
    long mask;
    XSizeHints* sizehints = XAllocSizeHints();
    XWindowAttributes wattr;

    if (XGetWindowAttributes (dpy, win, &wattr)) {
	*x = wattr.x;
	*y = wattr.y;
	*width = wattr.width;
	*height = wattr.height;
	*border_width = wattr.border_width;
	*cmap = wattr.colormap;
    }
    if (sizehints) {
	XGetWMNormalHints (dpy, win, sizehints, &mask);

	if (mask & USPosition|PPosition) {
	    *x = sizehints->x;
	    *y = sizehints->y;
	    *width = sizehints->width;
	    *height = sizehints->height;
	    XFree ((char*) sizehints);
	    return;
	}
	XFree ((char*) sizehints);
    }
    *x = 0;
    *y = 0;
    *width = 0;
    *height = 0;
}

/***********************************************************************
 * a set of utility functions to manipulate Windows lists
 ***********************************************************************/
static Bool
IsInWinList(Window *list, int count, Window win)
{
    int i;
    for (i = 0; i < count; i++, list++)
	if (*list == win)
	    return True;
    return False;
}

static void
AppendToWinList(Window **new_list, int *new_count,
		Window *list, int count, Window win)
{
    *new_count = count + 1;
    *new_list = (Window*) malloc(sizeof(Window) * *new_count);
    memcpy(*new_list, list, sizeof(Window) * count);
    (*new_list)[count] = win;
}

static void
PrependToWinList(Window **new_list, int *new_count,
		 Window *list, int count, Window win)
{
    *new_count = count + 1;
    *new_list = (Window*) malloc(sizeof(Window) * *new_count);
    (*new_list)[0] = win;
    memcpy(*new_list + 1, list, sizeof(Window) * count);
}

/* rotate the list so the given window is at the beginning of it */
static void
SetFirstWinList(Window *list, int count, Window win)
{
    int i;

    /* look for the given window starting from the end */
    list += count - 1;
    for (i = 0; i < count; i++, list--)
	if (*list == win)
	    break;
    if (i < count) {		/* when we found it rotate from there */
	/* shift every element to the right */
	for (i++; i < count; i++) {
	    list--;
	    list[1] = list[0];
	}
	/* and set the first one */
	*list = win;
    }
}

/* rotate the list so the given window which should be the first item is
   at the end of it */
static void
SetLastWinList(Window *list, int count, Window win)
{
    if (*list == win) {
	int i;
	/* shift every element to the left */
	for (i = 0; i < count - 1; i++, list++)
	    list[0] = list[1];
	/* and set the last one */
	*list = win;
    }
}

static void
RemoveFromWinList(Window **wlist, int *count, Window win)
{
    Window *list = *wlist;
    int i;
    /* look for the window to remove */
    for (i = 0; i < *count; i++, list++)
        if (*list == win) {
            (*count)--;
            break;
        }
    /* then simply shift following elements to the left */
    for (; i < *count; i++, list++)
        list[0] = list[1];
}

static void
ConcatWinLists(Window **list, int *count,
	       Window *list1, int count1,
	       Window *list2, int count2)
{
    *count = count1 + count2;
    *list = (Window*) malloc(sizeof(Window) * *count);
    memcpy(*list, list1, sizeof(Window) * count1);
    memcpy(*list + count1, list2, sizeof(Window) * count2);
}

static void
SubstractWinLists(Window **wlist, int *count,
		  Window *list1, int count1)
{
    Window *list = *wlist;
    int i, j;
    /* look for the beginning of the list to remove */
    for (i = 0; i < *count; i++, list++)
	if (*list == *list1)
	    break;
    /* skip the list to remove stopping at the end of one of the lists
       or at the first alien element */
    for (j = 0; j < count1 && i + j < *count; j++, list1++)
	if (list[j] != *list1)
	    break;
    /* then shift following elements */
    *count -= j;
    for (; i < *count; i++, list++)
	list[0] = list[j];
}

/***********************************************************************
 * Add window to the WM_COLORMAP_WINDOWS property on the Netscape
 * toplevel widget if necessary
 ***********************************************************************/
static void
SetWMColormap(PluginInstance* This, Window win)
{
    int i;
    Colormap top_cmap;
    Arg arg;

    /* get window's record */
    for (i = 0; i < This->nclient_windows; i++)
	if (This->client_windows[i].win = win)
	    break;

    if (i == This->nclient_windows)
	return;

    /* if window's colormap is different from toplevel's one set property */
    XtSetArg(arg, XmNcolormap, &top_cmap);
    XtGetValues(This->toplevel_widget, &arg, 1);
    if (This->client_windows[i].colormap != top_cmap) {
	Window *cur_list;
	int cur_count = 0;

	/* if there is already a non empty list we need to update it */
	if (XGetWMColormapWindows(RxGlobal.dpy, XtWindow(This->toplevel_widget),
				  &cur_list, &cur_count) == True &&
	    cur_count != 0) {

	    if (IsInWinList(cur_list, cur_count, win)) {
		/* window is already in the list just move it in first place */
		SetFirstWinList(cur_list, cur_count, win);
		XSetWMColormapWindows(RxGlobal.dpy,
				      XtWindow(This->toplevel_widget),
				      cur_list, cur_count);
	    } else {
		/* window is not in the list add it in first place */
		Window *new_list;
		int new_count;

		PrependToWinList(&new_list, &new_count,
				 cur_list, cur_count, win);
		XSetWMColormapWindows(RxGlobal.dpy,
				      XtWindow(This->toplevel_widget),
				      new_list, new_count);
		free(new_list);
	    }
	} else {		/* no list yet so lets make one */
	    Window list[2];

	    list[0] = win;
	    list[1] = XtWindow(This->toplevel_widget);
	    XSetWMColormapWindows(RxGlobal.dpy, XtWindow(This->toplevel_widget),
				  list, 2);
	}
	if (cur_count != 0)
	    XFree(cur_list);
    }
}

/***********************************************************************
 * Move window at the end of the WM_COLORMAP_WINDOWS property list on
 * the Netscape toplevel widget
 ***********************************************************************/
static void
UnsetWMColormap(PluginInstance* This, Window win)
{
    Window *list;
    int count = 0;

    if (XGetWMColormapWindows(RxGlobal.dpy, XtWindow(This->toplevel_widget),
			      &list, &count) == True && count != 0) {
	SetLastWinList(list, count, win);
	XSetWMColormapWindows(RxGlobal.dpy, XtWindow(This->toplevel_widget),
			      list, count);
    }
    if (count != 0)
	XFree(list);
}

/***********************************************************************
 * Remove window from the WM_COLORMAP_WINDOWS property on the Netscape
 * toplevel widget
 ***********************************************************************/
static void
ResetWMColormap(PluginInstance* This, Window win)
{
    Window *list;
    int count = 0;

    if (XGetWMColormapWindows(RxGlobal.dpy, XtWindow(This->toplevel_widget),
			      &list, &count) == True && count != 0) {
	RemoveFromWinList(&list, &count, win);

	if (count > 1)
	    XSetWMColormapWindows(RxGlobal.dpy, XtWindow(This->toplevel_widget),
				  list, count);
	else {			/* remove list when it becomes useless */
	    Atom prop;

	    prop = XInternAtom (RxGlobal.dpy, "WM_COLORMAP_WINDOWS", False);
	    XDeleteProperty(RxGlobal.dpy, XtWindow(This->toplevel_widget), prop);
	}
    }
    if (count != 0)
	XFree(list);
}

/***********************************************************************
 * Event Handler to reparent client window under plugin window 
 ***********************************************************************/
/* static */ void
SubstructureRedirectHandler (
    Widget widget, 
    XtPointer client_data, 
    XEvent* event, 
    Boolean* cont)
{
    windowrec* new_list;
    PluginInstance* This = (PluginInstance*) client_data;

#ifdef PLUGIN_TRACE
    fprintf (stderr, "%s\n", "SubstructureRedirectHandler");
    fprintf (stderr, "This: 0x%x\n", This);
#endif

    switch (event->type) {
    case ConfigureRequest:
	{
	    XWindowChanges config;
	    config.x = event->xconfigurerequest.x;
	    config.y = event->xconfigurerequest.y;
	    config.width = event->xconfigurerequest.width;
	    config.height = event->xconfigurerequest.height;
	    config.border_width = event->xconfigurerequest.border_width;
	    config.sibling = event->xconfigurerequest.above;
	    config.stack_mode = event->xconfigurerequest.detail;
#if 0
	    fprintf (stderr, "configuring at %dx%d+%d+%d\n", 
		     config.width, config.height, config.x, config.y);
#endif
	    XConfigureWindow (RxGlobal.dpy,
			      event->xconfigurerequest.window,
			      event->xconfigurerequest.value_mask,
			      &config);
	}
	break;

    case MapRequest:

	RxpSetStatusWidget(This, RUNNING);

	{
	    Window for_win;
	    int i;

	    if (XGetTransientForHint (RxGlobal.dpy, event->xmaprequest.window,
				      &for_win)) {
		for (i = 0; i < This->nclient_windows; i++)
		    if (for_win == This->client_windows[i].win)
			XMapWindow (RxGlobal.dpy, event->xmaprequest.window);
		return;
	    }
	}
	new_list = (windowrec*) 
	    NPN_MemAlloc (sizeof (windowrec) * (This->nclient_windows + 1));
	if (new_list) {
	    Position x, y;
	    Dimension width, height;
	    Dimension border_width;
	    Colormap cmap;
	    int n;
	    Atom* wm_proto;
	    windowrec* wp;
	    Window destwin = XtWindow (This->plugin_widget);

	    This->nclient_windows++;
	    if (This->nclient_windows > 1)
		memcpy ((void*) new_list, (void*) This->client_windows,
			(This->nclient_windows - 1) * sizeof (windowrec));
	    if (This->client_windows)
		NPN_MemFree (This->client_windows);
	    This->client_windows = new_list;

	    x = y = 0;
	    width = height = border_width = 0;
	    GetWindowGeometry (RxGlobal.dpy, event->xmaprequest.window, 
			       &x, &y, &width, &height, &border_width, &cmap);

	    wp = &This->client_windows[This->nclient_windows - 1];
	    wp->win = event->xmaprequest.window;
	    wp->x = x; wp->y = y;
	    wp->width = width; wp->height = height;
	    wp->border_width = border_width;
	    wp->flags = RxpMapped;
	    wp->colormap = cmap;

	    if (XGetWMProtocols (RxGlobal.dpy, wp->win, &wm_proto, &n)) {
		int i;
		Atom* ap;

		for (i = 0, ap = wm_proto; i < n; i++, ap++) {
		    if (*ap == RxGlobal.wm_delete_window)
			wp->flags |= RxpWmDelWin;
		}
		if (wm_proto) XFree ((char*) wm_proto);
	    }

	    XSelectInput(RxGlobal.dpy, wp->win,
			 EnterWindowMask | LeaveWindowMask);
	    XtRegisterDrawable (RxGlobal.dpy, wp->win, This->plugin_widget);
	    XReparentWindow (RxGlobal.dpy, wp->win, destwin, wp->x, wp->y);
	    XMapWindow (RxGlobal.dpy, wp->win);
	}
	break;
    }
}

/***********************************************************************
 * Event Handler to forward WM_DELETE_WINDOW events to the client windows
 ***********************************************************************/
void
RxpWmDelWinHandler (
    Widget widget,
    XtPointer client_data,
    XtPointer call_data)
{
    PluginInstance* This = (PluginInstance*) client_data;
    int i;

    for (i = 0; i < This->nclient_windows; i++) {
	if (This->client_windows[i].flags & RxpWmDelWin) {
	    XClientMessageEvent ev;

	    ev.type = ClientMessage;
	    ev.window = This->client_windows[i].win;
	    ev.message_type = RxGlobal.wm_protocols;
	    ev.format = 32;
	    ev.data.l[0] = RxGlobal.wm_delete_window;
	    ev.data.l[1] = XtLastTimestampProcessed (XtDisplay (widget));
	    XSendEvent (RxGlobal.dpy, ev.window, FALSE, 0L, (XEvent*) &ev);
	}
    }
}

/***********************************************************************
 * Event Handler to forward ConfigureNotify events to the client windows
 ***********************************************************************/
static void
StructureNotifyHandler (
    Widget widget, 
    XtPointer client_data, 
    XEvent* event, 
    Boolean* cont)
{
    PluginInstance* This = (PluginInstance*) client_data;

#ifdef PLUGIN_TRACE
    fprintf (stderr, "%s\n", "StructureNotifyHandler");
#endif

    switch (event->type) {

    /*
     * For the testplugin, which uses a ScrolledWindow, the clipped
     * window, i.e. This->plugin, is "configured" when the user pans
     * around the ScrolledWindow. The Netscape scrolled-window is
     * different. It moves-and-resizes the clip window, causing the
     * child, i.e. This->plugin, to be "dragged" up by win-gravity.
     */
    case ConfigureNotify:
    case GravityNotify:
	if (This->plugin_widget == NULL)
	    return;

	{
	    int i;
	    Position x, y;
	    XConfigureEvent sendev;
	    XConfigureEvent* ev = (XConfigureEvent*) event;

	    XtTranslateCoords (This->plugin_widget, 0, 0, &x, &y);
	    for (i = 0; i < This->nclient_windows; i++) {
		sendev.type = ConfigureNotify;
		sendev.send_event = True;
		sendev.event = sendev.window = This->client_windows[i].win;
		sendev.x = x + This->client_windows[i].x;
		sendev.y = y + This->client_windows[i].y;
		sendev.width = This->client_windows[i].width;
		sendev.height = This->client_windows[i].height;
		sendev.border_width = This->client_windows[i].border_width;
		sendev.above = None;
		sendev.override_redirect = False;
		if (!XSendEvent (RxGlobal.dpy, This->client_windows[i].win,
				 False, StructureNotifyMask,
				 (XEvent*) &sendev))
		    (void) fprintf (stderr, "%s\n", "XSendEvent Failed");
	    }
	}
	break;

    default:
	break;
    }
}

/***********************************************************************
 * Event Handler to detect the destruction of a client window
 ***********************************************************************/
static void
SubstructureNotifyHandler (
    Widget widget, 
    XtPointer client_data, 
    XEvent* event, 
    Boolean* cont)
{
    PluginInstance* This = (PluginInstance*) client_data;

#ifdef PLUGIN_TRACE
    fprintf (stderr, "%s\n", "SubstructureNotifyHandler");
#endif

    if (event->type == DestroyNotify) {
	int i;
#ifdef PLUGIN_TRACE
	fprintf (stderr, "%s\n", "DestroyNotify");
#endif
	for (i = 0; i < This->nclient_windows; i++)
	    if (This->client_windows[i].win == event->xdestroywindow.window) {
		This->nclient_windows--;
		if (This->nclient_windows > 0) {
		    /* remove this window from the list */
		    for (; i < This->nclient_windows; i++)
			This->client_windows[i] = This->client_windows[i + 1];
		} else {	/* no more client windows! */
		    /* get back to user to restart the application */
		    RxpSetStatusWidget(This, WAITING);
		}
		ResetWMColormap(This, event->xdestroywindow.window);
		UnregisterDrawable(event->xdestroywindow.window,
				   This->plugin_widget);
		break;
	    }

    }
}

/***********************************************************************
 * Arrange to receive  (synthetic) ConfigureNotify events on the proper
 * windows of this instance and relay them to the embedded apps
 ***********************************************************************/
static void
SetupStructureNotify (PluginInstance* This)
{
    /* Get ConfigureNotify when the browser is moved */
    XtAddRawEventHandler (This->toplevel_widget,
			  StructureNotifyMask,
			  False,
			  StructureNotifyHandler,
			  (XtPointer) This);
    XmAddWMProtocolCallback (This->toplevel_widget,
			     RxGlobal.wm_delete_window,
			     RxpWmDelWinHandler,
			     (XtPointer) This);
}

/***********************************************************************
 * Event Handler to deal with colormap settings
 ***********************************************************************/
static void
CrossingHandler (
    Widget widget, 
    XtPointer client_data, 
    XEvent* event, 
    Boolean* cont)
{
    PluginInstance* This = (PluginInstance*) client_data;

#ifdef PLUGIN_TRACE
    fprintf (stderr, "%s: 0x%x\n", "CrossingHandler", event->xany.window);
#endif

    if (event->xany.window != XtWindow(This->plugin_widget) &&
	event->xcrossing.detail != NotifyInferior) {
	if (event->type == EnterNotify) {
#ifdef PLUGIN_TRACE
	    fprintf (stderr, "%s\n", "EnterNotify");
#endif
	    SetWMColormap(This, event->xany.window);
	} else if (event->type == LeaveNotify) {
#ifdef PLUGIN_TRACE
	    fprintf (stderr, "%s\n", "LeaveNotify");
#endif
	    UnsetWMColormap(This, event->xany.window);
	}
    }
}

/***********************************************************************
 * Setup various event handlers on the plugin widget
 ***********************************************************************/
void
RxpSetupPluginEventHandlers (PluginInstance* This)
{
    int i;

    /* Get ConfigureNotify and GravityNotify on the plugin */
    XtAddEventHandler (This->plugin_widget,
		       StructureNotifyMask,
		       False,
		       StructureNotifyHandler,
		       (XtPointer) This);
    /* Arrange to receive DestroyNotify events on the clients windows. */
    XtAddEventHandler (This->plugin_widget,
		       SubstructureNotifyMask,
		       False,
		       SubstructureNotifyHandler,
		       (XtPointer) This);
    /* Arrange to receive MapRequest and ConfigureRequest events on the
     * netscape plug-in widget. */
    XtAddRawEventHandler(This->plugin_widget,
			 SubstructureRedirectMask, 
			 False, 
			 SubstructureRedirectHandler,
			 (XtPointer) This);
    XtRegisterDrawable (RxGlobal.dpy, This->app_group, This->plugin_widget);

    /* Arrange to receive Enter and Leave Notify events on application's
       toplevel windows */
    XtAddRawEventHandler(This->plugin_widget,
			 EnterWindowMask | LeaveWindowMask, 
			 False, 
			 CrossingHandler,
			 (XtPointer) This);
    for (i = 0; i < This->nclient_windows; i++) {
	XtRegisterDrawable (RxGlobal.dpy,
			    This->client_windows[i].win, This->plugin_widget);
    }
}

/***********************************************************************
 * The instance is gone. Remove Event Handlers so that they aren't
 * called with a reference to the old instance.
 ***********************************************************************/
void
RxpTeardown (PluginInstance* This)
{
    if (This->toplevel_widget != NULL) {
	/* ConfigureNotify on top level */
	XtRemoveRawEventHandler (This->toplevel_widget,
				 StructureNotifyMask,
				 False, 
				 StructureNotifyHandler,
				 (XtPointer) This);
	XmRemoveWMProtocolCallback (This->toplevel_widget,
				    RxGlobal.wm_delete_window,
				    RxpWmDelWinHandler,
				    (XtPointer) This);
    }
}

/***********************************************************************
 * Process the given RxParams and make the RxReturnParams
 ***********************************************************************/

static void
ProcessUIParams(PluginInstance* This,
		Boolean trusted, Boolean use_fwp, Boolean use_lbx,
		RxParams *in, RxReturnParams *out, char **x_ui_auth_ret)
{
    XSecurityAuthorization dum;
    int dummy;
    char *display_name;

    This->app_group = None;
    if (out->embedded != RxFalse) {	/* default is embedded */
	/* let's see whether the server supports AppGroups or not */
	if (RxGlobal.has_appgroup == RxUndef) {
	    if (XQueryExtension(RxGlobal.dpy, "XC-APPGROUP",
				&dummy, &dummy, &dummy) &&
		XagQueryVersion (RxGlobal.dpy, &dummy, &dummy))
		RxGlobal.has_appgroup = RxTrue;
	    else
		RxGlobal.has_appgroup = RxFalse;
	}
	if (RxGlobal.has_appgroup == RxTrue) {
	    Screen *scr;
	    Colormap cmap;
	    Arg arg;

	    /* use plugin's colormap as the default colormap */
	    XtSetArg(arg, XmNcolormap, &cmap);
	    XtGetValues(This->plugin_widget, &arg, 1);
	    scr = XtScreen(This->plugin_widget);
	    if (cmap == DefaultColormapOfScreen(scr)) {
		XagCreateEmbeddedApplicationGroup (RxGlobal.dpy, None,
						   cmap,
						   BlackPixelOfScreen(scr),
						   WhitePixelOfScreen(scr),
						   &This->app_group);
	    } else {
		XColor black, white;
		Pixel pixels[2];

		black.red = black.green = black.blue = 0;
		XAllocColor(RxGlobal.dpy, cmap, &black);
		white.red = white.green = white.blue = 65535;
		XAllocColor(RxGlobal.dpy, cmap, &white);
		XagCreateEmbeddedApplicationGroup (RxGlobal.dpy, None,
						   cmap,
						   pixels[0] = black.pixel,
						   pixels[1] = white.pixel,
						   &This->app_group);
		XFreeColors(RxGlobal.dpy, cmap, pixels, 2, 0);
	    }
	    SetupStructureNotify (This);
	    RxpSetupPluginEventHandlers (This);
	} else {		/* too bad */
	    out->embedded = RxFalse;
	    fprintf(stderr, "Warning: Cannot perform embedding as \
requested, APPGROUP extension not supported\n");
	}
    }

    if (in->x_ui_auth[0] != 0) {
	GetXAuth(RxGlobal.dpy, in->x_ui_auth[0], in->x_ui_auth_data[0],
		 trusted, This->app_group, False, DEFAULT_TIMEOUT,
		 x_ui_auth_ret, &This->x_ui_auth_id, &dummy);
    } else if (in->x_auth[0] != 0)
	GetXAuth(RxGlobal.dpy, in->x_auth[0], in->x_auth_data[0], 
		 trusted, This->app_group, False, DEFAULT_TIMEOUT,
		 x_ui_auth_ret, &This->x_ui_auth_id, &dummy);

    /* make sure we use the server the user wants us to use */
    if (RxGlobal.has_real_server == RxUndef) {
	Display *rdpy = RxGlobal.dpy;
	char *real_display = getenv("XREALDISPLAY");
	RxGlobal.has_real_server = RxFalse;
	if (real_display != NULL) {
	    rdpy = XOpenDisplay(real_display);
	    if (rdpy == NULL)
		rdpy = RxGlobal.dpy;
	    else
		RxGlobal.has_real_server = RxTrue;
	}
	/* let's see now whether the server supports LBX or not */
	if (XQueryExtension(rdpy, "LBX", &dummy, &dummy, &dummy))
	    RxGlobal.has_ui_lbx = RxTrue;
	else
	    RxGlobal.has_ui_lbx = RxFalse;

	if (rdpy != RxGlobal.dpy)
	    XCloseDisplay(rdpy);
    }
    if (RxGlobal.has_real_server == RxTrue)
	display_name = getenv("XREALDISPLAY");
    else
	display_name = DisplayString(RxGlobal.dpy);

    /* let's see whether we have a firewall proxy */
    if (use_fwp == True && RxGlobal.has_ui_fwp == RxUndef) {
	RxGlobal.fwp_dpyname = GetXFwpDisplayName(display_name);
	if (RxGlobal.fwp_dpyname != NULL)
	    RxGlobal.has_ui_fwp = RxTrue;
	else
	    RxGlobal.has_ui_fwp = RxFalse;
    }
    if (use_fwp == True && RxGlobal.has_ui_fwp == RxTrue)
	out->ui = GetXUrl(RxGlobal.fwp_dpyname, *x_ui_auth_ret);
    else
	out->ui = GetXUrl(display_name, *x_ui_auth_ret);

    if (in->x_ui_lbx == RxTrue) {
	if (use_lbx == True) {
	    if (RxGlobal.has_ui_lbx == RxTrue) {
		out->x_ui_lbx = RxTrue;

		/* let's get a key for the proxy now */
		if (in->x_ui_lbx_auth[0] != 0) {
		    GetXAuth(RxGlobal.dpy, in->x_ui_lbx_auth[0],
			     in->x_ui_lbx_auth_data[0],
			     trusted, None, False, DEFAULT_TIMEOUT,
			     &out->x_ui_lbx_auth, &dum, &dummy);
		} else if (in->x_auth[0] != 0)
		    GetXAuth(RxGlobal.dpy, in->x_auth[0], in->x_auth_data[0],
			     trusted, None, False, DEFAULT_TIMEOUT,
			     &out->x_ui_lbx_auth, &dum, &dummy);
	    } else {
		out->x_ui_lbx = RxFalse;
		fprintf(stderr, "Warning: Cannot setup LBX as requested, \
LBX extension not supported\n");
	    }
	} else
	    out->x_ui_lbx = RxFalse;
    } else			/* it's either RxFalse or RxUndef */
	out->x_ui_lbx = in->x_ui_lbx;
}

static void
ProcessPrintParams(PluginInstance* This,
		   Boolean trusted, Boolean use_fwp, Boolean use_lbx,
		   RxParams *in, RxReturnParams *out, char *x_ui_auth)
{
    char *auth = NULL;
    XSecurityAuthorization dum;
    int dummy;

    /* let's find out if we have a print server */
    if (RxGlobal.has_printer == RxUndef) {
	RxGlobal.pdpy_name = GetXPrintDisplayName(&RxGlobal.printer_name);
	if (RxGlobal.pdpy_name != NULL) {
	    /* open connection to the print server */
	    RxGlobal.pdpy = XOpenDisplay(RxGlobal.pdpy_name);
	    if (RxGlobal.pdpy != NULL)
		RxGlobal.has_printer = RxTrue;
	    else
		RxGlobal.has_printer = RxFalse;
	} else {
	    /* no server specified,
	       let's see if the video server could do it */
	    if (XQueryExtension(RxGlobal.dpy, "XpExtension",
				&dummy, &dummy, &dummy)) {
		RxGlobal.has_printer = RxTrue;
		RxGlobal.pdpy = RxGlobal.dpy;
	    } else
		RxGlobal.has_printer = RxFalse;
	}
    }
    if (RxGlobal.has_printer == RxFalse) {
	fprintf(stderr, "Warning: Cannot setup X printer as requested, \
no server found\n");
	return;
    }

    /* create a key only when the video server is not the print
       server or when we didn't create a key yet */
    if (RxGlobal.pdpy != RxGlobal.dpy || x_ui_auth == NULL) {
	if (in->x_print_auth[0] != 0)
	    GetXAuth(RxGlobal.pdpy, in->x_print_auth[0],
		     in->x_print_auth_data[0],
		     trusted, None, False, NO_TIMEOUT,
		     &auth, &This->x_print_auth_id, &dummy);
	else if (in->x_auth[0] != 0)
	    GetXAuth(RxGlobal.pdpy, in->x_auth[0], in->x_auth_data[0],
		     trusted, None, False, NO_TIMEOUT,
		     &auth, &This->x_print_auth_id, &dummy);
    }

    /* let's see whether we have a firewall proxy */
    if (use_fwp == True && RxGlobal.has_print_fwp == RxUndef) {
	RxGlobal.pfwp_dpyname = GetXFwpDisplayName(DisplayString(RxGlobal.pdpy));
	if (RxGlobal.pfwp_dpyname != NULL)
	    RxGlobal.has_print_fwp = RxTrue;
	else
	    RxGlobal.has_print_fwp = RxFalse;
    }
    if (use_fwp == True && RxGlobal.has_print_fwp == RxTrue)
	out->print = GetXPrintUrl(RxGlobal.pfwp_dpyname,
				  RxGlobal.printer_name, auth);
    else
	out->print = GetXPrintUrl(DisplayString(RxGlobal.pdpy),
				  RxGlobal.printer_name, auth);

    if (auth != NULL)
	NPN_MemFree(auth);

    if (in->x_print_lbx == RxTrue) {
	if (use_lbx == True) {
	    /* let's see whether the server supports LBX or not */
	    if (RxGlobal.has_print_lbx == RxUndef) {
		if (RxGlobal.pdpy == RxGlobal.dpy &&
		    RxGlobal.has_ui_lbx != RxUndef) {
		    /* the video server is the print server and we already
		       know whether it supports LBX or not */
		    RxGlobal.has_print_lbx = RxGlobal.has_ui_lbx;
		} else {
		    if (XQueryExtension(RxGlobal.pdpy, "LBX",
					&dummy, &dummy, &dummy))
			RxGlobal.has_print_lbx = RxTrue;
		    else
			RxGlobal.has_print_lbx = RxFalse;
		}
	    }
	    if (RxGlobal.has_print_lbx == RxTrue) {
		out->x_print_lbx = RxTrue;
		if (RxGlobal.pdpy != RxGlobal.dpy) {
		    /* let's get a key for the proxy now */
		    if (in->x_print_lbx_auth[0] != 0) {
			GetXAuth(RxGlobal.pdpy, in->x_print_lbx_auth[0],
				 in->x_print_lbx_auth_data[0],
				 trusted, None, False, DEFAULT_TIMEOUT,
				 &out->x_print_lbx_auth, &dum, &dummy);
		    } else if (in->x_auth[0] != 0)
			GetXAuth(RxGlobal.pdpy, in->x_auth[0],
				 in->x_auth_data[0],
				 trusted, None, False, DEFAULT_TIMEOUT,
				 &out->x_print_lbx_auth, &dum, &dummy);
		}
	    } else {
		out->x_print_lbx = RxFalse;
		fprintf(stderr, "Warning: Cannot setup LBX as \
requested, LBX extension not supported\n");
	    }
	} else
	    out->x_print_lbx = RxFalse;
    } else		/* it's either RxFalse or RxUndef */
	out->x_print_lbx = in->x_print_lbx;
}

int
RxpProcessParams(PluginInstance* This, RxParams *in, RxReturnParams *out)
{
    char *x_ui_auth = NULL;
    char webserver[MAXHOSTNAMELEN];
    Boolean trusted, use_fwp, use_lbx;

#ifdef PLUGIN_TRACE
    fprintf (stderr, "%s\n", "RxpProcessParams");
    fprintf (stderr, "This: 0x%x\n", This);
#endif

    /* init return struture */
    memset(out, 0, sizeof(RxReturnParams));
    out->x_ui_lbx = RxUndef;
    out->x_print_lbx = RxUndef;
    out->action = in->action;

    if (in->embedded != RxUndef)
	out->embedded = in->embedded;
    else
	out->embedded = RxUndef;

    out->width = in->width;
    out->height = in->height;	

    if (RxGlobal.get_prefs == True) {
	GetPreferences(This->toplevel_widget, &RxGlobal.prefs);
	RxGlobal.get_prefs = False;
    }
    ComputePreferences(&RxGlobal.prefs,
       ParseHostname(in->action, webserver, MAXHOSTNAMELEN) ? webserver : NULL,
       &trusted, &use_fwp, &use_lbx);

    if (in->ui[0] == XUI)	/* X display needed */
	ProcessUIParams(This, trusted, use_fwp, use_lbx, in, out, &x_ui_auth);

    if (in->print[0] == XPrint) /* XPrint server needed */
	ProcessPrintParams(This, trusted, use_fwp, use_lbx,
			   in, out, x_ui_auth);

    if (x_ui_auth != NULL)
	NPN_MemFree(x_ui_auth);

    return 0;
}
