Milán Major

netscape-revival

1 branch
Code

cmd/xfe/remote-s.c

/* -*- Mode:C; tab-width: 8 -*-
   remote.c --- remote control of netscape.
   Copyright © 1996 Netscape Communications Corporation, all rights reserved.
   Created: Jamie Zawinski <jwz@netscape.com>, 24-Dec-94.
 */

#include "mozilla.h"
#include "xfe.h"

#define progname fe_progname
#define mozilla_remote_init_atoms fe_init_atoms
#define mozilla_remote_commands fe_RemoteCommands
static const char *expected_mozilla_version = fe_version;
#include "remote.c"


/* server side */

static void fe_property_change_action (Widget widget, XEvent *event,
				       String *av, Cardinal *ac);

static char *fe_server_handle_command (Display *dpy, Window window,
				       XEvent *event, char *command);

static XtTranslations fe_prop_translations;

void
fe_InitRemoteServer (Display *dpy)
{
  static Boolean done = False;
  static XtActionsRec actions [] =
    { { "HandleMozillaCommand", fe_property_change_action } };

  if (done) return;
  done = True;
  fe_init_atoms (dpy);

  XtAppAddActions (fe_XtAppContext, actions, countof (actions));

  fe_prop_translations =
    XtParseTranslationTable ("<PropertyNotify>: HandleMozillaCommand()");
}

void
fe_InitRemoteServerWindow (MWContext *context)
{
  Widget widget = CONTEXT_WIDGET (context);
  Display *dpy = XtDisplay (widget);
  Window window = XtWindow (widget);
  XWindowAttributes attrs;
  unsigned char *data = (unsigned char *) fe_version;

  XtOverrideTranslations (widget, fe_prop_translations);

#ifndef DEBUG_jwz
  /* don't let ns3 handle -remote requests at all. */
  XChangeProperty (dpy, window, XA_MOZILLA_VERSION, XA_STRING,
		   8, PropModeReplace, data, strlen (data));
#endif /* DEBUG_jwz */

  XGetWindowAttributes (dpy, window, &attrs);
  if (! (attrs.your_event_mask & PropertyChangeMask))
    XSelectInput (dpy, window, attrs.your_event_mask | PropertyChangeMask);
}


static void
fe_server_handle_property_change (Display *dpy, Window window, XEvent *event)
{
  if (event->xany.type == PropertyNotify &&
      event->xproperty.state == PropertyNewValue &&
      event->xproperty.window == window &&
      event->xproperty.atom == XA_MOZILLA_COMMAND)
    {
      int result;
      Atom actual_type;
      int actual_format;
      unsigned long nitems, bytes_after;
      unsigned char *data = 0;

      result = XGetWindowProperty (dpy, window, XA_MOZILLA_COMMAND,
				   0, (65536 / sizeof (long)),
				   True, /* atomic delete after */
				   XA_STRING,
				   &actual_type, &actual_format,
				   &nitems, &bytes_after,
				   &data);
      if (result != Success)
	{
	  fprintf (stderr, "%s: unable to read " MOZILLA_COMMAND_PROP
		   " property\n",
		   fe_progname);
	  return;
	}
      else if (!data || !*data)
	{
	  fprintf (stderr, "%s: invalid data on " MOZILLA_COMMAND_PROP
		   " of window 0x%x.\n",
		   fe_progname, (unsigned int) window);
	  return;
	}
      else
	{
	  char *response =
	    fe_server_handle_command (dpy, window, event, (char *) data);
	  if (! response) abort ();
	  XChangeProperty (dpy, window, XA_MOZILLA_RESPONSE, XA_STRING,
			   8, PropModeReplace, response, strlen (response));
	  free (response);
	}

      if (data)
	XFree (data);
    }
#ifdef DEBUG_PROPS
  else if (event->xany.type == PropertyNotify &&
	   event->xproperty.window == window &&
	   event->xproperty.state == PropertyDelete &&
	   event->xproperty.atom == XA_MOZILLA_RESPONSE)
    {
      fprintf (stderr, "%s: (client has accepted the response on 0x%x.)\n",
	       fe_progname, (unsigned int) window);
    }
  else if (event->xany.type == PropertyNotify &&
	   event->xproperty.window == window &&
	   event->xproperty.atom == XA_MOZILLA_LOCK)
    {
      fprintf (stderr, "%s: (client has %s a lock on 0x%x.)\n",
	       fe_progname,
	       (event->xproperty.state == PropertyNewValue
		? "obtained" : "freed"),
	       (unsigned int) window);
    }
#endif /* DEBUG_PROPS */

}

static void
fe_property_change_action (Widget widget, XEvent *event,
			   String *av, Cardinal *ac)
{
  MWContext *context = fe_WidgetToMWContext (widget);
  if (! context) return;
  fe_server_handle_property_change (XtDisplay (CONTEXT_WIDGET (context)),
				    XtWindow (CONTEXT_WIDGET (context)),
				    event);
}

