/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=8:
 */
/* vim:set ts=8 sw=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/ArrayUtils.h"
#include "mozilla/Sprintf.h"
#include "XRemoteClient.h"
#include "prmem.h"
#include "plstr.h"
#include "prsystem.h"
#include "mozilla/Logging.h"
#include "prenv.h"
#include "prdtoa.h"
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include <X11/Xatom.h>

#define MOZILLA_VERSION_PROP   "_MOZILLA_VERSION"
#define MOZILLA_LOCK_PROP      "_MOZILLA_LOCK"
#define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
#define MOZILLA_RESPONSE_PROP  "_MOZILLA_RESPONSE"
#define MOZILLA_USER_PROP      "_MOZILLA_USER"
#define MOZILLA_PROFILE_PROP   "_MOZILLA_PROFILE"
#define MOZILLA_PROGRAM_PROP   "_MOZILLA_PROGRAM"

#ifdef IS_BIG_ENDIAN
#define TO_LITTLE_ENDIAN32(x) \
    ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
    (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
#else
#define TO_LITTLE_ENDIAN32(x) (x)
#endif
    
#ifndef MAX_PATH
#ifdef PATH_MAX
#define MAX_PATH PATH_MAX
#else
#define MAX_PATH 1024
#endif
#endif

using mozilla::LogLevel;

static PRLogModuleInfo *sRemoteLm = nullptr;

static int (*sOldHandler)(Display *, XErrorEvent *);
static bool sGotBadWindow;

XRemoteClient::XRemoteClient()
{
  mDisplay = 0;
  mInitialized = false;
  mMozVersionAtom = 0;
  mMozLockAtom = 0;
  mMozCommandLineAtom = 0;
  mMozResponseAtom = 0;
  mMozWMStateAtom = 0;
  mMozUserAtom = 0;
  mMozProfileAtom = 0;
  mMozProgramAtom = 0;
  mLockData = 0;
  if (!sRemoteLm)
    sRemoteLm = PR_NewLogModule("XRemoteClient");
  MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::XRemoteClient"));
}

XRemoteClient::~XRemoteClient()
{
  MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::~XRemoteClient"));
  if (mInitialized)
    Shutdown();
}

// Minimize the roundtrips to the X-server
static const char *XAtomNames[] = {
  MOZILLA_VERSION_PROP,
  MOZILLA_LOCK_PROP,
  MOZILLA_RESPONSE_PROP,
  "WM_STATE",
  MOZILLA_USER_PROP,
  MOZILLA_PROFILE_PROP,
  MOZILLA_PROGRAM_PROP,
  MOZILLA_COMMANDLINE_PROP
};
static Atom XAtoms[MOZ_ARRAY_LENGTH(XAtomNames)];

nsresult
XRemoteClient::Init()
{
  MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::Init"));

  if (mInitialized)
    return NS_OK;

  // try to open the display
  mDisplay = XOpenDisplay(0);
  if (!mDisplay)
    return NS_ERROR_FAILURE;

  // get our atoms
  XInternAtoms(mDisplay, const_cast<char**>(XAtomNames),
               MOZ_ARRAY_LENGTH(XAtomNames), False, XAtoms);

  int i = 0;
  mMozVersionAtom  = XAtoms[i++];
  mMozLockAtom     = XAtoms[i++];
  mMozResponseAtom = XAtoms[i++];
  mMozWMStateAtom  = XAtoms[i++];
  mMozUserAtom     = XAtoms[i++];
  mMozProfileAtom  = XAtoms[i++];
  mMozProgramAtom  = XAtoms[i++];
  mMozCommandLineAtom = XAtoms[i++];

  mInitialized = true;

  return NS_OK;
}

void
XRemoteClient::Shutdown (void)
{
  MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::Shutdown"));

  if (!mInitialized)
    return;

  // shut everything down
  XCloseDisplay(mDisplay);
  mDisplay = 0;
  mInitialized = false;
  if (mLockData) {
    free(mLockData);
    mLockData = 0;
  }
}

static int
HandleBadWindow(Display *display, XErrorEvent *event)
{
  if (event->error_code == BadWindow) {
    sGotBadWindow = true;
    return 0; // ignored
  }
  else {
    return (*sOldHandler)(display, event);
  }
}

nsresult
XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername,
                                const char *aProfile,
                                int32_t argc, char **argv,
                                const char* aDesktopStartupID,
                                char **aResponse, bool *aWindowFound)
{
  MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::SendCommandLine"));

  *aWindowFound = false;

  // FindBestWindow() iterates down the window hierarchy, so catch X errors
  // when windows get destroyed before being accessed.
  sOldHandler = XSetErrorHandler(HandleBadWindow);

  Window w = FindBestWindow(aProgram, aUsername, aProfile);

  nsresult rv = NS_OK;

  if (w) {
    // ok, let the caller know that we at least found a window.
    *aWindowFound = true;

    // Ignore BadWindow errors up to this point.  The last request from
    // FindBestWindow() was a synchronous XGetWindowProperty(), so no need to
    // Sync.  Leave the error handler installed to detect if w gets destroyed.
    sGotBadWindow = false;

    // make sure we get the right events on that window
    XSelectInput(mDisplay, w,
                 (PropertyChangeMask|StructureNotifyMask));

    bool destroyed = false;

    // get the lock on the window
    rv = GetLock(w, &destroyed);

    if (NS_SUCCEEDED(rv)) {
      // send our command
      rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse,
                             &destroyed);

      // if the window was destroyed, don't bother trying to free the
      // lock.
      if (!destroyed)
          FreeLock(w); // doesn't really matter what this returns

    }
  }

  XSetErrorHandler(sOldHandler);

  MOZ_LOG(sRemoteLm, LogLevel::Debug, ("SendCommandInternal returning 0x%x\n", rv));

  return rv;
}

Window
XRemoteClient::CheckWindow(Window aWindow)
{
  Atom type = None;
  int  format;
  unsigned long nitems, bytesafter;
  unsigned char *data;
  Window innerWindow;

  XGetWindowProperty(mDisplay, aWindow, mMozWMStateAtom,
		     0, 0, False, AnyPropertyType,
		     &type, &format, &nitems, &bytesafter, &data);

  if (type) {
    XFree(data);
    return aWindow;
  }

  // didn't find it here so check the children of this window
  innerWindow = CheckChildren(aWindow);

  if (innerWindow)
    return innerWindow;

  return aWindow;
}

Window
XRemoteClient::CheckChildren(Window aWindow)
{
  Window root, parent;
  Window *children;
  unsigned int nchildren;
  unsigned int i;
  Atom type = None;
  int format;
  unsigned long nitems, after;
  unsigned char *data;
  Window retval = None;
  
  if (!XQueryTree(mDisplay, aWindow, &root, &parent, &children,
		  &nchildren))
    return None;
  
  // scan the list first before recursing into the list of windows
  // which can get quite deep.
  for (i=0; !retval && (i < nchildren); i++) {
    XGetWindowProperty(mDisplay, children[i], mMozWMStateAtom,
		       0, 0, False, AnyPropertyType, &type, &format,
		       &nitems, &after, &data);
    if (type) {
      XFree(data);
      retval = children[i];
    }
  }

  // otherwise recurse into the list
  for (i=0; !retval && (i < nchildren); i++) {
    retval = CheckChildren(children[i]);
  }

  if (children)
    XFree((char *)children);

  return retval;
}

nsresult
XRemoteClient::GetLock(Window aWindow, bool *aDestroyed)
{
  bool locked = false;
  bool waited = false;
  *aDestroyed = false;

  nsresult rv = NS_OK;

  if (!mLockData) {
    
    char pidstr[32];
    char sysinfobuf[SYS_INFO_BUFFER_LENGTH];
    SprintfLiteral(pidstr, "pid%d@", getpid());
    PRStatus status;
    status = PR_GetSystemInfo(PR_SI_HOSTNAME, sysinfobuf,
			      SYS_INFO_BUFFER_LENGTH);
    if (status != PR_SUCCESS) {
      return NS_ERROR_FAILURE;
    }
    
    // allocate enough space for the string plus the terminating
    // char
    mLockData = (char *)malloc(strlen(pidstr) + strlen(sysinfobuf) + 1);
    if (!mLockData)
      return NS_ERROR_OUT_OF_MEMORY;

    strcpy(mLockData, pidstr);
    if (!strcat(mLockData, sysinfobuf))
      return NS_ERROR_FAILURE;
  }

  do {
    int result;
    Atom actual_type;
    int actual_format;
    unsigned long nitems, bytes_after;
    unsigned char *data = 0;

    XGrabServer(mDisplay);

    result = XGetWindowProperty (mDisplay, aWindow, mMozLockAtom,
				 0, (65536 / sizeof (long)),
				 False, /* don't delete */
				 XA_STRING,
				 &actual_type, &actual_format,
				 &nitems, &bytes_after,
				 &data);

    // aWindow may have been destroyed before XSelectInput was processed, in
    // which case there may not be any DestroyNotify event in the queue to
    // tell us.  XGetWindowProperty() was synchronous so error responses have
    // now been processed, setting sGotBadWindow.
    if (sGotBadWindow) {
      *aDestroyed = true;
      rv = NS_ERROR_FAILURE;
    }
    else if (result != Success || actual_type == None) {
      /* It's not now locked - lock it. */
      XChangeProperty (mDisplay, aWindow, mMozLockAtom, XA_STRING, 8,
		       PropModeReplace,
		       (unsigned char *)mLockData,
		       strlen(mLockData));
      locked = True;
    }

    XUngrabServer(mDisplay);
    XFlush(mDisplay); // ungrab now!

    if (!locked && !NS_FAILED(rv)) {
      /* We tried to grab the lock this time, and failed because someone
	 else is holding it already.  So, wait for a PropertyDelete event
	 to come in, and try again. */
      MOZ_LOG(sRemoteLm, LogLevel::Debug, 
	     ("window 0x%x is locked by %s; waiting...\n",
	      (unsigned int) aWindow, data));
      waited = True;
      while (1) {
	XEvent event;
	int select_retval;
	fd_set select_set;
	struct timeval delay;
	delay.tv_sec = 10;
	delay.tv_usec = 0;

	FD_ZERO(&select_set);
	// add the x event queue to the select set
	FD_SET(ConnectionNumber(mDisplay), &select_set);
	select_retval = select(ConnectionNumber(mDisplay) + 1,
			       &select_set, nullptr, nullptr, &delay);
	// did we time out?
	if (select_retval == 0) {
	  MOZ_LOG(sRemoteLm, LogLevel::Debug, ("timed out waiting for window\n"));
          rv = NS_ERROR_FAILURE;
          break;
	}
	MOZ_LOG(sRemoteLm, LogLevel::Debug, ("xevent...\n"));
	XNextEvent (mDisplay, &event);
	if (event.xany.type == DestroyNotify &&
	    event.xdestroywindow.window == aWindow) {
	  *aDestroyed = true;
          rv = NS_ERROR_FAILURE;
          break;
	}
	else if (event.xany.type == PropertyNotify &&
		 event.xproperty.state == PropertyDelete &&
		 event.xproperty.window == aWindow &&
		 event.xproperty.atom == mMozLockAtom) {
	  /* Ok!  Someone deleted their lock, so now we can try
	     again. */
	  MOZ_LOG(sRemoteLm, LogLevel::Debug,
		 ("(0x%x unlocked, trying again...)\n",
		  (unsigned int) aWindow));
		  break;
	}
      }
    }
    if (data)
      XFree(data);
  } while (!locked && !NS_FAILED(rv));

  if (waited && locked) {
    MOZ_LOG(sRemoteLm, LogLevel::Debug, ("obtained lock.\n"));
  } else if (*aDestroyed) {
    MOZ_LOG(sRemoteLm, LogLevel::Debug,
           ("window 0x%x unexpectedly destroyed.\n",
            (unsigned int) aWindow));
  }

  return rv;
}

Window
XRemoteClient::FindBestWindow(const char *aProgram, const char *aUsername,
                              const char *aProfile)
{
  Window root = RootWindowOfScreen(DefaultScreenOfDisplay(mDisplay));
  Window bestWindow = 0;
  Window root2, parent, *kids;
  unsigned int nkids;

  // Get a list of the children of the root window, walk the list
  // looking for the best window that fits the criteria.
  if (!XQueryTree(mDisplay, root, &root2, &parent, &kids, &nkids)) {
    MOZ_LOG(sRemoteLm, LogLevel::Debug,
           ("XQueryTree failed in XRemoteClient::FindBestWindow"));
    return 0;
  }

  if (!(kids && nkids)) {
    MOZ_LOG(sRemoteLm, LogLevel::Debug, ("root window has no children"));
    return 0;
  }

  // We'll walk the list of windows looking for a window that best
  // fits the criteria here.

  for (unsigned int i = 0; i < nkids; i++) {
    Atom type;
    int format;
    unsigned long nitems, bytesafter;
    unsigned char *data_return = 0;
    Window w;
    w = kids[i];
    // find the inner window with WM_STATE on it
    w = CheckWindow(w);

    int status = XGetWindowProperty(mDisplay, w, mMozVersionAtom,
                                    0, (65536 / sizeof (long)),
                                    False, XA_STRING,
                                    &type, &format, &nitems, &bytesafter,
                                    &data_return);

    if (!data_return)
      continue;

    double version = PR_strtod((char*) data_return, nullptr);
    XFree(data_return);

    if (!(version >= 5.1 && version < 6))
      continue;

    data_return = 0;

    if (status != Success || type == None)
      continue;

    // If someone passed in a program name, check it against this one
    // unless it's "any" in which case, we don't care.  If someone did
    // pass in a program name and this window doesn't support that
    // protocol, we don't include it in our list.
    if (aProgram && strcmp(aProgram, "any")) {
        status = XGetWindowProperty(mDisplay, w, mMozProgramAtom,
                                    0, (65536 / sizeof(long)),
                                    False, XA_STRING,
                                    &type, &format, &nitems, &bytesafter,
                                    &data_return);
        
        // If the return name is not the same as what someone passed in,
        // we don't want this window.
        if (data_return) {
            if (strcmp(aProgram, (const char *)data_return)) {
                XFree(data_return);
                continue;
            }

            // This is actually the success condition.
            XFree(data_return);
        }
        else {
            // Doesn't support the protocol, even though the user
            // requested it.  So we're not going to use this window.
            continue;
        }
    }

    // Check to see if it has the user atom on that window.  If there
    // is then we need to make sure that it matches what we have.
    const char *username;
    if (aUsername) {
      username = aUsername;
    }
    else {
      username = PR_GetEnv("LOGNAME");
    }

    if (username) {
        status = XGetWindowProperty(mDisplay, w, mMozUserAtom,
                                    0, (65536 / sizeof(long)),
                                    False, XA_STRING,
                                    &type, &format, &nitems, &bytesafter,
                                    &data_return);

        // if there's a username compare it with what we have
        if (data_return) {
            // If the IDs aren't equal, we don't want this window.
            if (strcmp(username, (const char *)data_return)) {
                XFree(data_return);
                continue;
            }

            XFree(data_return);
        }
    }

    // Check to see if there's a profile name on this window.  If
    // there is, then we need to make sure it matches what someone
    // passed in.
    if (aProfile) {
        status = XGetWindowProperty(mDisplay, w, mMozProfileAtom,
                                    0, (65536 / sizeof(long)),
                                    False, XA_STRING,
                                    &type, &format, &nitems, &bytesafter,
                                    &data_return);

        // If there's a profile compare it with what we have
        if (data_return) {
            // If the profiles aren't equal, we don't want this window.
            if (strcmp(aProfile, (const char *)data_return)) {
                XFree(data_return);
                continue;
            }

            XFree(data_return);
        }
    }

    // Check to see if the window supports the new command-line passing
    // protocol, if that is requested.

    // If we got this far, this is the best window.  It passed
    // all the tests.
    bestWindow = w;
    break;
  }

  if (kids)
    XFree((char *) kids);

  return bestWindow;
}