static char *
fe_server_handle_command (Display *dpy, Window window, XEvent *event,
			  char *command)
{
  char *name = 0;
  char **av = 0;
  int ac = 0;
  int avsize = 0;
  Boolean raise_p = True;
  int i;
  char *buf;
  char *buf2;
  int32 buf2_size;
  char *head, *tail;
  XtActionProc action = 0;
  Widget widget = XtWindowToWidget (dpy, window);
  MWContext *context = fe_WidgetToMWContext (widget);
  XP_Bool mail_or_news_required = FALSE;
  MWContextType required_type = (MWContextType) (~0);
  XP_Bool make_context_if_necessary = FALSE;
  XP_Bool done_once = FALSE;

  XP_ASSERT(context);

  buf = fe_StringTrim (strdup (command));
  buf2_size = strlen (buf) + 200;
  buf2 = (char *) malloc (buf2_size);

  head = buf;
  tail = buf;

  if (! widget)
    {
      PR_snprintf (buf2, buf2_size,
	    "509 internal error: unable to translate"
	    "window 0x%x to a widget",
	    (unsigned int) window);
      free (buf);
      return buf2;
    }

  while (1)
    if (*tail == '(' || isspace (*tail) || !*tail)
      {
	*tail = 0;
	tail++;
	name = fe_StringTrim (head);
	break;
      }
    else
      tail++;

  if (!name || !*name)
    {
      PR_snprintf (buf2, buf2_size, "500 unparsable command: %s", command);
      free (buf);
      return buf2;
    }

  for (i = 0; i < fe_CommandActionsSize; i++)
    if (!strcasecmp (name, fe_CommandActions [i].string))
      {
	name = fe_CommandActions [i].string;
	action = fe_CommandActions [i].proc;
	break;
      }

  if (!action)
    for (i = 0; i < fe_MailNewsActionsSize; i++)
      if (!strcasecmp (name, fe_MailNewsActions [i].string))
	{
	  name = fe_MailNewsActions [i].string;
	  action = fe_MailNewsActions [i].proc;

	  if (strstr(name, "Mail") || strstr(name, "Folder"))
	    required_type = MWContextMail;
	  else if (strstr(name, "News") || strstr(name, "Group") ||
		   strstr(name, "Host"))
	    required_type = MWContextNews;
	  else
	    {
	      required_type = (MWContextType) (~0);
	      mail_or_news_required = TRUE;
	    }

	  break;
	}

  if (!action)
    {
      PR_snprintf (buf2, buf2_size, "501 unrecognised command: %s", name);
      free (buf);
      return buf2;
    }

 AGAIN:

  /* If the required context-type doesn't match, find one that does.
   */
  if ((mail_or_news_required &&
       context->type != MWContextMail &&
       context->type != MWContextNews) ||
      (required_type != (MWContextType) (~0) &&
       required_type != context->type))
    {
      MWContext *oc = context;
      MWContextType type = (mail_or_news_required
			    ? MWContextMail
			    : required_type);
      context = XP_FindContextOfType (context, type);

      if (!context && make_context_if_necessary)
	context = fe_MakeWindow (XtParent(widget), oc, 0, NULL, type, FALSE);

      widget = context ? CONTEXT_WIDGET(context) : 0;
      window = widget ? XtWindow(widget) : 0;
      if (!window)
	{
	  PR_snprintf (buf2, buf2_size, "502 no appropriate window for %s",
		       (av ? av[0] : name));
	  free (buf);
	  if (av) free (av);
	  return buf2;
	}
    }

  if (!av)
    {
      avsize = 20;
      av = (char **) calloc (avsize, sizeof (char *));

      while (*tail == '(' || isspace (*tail))
	tail++;

      head = tail;
      while (1)
	{
	  if (*tail == ')' || *tail == ',' || *tail == 0)
	    {
	      char delim = *tail;
	      if (ac >= (avsize - 2))
		{
		  avsize += 20;
		  av = (char **) realloc (av, avsize * sizeof (char *));
		}
	      *tail = 0;

	      av [ac++] = fe_StringTrim (head);

	      if (delim != ',' && !*av[ac-1])
		ac--;
	      else if (!strcasecomp (av [ac-1], "noraise"))
		{
		  raise_p = False;
		  ac--;
		}
	      else if (!strcasecomp (av [ac-1], "raise"))
		{
		  raise_p = True;
		  ac--;
		}
#ifdef DEBUG_jwz
              /* If the argument "new-window" will be passed to the actual
                 command, then don't bother raising this window (but do
                 leave that arg in the list.) */
	      else if (!strcasecomp (av[ac-1], "new-window") ||
                       !strcasecomp (av[ac-1], "new_window") ||
                       !strcasecomp (av[ac-1], "newWindow") ||
                       !strcasecomp (av[ac-1], "new"))
		{
		  raise_p = False;
		}
#endif /* DEBUG_jwz */


	      head = tail+1;
	      if (delim != ',')
		break;
	    }
	  tail++;
	}

      av [ac++] = "remote";
    }


  /* If this is GetURL or something like it, make sure the context we pick
     matches the URL.
   */
  if (!done_once &&
      required_type == (MWContextType) (~0) &&
      strstr(name, "URL"))
    {
      const char *url = av[0];
      mail_or_news_required = FALSE;
      required_type = (MWContextType) (~0);
      if (MSG_RequiresMailWindow (url))
	required_type = MWContextMail;
      else if (MSG_RequiresNewsWindow (url))
	required_type = MWContextNews;
      else if (MSG_RequiresBrowserWindow (url))
	required_type = MWContextBrowser;
      /* Nothing to do for MSG_RequiresComposeWindow compose. */

      if (required_type != (MWContextType) (~0))
	{
	  done_once = TRUE;
	  make_context_if_necessary = TRUE;
	  goto AGAIN;
	}
    }


  if (raise_p)
    XMapRaised (dpy, window);

  {
    Cardinal ac2 = ac; /* why is this passed as a pointer??? */
    (*action) (widget, event, av, &ac2);
  }

  PR_snprintf (buf2, buf2_size, "200 executed command: %s(", name);
  for (i = 0; i < ac-1; i++)
    {
      strcat (buf2, av [i]);
      if (i < ac-2)
	strcat (buf2, ", ");
    }
  strcat (buf2, ")");

  free (av);

  free (buf);
  return buf2;
}