nsresult
XRemoteClient::FreeLock(Window aWindow)
{
  int result;
  Atom actual_type;
  int actual_format;
  unsigned long nitems, bytes_after;
  unsigned char *data = 0;

  result = XGetWindowProperty(mDisplay, aWindow, mMozLockAtom,
                              0, (65536 / sizeof(long)),
                              True, /* atomic delete after */
                              XA_STRING,
                              &actual_type, &actual_format,
                              &nitems, &bytes_after,
                              &data);
  if (result != Success) {
      MOZ_LOG(sRemoteLm, LogLevel::Debug,
             ("unable to read and delete " MOZILLA_LOCK_PROP
              " property\n"));
      return NS_ERROR_FAILURE;
  }
  else if (!data || !*data){
      MOZ_LOG(sRemoteLm, LogLevel::Debug,
             ("invalid data on " MOZILLA_LOCK_PROP
              " of window 0x%x.\n",
              (unsigned int) aWindow));
      return NS_ERROR_FAILURE;
  }
  else if (strcmp((char *)data, mLockData)) {
      MOZ_LOG(sRemoteLm, LogLevel::Debug,
             (MOZILLA_LOCK_PROP " was stolen!  Expected \"%s\", saw \"%s\"!\n",
              mLockData, data));
      return NS_ERROR_FAILURE;
  }

  if (data)
      XFree(data);
  return NS_OK;
}

/* like strcpy, but return the char after the final null */
static char*
estrcpy(const char* s, char* d)
{
  while (*s)
    *d++ = *s++;

  *d++ = '\0';
  return d;
}

nsresult
XRemoteClient::DoSendCommandLine(Window aWindow, int32_t argc, char **argv,
                                 const char* aDesktopStartupID,
                                 char **aResponse, bool *aDestroyed)
{
  *aDestroyed = false;

  char cwdbuf[MAX_PATH];
  if (!getcwd(cwdbuf, MAX_PATH))
    return NS_ERROR_UNEXPECTED;

  // the commandline property is constructed as an array of int32_t
  // followed by a series of null-terminated strings:
  //
  // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
  // (offset is from the beginning of the buffer)

  static char desktopStartupPrefix[] = " DESKTOP_STARTUP_ID=";

  int32_t argvlen = strlen(cwdbuf);
  for (int i = 0; i < argc; ++i) {
    int32_t len = strlen(argv[i]);
    if (i == 0 && aDesktopStartupID) {
      len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID);
    }
    argvlen += len;
  }

  int32_t* buffer = (int32_t*) malloc(argvlen + argc + 1 +
                                      sizeof(int32_t) * (argc + 1));
  if (!buffer)
    return NS_ERROR_OUT_OF_MEMORY;

  buffer[0] = TO_LITTLE_ENDIAN32(argc);

  char *bufend = (char*) (buffer + argc + 1);

  bufend = estrcpy(cwdbuf, bufend);

  for (int i = 0; i < argc; ++i) {
    buffer[i + 1] = TO_LITTLE_ENDIAN32(bufend - ((char*) buffer));
    bufend = estrcpy(argv[i], bufend);
    if (i == 0 && aDesktopStartupID) {
      bufend = estrcpy(desktopStartupPrefix, bufend - 1);
      bufend = estrcpy(aDesktopStartupID, bufend - 1);
    }
  }

#ifdef DEBUG_bsmedberg
  int32_t   debug_argc   = TO_LITTLE_ENDIAN32(*buffer);
  char *debug_workingdir = (char*) (buffer + argc + 1);

  printf("Sending command line:\n"
         "  working dir: %s\n"
         "  argc:\t%i",
         debug_workingdir,
         debug_argc);

  int32_t  *debug_offset = buffer + 1;
  for (int debug_i = 0; debug_i < debug_argc; ++debug_i)
    printf("  argv[%i]:\t%s\n", debug_i,
           ((char*) buffer) + TO_LITTLE_ENDIAN32(debug_offset[debug_i]));
#endif

  XChangeProperty (mDisplay, aWindow, mMozCommandLineAtom, XA_STRING, 8,
                   PropModeReplace, (unsigned char *) buffer,
                   bufend - ((char*) buffer));
  free(buffer);

  if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandLineAtom))
    return NS_ERROR_FAILURE;
  
  return NS_OK;
}

bool
XRemoteClient::WaitForResponse(Window aWindow, char **aResponse,
                               bool *aDestroyed, Atom aCommandAtom)
{
  bool done = false;
  bool accepted = false;

  while (!done) {
    XEvent event;
    XNextEvent (mDisplay, &event);
    if (event.xany.type == DestroyNotify &&
        event.xdestroywindow.window == aWindow) {
      /* Print to warn user...*/
      MOZ_LOG(sRemoteLm, LogLevel::Debug,
             ("window 0x%x was destroyed.\n",
              (unsigned int) aWindow));
      *aResponse = strdup("Window was destroyed while reading response.");
      *aDestroyed = true;
      return false;
    }
    else if (event.xany.type == PropertyNotify &&
             event.xproperty.state == PropertyNewValue &&
             event.xproperty.window == aWindow &&
             event.xproperty.atom == mMozResponseAtom) {
      Atom actual_type;
      int actual_format;
      unsigned long nitems, bytes_after;
      unsigned char *data = 0;
      Bool result;
      result = XGetWindowProperty (mDisplay, aWindow, mMozResponseAtom,
                                   0, (65536 / sizeof (long)),
                                   True, /* atomic delete after */
                                   XA_STRING,
                                   &actual_type, &actual_format,
                                   &nitems, &bytes_after,
                                   &data);
      if (result != Success) {
        MOZ_LOG(sRemoteLm, LogLevel::Debug,
               ("failed reading " MOZILLA_RESPONSE_PROP
                " from window 0x%0x.\n",
                (unsigned int) aWindow));
        *aResponse = strdup("Internal error reading response from window.");
        done = true;
      }
      else if (!data || strlen((char *) data) < 5) {
        MOZ_LOG(sRemoteLm, LogLevel::Debug,
               ("invalid data on " MOZILLA_RESPONSE_PROP
                " property of window 0x%0x.\n",
                (unsigned int) aWindow));
        *aResponse = strdup("Server returned invalid data in response.");
        done = true;
      }
      else if (*data == '1') {  /* positive preliminary reply */
        MOZ_LOG(sRemoteLm, LogLevel::Debug,  ("%s\n", data + 4));
        /* keep going */
        done = false;
      }

      else if (!strncmp ((char *)data, "200", 3)) { /* positive completion */
        *aResponse = strdup((char *)data);
        accepted = true;
        done = true;
      }

      else if (*data == '2') {  /* positive completion */
        MOZ_LOG(sRemoteLm, LogLevel::Debug, ("%s\n", data + 4));
        *aResponse = strdup((char *)data);
        accepted = true;
        done = true;
      }

      else if (*data == '3') {  /* positive intermediate reply */
        MOZ_LOG(sRemoteLm, LogLevel::Debug,
               ("internal error: "
                "server wants more information?  (%s)\n",
                data));
        *aResponse = strdup((char *)data);
        done = true;
      }

      else if (*data == '4' ||  /* transient negative completion */
               *data == '5') {  /* permanent negative completion */
        MOZ_LOG(sRemoteLm, LogLevel::Debug, ("%s\n", data + 4));
        *aResponse = strdup((char *)data);
        done = true;
      }

      else {
        MOZ_LOG(sRemoteLm, LogLevel::Debug,
               ("unrecognised " MOZILLA_RESPONSE_PROP
                " from window 0x%x: %s\n",
                (unsigned int) aWindow, data));
        *aResponse = strdup((char *)data);
        done = true;
      }

      if (data)
        XFree(data);
    }

    else if (event.xany.type == PropertyNotify &&
             event.xproperty.window == aWindow &&
             event.xproperty.state == PropertyDelete &&
             event.xproperty.atom == aCommandAtom) {
      MOZ_LOG(sRemoteLm, LogLevel::Debug,
             ("(server 0x%x has accepted "
              MOZILLA_COMMANDLINE_PROP ".)\n",
              (unsigned int) aWindow));
    }
    
  }

  return accepted;
}