widget/xremoteclient/XRemoteClient.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:expandtab:shiftwidth=2:tabstop=8:
     3  */
     4 /* vim:set ts=8 sw=2 et cindent: */
     5 /* This Source Code Form is subject to the terms of the Mozilla Public
     6  * License, v. 2.0. If a copy of the MPL was not distributed with this
     7  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     9 #include "XRemoteClient.h"
    10 #include "prmem.h"
    11 #include "prprf.h"
    12 #include "plstr.h"
    13 #include "prsystem.h"
    14 #include "prlog.h"
    15 #include "prenv.h"
    16 #include "prdtoa.h"
    17 #include <stdlib.h>
    18 #include <unistd.h>
    19 #include <string.h>
    20 #include <strings.h>
    21 #include <sys/time.h>
    22 #include <sys/types.h>
    23 #include <unistd.h>
    24 #include <limits.h>
    25 #include <X11/Xatom.h>
    27 #define MOZILLA_VERSION_PROP   "_MOZILLA_VERSION"
    28 #define MOZILLA_LOCK_PROP      "_MOZILLA_LOCK"
    29 #define MOZILLA_COMMAND_PROP   "_MOZILLA_COMMAND"
    30 #define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
    31 #define MOZILLA_RESPONSE_PROP  "_MOZILLA_RESPONSE"
    32 #define MOZILLA_USER_PROP      "_MOZILLA_USER"
    33 #define MOZILLA_PROFILE_PROP   "_MOZILLA_PROFILE"
    34 #define MOZILLA_PROGRAM_PROP   "_MOZILLA_PROGRAM"
    36 #ifdef IS_BIG_ENDIAN
    37 #define TO_LITTLE_ENDIAN32(x) \
    38     ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
    39     (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
    40 #else
    41 #define TO_LITTLE_ENDIAN32(x) (x)
    42 #endif
    44 #ifndef MAX_PATH
    45 #ifdef PATH_MAX
    46 #define MAX_PATH PATH_MAX
    47 #else
    48 #define MAX_PATH 1024
    49 #endif
    50 #endif
    52 #define ARRAY_LENGTH(array_) (sizeof(array_)/sizeof(array_[0]))
    54 static PRLogModuleInfo *sRemoteLm = nullptr;
    56 static int (*sOldHandler)(Display *, XErrorEvent *);
    57 static bool sGotBadWindow;
    59 XRemoteClient::XRemoteClient()
    60 {
    61   mDisplay = 0;
    62   mInitialized = false;
    63   mMozVersionAtom = 0;
    64   mMozLockAtom = 0;
    65   mMozCommandAtom = 0;
    66   mMozResponseAtom = 0;
    67   mMozWMStateAtom = 0;
    68   mMozUserAtom = 0;
    69   mLockData = 0;
    70   if (!sRemoteLm)
    71     sRemoteLm = PR_NewLogModule("XRemoteClient");
    72   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::XRemoteClient"));
    73 }
    75 XRemoteClient::~XRemoteClient()
    76 {
    77   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::~XRemoteClient"));
    78   if (mInitialized)
    79     Shutdown();
    80 }
    82 // Minimize the roundtrips to the X-server
    83 static const char *XAtomNames[] = {
    84   MOZILLA_VERSION_PROP,
    85   MOZILLA_LOCK_PROP,
    86   MOZILLA_COMMAND_PROP,
    87   MOZILLA_RESPONSE_PROP,
    88   "WM_STATE",
    89   MOZILLA_USER_PROP,
    90   MOZILLA_PROFILE_PROP,
    91   MOZILLA_PROGRAM_PROP,
    92   MOZILLA_COMMANDLINE_PROP
    93 };
    94 static Atom XAtoms[ARRAY_LENGTH(XAtomNames)];
    96 nsresult
    97 XRemoteClient::Init()
    98 {
    99   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::Init"));
   101   if (mInitialized)
   102     return NS_OK;
   104   // try to open the display
   105   mDisplay = XOpenDisplay(0);
   106   if (!mDisplay)
   107     return NS_ERROR_FAILURE;
   109   // get our atoms
   110   XInternAtoms(mDisplay, const_cast<char**>(XAtomNames),
   111                ARRAY_LENGTH(XAtomNames), False, XAtoms);
   113   int i = 0;
   114   mMozVersionAtom  = XAtoms[i++];
   115   mMozLockAtom     = XAtoms[i++];
   116   mMozCommandAtom  = XAtoms[i++];
   117   mMozResponseAtom = XAtoms[i++];
   118   mMozWMStateAtom  = XAtoms[i++];
   119   mMozUserAtom     = XAtoms[i++];
   120   mMozProfileAtom  = XAtoms[i++];
   121   mMozProgramAtom  = XAtoms[i++];
   122   mMozCommandLineAtom = XAtoms[i++];
   124   mInitialized = true;
   126   return NS_OK;
   127 }
   129 void
   130 XRemoteClient::Shutdown (void)
   131 {
   132   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::Shutdown"));
   134   if (!mInitialized)
   135     return;
   137   // shut everything down
   138   XCloseDisplay(mDisplay);
   139   mDisplay = 0;
   140   mInitialized = false;
   141   if (mLockData) {
   142     free(mLockData);
   143     mLockData = 0;
   144   }
   145 }
   147 nsresult
   148 XRemoteClient::SendCommand (const char *aProgram, const char *aUsername,
   149                             const char *aProfile, const char *aCommand,
   150                             const char* aDesktopStartupID,
   151                             char **aResponse, bool *aWindowFound)
   152 {
   153   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommand"));
   155   return SendCommandInternal(aProgram, aUsername, aProfile,
   156                              aCommand, 0, nullptr,
   157                              aDesktopStartupID,
   158                              aResponse, aWindowFound);
   159 }
   161 nsresult
   162 XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername,
   163                                 const char *aProfile,
   164                                 int32_t argc, char **argv,
   165                                 const char* aDesktopStartupID,
   166                                 char **aResponse, bool *aWindowFound)
   167 {
   168   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommandLine"));
   170   return SendCommandInternal(aProgram, aUsername, aProfile,
   171                              nullptr, argc, argv,
   172                              aDesktopStartupID,
   173                              aResponse, aWindowFound);
   174 }
   176 static int
   177 HandleBadWindow(Display *display, XErrorEvent *event)
   178 {
   179   if (event->error_code == BadWindow) {
   180     sGotBadWindow = true;
   181     return 0; // ignored
   182   }
   183   else {
   184     return (*sOldHandler)(display, event);
   185   }
   186 }
   188 nsresult
   189 XRemoteClient::SendCommandInternal(const char *aProgram, const char *aUsername,
   190                                    const char *aProfile, const char *aCommand,
   191                                    int32_t argc, char **argv,
   192                                    const char* aDesktopStartupID,
   193                                    char **aResponse, bool *aWindowFound)
   194 {
   195   *aWindowFound = false;
   196   bool isCommandLine = !aCommand;
   198   // FindBestWindow() iterates down the window hierarchy, so catch X errors
   199   // when windows get destroyed before being accessed.
   200   sOldHandler = XSetErrorHandler(HandleBadWindow);
   202   Window w = FindBestWindow(aProgram, aUsername, aProfile, isCommandLine);
   204   nsresult rv = NS_OK;
   206   if (w) {
   207     // ok, let the caller know that we at least found a window.
   208     *aWindowFound = true;
   210     // Ignore BadWindow errors up to this point.  The last request from
   211     // FindBestWindow() was a synchronous XGetWindowProperty(), so no need to
   212     // Sync.  Leave the error handler installed to detect if w gets destroyed.
   213     sGotBadWindow = false;
   215     // make sure we get the right events on that window
   216     XSelectInput(mDisplay, w,
   217                  (PropertyChangeMask|StructureNotifyMask));
   219     bool destroyed = false;
   221     // get the lock on the window
   222     rv = GetLock(w, &destroyed);
   224     if (NS_SUCCEEDED(rv)) {
   225       // send our command
   226       if (isCommandLine) {
   227         rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse,
   228                                &destroyed);
   229       }
   230       else {
   231         rv = DoSendCommand(w, aCommand, aDesktopStartupID, aResponse,
   232                            &destroyed);
   233       }
   235       // if the window was destroyed, don't bother trying to free the
   236       // lock.
   237       if (!destroyed)
   238           FreeLock(w); // doesn't really matter what this returns
   240     }
   241   }
   243   XSetErrorHandler(sOldHandler);
   245   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("SendCommandInternal returning 0x%x\n", rv));
   247   return rv;
   248 }
   250 Window
   251 XRemoteClient::CheckWindow(Window aWindow)
   252 {
   253   Atom type = None;
   254   int  format;
   255   unsigned long nitems, bytesafter;
   256   unsigned char *data;
   257   Window innerWindow;
   259   XGetWindowProperty(mDisplay, aWindow, mMozWMStateAtom,
   260 		     0, 0, False, AnyPropertyType,
   261 		     &type, &format, &nitems, &bytesafter, &data);
   263   if (type) {
   264     XFree(data);
   265     return aWindow;
   266   }
   268   // didn't find it here so check the children of this window
   269   innerWindow = CheckChildren(aWindow);
   271   if (innerWindow)
   272     return innerWindow;
   274   return aWindow;
   275 }
   277 Window
   278 XRemoteClient::CheckChildren(Window aWindow)
   279 {
   280   Window root, parent;
   281   Window *children;
   282   unsigned int nchildren;
   283   unsigned int i;
   284   Atom type = None;
   285   int format;
   286   unsigned long nitems, after;
   287   unsigned char *data;
   288   Window retval = None;
   290   if (!XQueryTree(mDisplay, aWindow, &root, &parent, &children,
   291 		  &nchildren))
   292     return None;
   294   // scan the list first before recursing into the list of windows
   295   // which can get quite deep.
   296   for (i=0; !retval && (i < nchildren); i++) {
   297     XGetWindowProperty(mDisplay, children[i], mMozWMStateAtom,
   298 		       0, 0, False, AnyPropertyType, &type, &format,
   299 		       &nitems, &after, &data);
   300     if (type) {
   301       XFree(data);
   302       retval = children[i];
   303     }
   304   }
   306   // otherwise recurse into the list
   307   for (i=0; !retval && (i < nchildren); i++) {
   308     retval = CheckChildren(children[i]);
   309   }
   311   if (children)
   312     XFree((char *)children);
   314   return retval;
   315 }
   317 nsresult
   318 XRemoteClient::GetLock(Window aWindow, bool *aDestroyed)
   319 {
   320   bool locked = false;
   321   bool waited = false;
   322   *aDestroyed = false;
   324   nsresult rv = NS_OK;
   326   if (!mLockData) {
   328     char pidstr[32];
   329     char sysinfobuf[SYS_INFO_BUFFER_LENGTH];
   330     PR_snprintf(pidstr, sizeof(pidstr), "pid%d@", getpid());
   331     PRStatus status;
   332     status = PR_GetSystemInfo(PR_SI_HOSTNAME, sysinfobuf,
   333 			      SYS_INFO_BUFFER_LENGTH);
   334     if (status != PR_SUCCESS) {
   335       return NS_ERROR_FAILURE;
   336     }
   338     // allocate enough space for the string plus the terminating
   339     // char
   340     mLockData = (char *)malloc(strlen(pidstr) + strlen(sysinfobuf) + 1);
   341     if (!mLockData)
   342       return NS_ERROR_OUT_OF_MEMORY;
   344     strcpy(mLockData, pidstr);
   345     if (!strcat(mLockData, sysinfobuf))
   346       return NS_ERROR_FAILURE;
   347   }
   349   do {
   350     int result;
   351     Atom actual_type;
   352     int actual_format;
   353     unsigned long nitems, bytes_after;
   354     unsigned char *data = 0;
   356     XGrabServer(mDisplay);
   358     result = XGetWindowProperty (mDisplay, aWindow, mMozLockAtom,
   359 				 0, (65536 / sizeof (long)),
   360 				 False, /* don't delete */
   361 				 XA_STRING,
   362 				 &actual_type, &actual_format,
   363 				 &nitems, &bytes_after,
   364 				 &data);
   366     // aWindow may have been destroyed before XSelectInput was processed, in
   367     // which case there may not be any DestroyNotify event in the queue to
   368     // tell us.  XGetWindowProperty() was synchronous so error responses have
   369     // now been processed, setting sGotBadWindow.
   370     if (sGotBadWindow) {
   371       *aDestroyed = true;
   372       rv = NS_ERROR_FAILURE;
   373     }
   374     else if (result != Success || actual_type == None) {
   375       /* It's not now locked - lock it. */
   376       XChangeProperty (mDisplay, aWindow, mMozLockAtom, XA_STRING, 8,
   377 		       PropModeReplace,
   378 		       (unsigned char *)mLockData,
   379 		       strlen(mLockData));
   380       locked = True;
   381     }
   383     XUngrabServer(mDisplay);
   384     XFlush(mDisplay); // ungrab now!
   386     if (!locked && !NS_FAILED(rv)) {
   387       /* We tried to grab the lock this time, and failed because someone
   388 	 else is holding it already.  So, wait for a PropertyDelete event
   389 	 to come in, and try again. */
   390       PR_LOG(sRemoteLm, PR_LOG_DEBUG, 
   391 	     ("window 0x%x is locked by %s; waiting...\n",
   392 	      (unsigned int) aWindow, data));
   393       waited = True;
   394       while (1) {
   395 	XEvent event;
   396 	int select_retval;
   397 	fd_set select_set;
   398 	struct timeval delay;
   399 	delay.tv_sec = 10;
   400 	delay.tv_usec = 0;
   402 	FD_ZERO(&select_set);
   403 	// add the x event queue to the select set
   404 	FD_SET(ConnectionNumber(mDisplay), &select_set);
   405 	select_retval = select(ConnectionNumber(mDisplay) + 1,
   406 			       &select_set, nullptr, nullptr, &delay);
   407 	// did we time out?
   408 	if (select_retval == 0) {
   409 	  PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("timed out waiting for window\n"));
   410           rv = NS_ERROR_FAILURE;
   411           break;
   412 	}
   413 	PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("xevent...\n"));
   414 	XNextEvent (mDisplay, &event);
   415 	if (event.xany.type == DestroyNotify &&
   416 	    event.xdestroywindow.window == aWindow) {
   417 	  *aDestroyed = true;
   418           rv = NS_ERROR_FAILURE;
   419           break;
   420 	}
   421 	else if (event.xany.type == PropertyNotify &&
   422 		 event.xproperty.state == PropertyDelete &&
   423 		 event.xproperty.window == aWindow &&
   424 		 event.xproperty.atom == mMozLockAtom) {
   425 	  /* Ok!  Someone deleted their lock, so now we can try
   426 	     again. */
   427 	  PR_LOG(sRemoteLm, PR_LOG_DEBUG,
   428 		 ("(0x%x unlocked, trying again...)\n",
   429 		  (unsigned int) aWindow));
   430 		  break;
   431 	}
   432       }
   433     }
   434     if (data)
   435       XFree(data);
   436   } while (!locked && !NS_FAILED(rv));
   438   if (waited && locked) {
   439     PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("obtained lock.\n"));
   440   } else if (*aDestroyed) {
   441     PR_LOG(sRemoteLm, PR_LOG_DEBUG,
   442            ("window 0x%x unexpectedly destroyed.\n",
   443             (unsigned int) aWindow));
   444   }
   446   return rv;
   447 }
   449 Window
   450 XRemoteClient::FindBestWindow(const char *aProgram, const char *aUsername,
   451                               const char *aProfile,
   452                               bool aSupportsCommandLine)
   453 {
   454   Window root = RootWindowOfScreen(DefaultScreenOfDisplay(mDisplay));
   455   Window bestWindow = 0;
   456   Window root2, parent, *kids;
   457   unsigned int nkids;
   459   // Get a list of the children of the root window, walk the list
   460   // looking for the best window that fits the criteria.
   461   if (!XQueryTree(mDisplay, root, &root2, &parent, &kids, &nkids)) {
   462     PR_LOG(sRemoteLm, PR_LOG_DEBUG,
   463            ("XQueryTree failed in XRemoteClient::FindBestWindow"));
   464     return 0;
   465   }
   467   if (!(kids && nkids)) {
   468     PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("root window has no children"));
   469     return 0;
   470   }
   472   // We'll walk the list of windows looking for a window that best
   473   // fits the criteria here.
   475   for (unsigned int i = 0; i < nkids; i++) {
   476     Atom type;
   477     int format;
   478     unsigned long nitems, bytesafter;
   479     unsigned char *data_return = 0;
   480     Window w;
   481     w = kids[i];
   482     // find the inner window with WM_STATE on it
   483     w = CheckWindow(w);
   485     int status = XGetWindowProperty(mDisplay, w, mMozVersionAtom,
   486                                     0, (65536 / sizeof (long)),
   487                                     False, XA_STRING,
   488                                     &type, &format, &nitems, &bytesafter,
   489                                     &data_return);
   491     if (!data_return)
   492       continue;
   494     double version = PR_strtod((char*) data_return, nullptr);
   495     XFree(data_return);
   497     if (aSupportsCommandLine && !(version >= 5.1 && version < 6))
   498       continue;
   500     data_return = 0;
   502     if (status != Success || type == None)
   503       continue;
   505     // If someone passed in a program name, check it against this one
   506     // unless it's "any" in which case, we don't care.  If someone did
   507     // pass in a program name and this window doesn't support that
   508     // protocol, we don't include it in our list.
   509     if (aProgram && strcmp(aProgram, "any")) {
   510         status = XGetWindowProperty(mDisplay, w, mMozProgramAtom,
   511                                     0, (65536 / sizeof(long)),
   512                                     False, XA_STRING,
   513                                     &type, &format, &nitems, &bytesafter,
   514                                     &data_return);
   516         // If the return name is not the same as what someone passed in,
   517         // we don't want this window.
   518         if (data_return) {
   519             if (strcmp(aProgram, (const char *)data_return)) {
   520                 XFree(data_return);
   521                 continue;
   522             }
   524             // This is actually the success condition.
   525             XFree(data_return);
   526         }
   527         else {
   528             // Doesn't support the protocol, even though the user
   529             // requested it.  So we're not going to use this window.
   530             continue;
   531         }
   532     }
   534     // Check to see if it has the user atom on that window.  If there
   535     // is then we need to make sure that it matches what we have.
   536     const char *username;
   537     if (aUsername) {
   538       username = aUsername;
   539     }
   540     else {
   541       username = PR_GetEnv("LOGNAME");
   542     }
   544     if (username) {
   545         status = XGetWindowProperty(mDisplay, w, mMozUserAtom,
   546                                     0, (65536 / sizeof(long)),
   547                                     False, XA_STRING,
   548                                     &type, &format, &nitems, &bytesafter,
   549                                     &data_return);
   551         // if there's a username compare it with what we have
   552         if (data_return) {
   553             // If the IDs aren't equal, we don't want this window.
   554             if (strcmp(username, (const char *)data_return)) {
   555                 XFree(data_return);
   556                 continue;
   557             }
   559             XFree(data_return);
   560         }
   561     }
   563     // Check to see if there's a profile name on this window.  If
   564     // there is, then we need to make sure it matches what someone
   565     // passed in.
   566     if (aProfile) {
   567         status = XGetWindowProperty(mDisplay, w, mMozProfileAtom,
   568                                     0, (65536 / sizeof(long)),
   569                                     False, XA_STRING,
   570                                     &type, &format, &nitems, &bytesafter,
   571                                     &data_return);
   573         // If there's a profile compare it with what we have
   574         if (data_return) {
   575             // If the profiles aren't equal, we don't want this window.
   576             if (strcmp(aProfile, (const char *)data_return)) {
   577                 XFree(data_return);
   578                 continue;
   579             }
   581             XFree(data_return);
   582         }
   583     }
   585     // Check to see if the window supports the new command-line passing
   586     // protocol, if that is requested.
   588     // If we got this far, this is the best window.  It passed
   589     // all the tests.
   590     bestWindow = w;
   591     break;
   592   }
   594   if (kids)
   595     XFree((char *) kids);
   597   return bestWindow;
   598 }
   600 nsresult
   601 XRemoteClient::FreeLock(Window aWindow)
   602 {
   603   int result;
   604   Atom actual_type;
   605   int actual_format;
   606   unsigned long nitems, bytes_after;
   607   unsigned char *data = 0;
   609   result = XGetWindowProperty(mDisplay, aWindow, mMozLockAtom,
   610                               0, (65536 / sizeof(long)),
   611                               True, /* atomic delete after */
   612                               XA_STRING,
   613                               &actual_type, &actual_format,
   614                               &nitems, &bytes_after,
   615                               &data);
   616   if (result != Success) {
   617       PR_LOG(sRemoteLm, PR_LOG_DEBUG,
   618              ("unable to read and delete " MOZILLA_LOCK_PROP
   619               " property\n"));
   620       return NS_ERROR_FAILURE;
   621   }
   622   else if (!data || !*data){
   623       PR_LOG(sRemoteLm, PR_LOG_DEBUG,
   624              ("invalid data on " MOZILLA_LOCK_PROP
   625               " of window 0x%x.\n",
   626               (unsigned int) aWindow));
   627       return NS_ERROR_FAILURE;
   628   }
   629   else if (strcmp((char *)data, mLockData)) {
   630       PR_LOG(sRemoteLm, PR_LOG_DEBUG,
   631              (MOZILLA_LOCK_PROP " was stolen!  Expected \"%s\", saw \"%s\"!\n",
   632               mLockData, data));
   633       return NS_ERROR_FAILURE;
   634   }
   636   if (data)
   637       XFree(data);
   638   return NS_OK;
   639 }
   641 nsresult
   642 XRemoteClient::DoSendCommand(Window aWindow, const char *aCommand,
   643                              const char* aDesktopStartupID,
   644                              char **aResponse, bool *aDestroyed)
   645 {
   646   *aDestroyed = false;
   648   PR_LOG(sRemoteLm, PR_LOG_DEBUG,
   649      ("(writing " MOZILLA_COMMAND_PROP " \"%s\" to 0x%x)\n",
   650       aCommand, (unsigned int) aWindow));
   652   // We add the DESKTOP_STARTUP_ID setting as an extra line of
   653   // the command string. Firefox ignores all lines but the first.
   654   static char desktopStartupPrefix[] = "\nDESKTOP_STARTUP_ID=";
   656   int32_t len = strlen(aCommand);
   657   if (aDesktopStartupID) {
   658     len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID);
   659   }
   660   char* buffer = (char*)malloc(len + 1);
   661   if (!buffer)
   662     return NS_ERROR_OUT_OF_MEMORY;
   664   strcpy(buffer, aCommand);
   665   if (aDesktopStartupID) {
   666     strcat(buffer, desktopStartupPrefix);
   667     strcat(buffer, aDesktopStartupID);
   668   }
   670   XChangeProperty (mDisplay, aWindow, mMozCommandAtom, XA_STRING, 8,
   671            PropModeReplace, (unsigned char *)buffer, len);
   673   free(buffer);
   675   if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandAtom))
   676     return NS_ERROR_FAILURE;
   678   return NS_OK;
   679 }
   681 /* like strcpy, but return the char after the final null */
   682 static char*
   683 estrcpy(const char* s, char* d)
   684 {
   685   while (*s)
   686     *d++ = *s++;
   688   *d++ = '\0';
   689   return d;
   690 }
   692 nsresult
   693 XRemoteClient::DoSendCommandLine(Window aWindow, int32_t argc, char **argv,
   694                                  const char* aDesktopStartupID,
   695                                  char **aResponse, bool *aDestroyed)
   696 {
   697   *aDestroyed = false;
   699   char cwdbuf[MAX_PATH];
   700   if (!getcwd(cwdbuf, MAX_PATH))
   701     return NS_ERROR_UNEXPECTED;
   703   // the commandline property is constructed as an array of int32_t
   704   // followed by a series of null-terminated strings:
   705   //
   706   // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
   707   // (offset is from the beginning of the buffer)
   709   static char desktopStartupPrefix[] = " DESKTOP_STARTUP_ID=";
   711   int32_t argvlen = strlen(cwdbuf);
   712   for (int i = 0; i < argc; ++i) {
   713     int32_t len = strlen(argv[i]);
   714     if (i == 0 && aDesktopStartupID) {
   715       len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID);
   716     }
   717     argvlen += len;
   718   }
   720   int32_t* buffer = (int32_t*) malloc(argvlen + argc + 1 +
   721                                       sizeof(int32_t) * (argc + 1));
   722   if (!buffer)
   723     return NS_ERROR_OUT_OF_MEMORY;
   725   buffer[0] = TO_LITTLE_ENDIAN32(argc);
   727   char *bufend = (char*) (buffer + argc + 1);
   729   bufend = estrcpy(cwdbuf, bufend);
   731   for (int i = 0; i < argc; ++i) {
   732     buffer[i + 1] = TO_LITTLE_ENDIAN32(bufend - ((char*) buffer));
   733     bufend = estrcpy(argv[i], bufend);
   734     if (i == 0 && aDesktopStartupID) {
   735       bufend = estrcpy(desktopStartupPrefix, bufend - 1);
   736       bufend = estrcpy(aDesktopStartupID, bufend - 1);
   737     }
   738   }
   740 #ifdef DEBUG_bsmedberg
   741   int32_t   debug_argc   = TO_LITTLE_ENDIAN32(*buffer);
   742   char *debug_workingdir = (char*) (buffer + argc + 1);
   744   printf("Sending command line:\n"
   745          "  working dir: %s\n"
   746          "  argc:\t%i",
   747          debug_workingdir,
   748          debug_argc);
   750   int32_t  *debug_offset = buffer + 1;
   751   for (int debug_i = 0; debug_i < debug_argc; ++debug_i)
   752     printf("  argv[%i]:\t%s\n", debug_i,
   753            ((char*) buffer) + TO_LITTLE_ENDIAN32(debug_offset[debug_i]));
   754 #endif
   756   XChangeProperty (mDisplay, aWindow, mMozCommandLineAtom, XA_STRING, 8,
   757                    PropModeReplace, (unsigned char *) buffer,
   758                    bufend - ((char*) buffer));
   759   free(buffer);
   761   if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandLineAtom))
   762     return NS_ERROR_FAILURE;
   764   return NS_OK;
   765 }
   767 bool
   768 XRemoteClient::WaitForResponse(Window aWindow, char **aResponse,
   769                                bool *aDestroyed, Atom aCommandAtom)
   770 {
   771   bool done = false;
   772   bool accepted = false;
   774   while (!done) {
   775     XEvent event;
   776     XNextEvent (mDisplay, &event);
   777     if (event.xany.type == DestroyNotify &&
   778         event.xdestroywindow.window == aWindow) {
   779       /* Print to warn user...*/
   780       PR_LOG(sRemoteLm, PR_LOG_DEBUG,
   781              ("window 0x%x was destroyed.\n",
   782               (unsigned int) aWindow));
   783       *aResponse = strdup("Window was destroyed while reading response.");
   784       *aDestroyed = true;
   785       return false;
   786     }
   787     else if (event.xany.type == PropertyNotify &&
   788              event.xproperty.state == PropertyNewValue &&
   789              event.xproperty.window == aWindow &&
   790              event.xproperty.atom == mMozResponseAtom) {
   791       Atom actual_type;
   792       int actual_format;
   793       unsigned long nitems, bytes_after;
   794       unsigned char *data = 0;
   795       Bool result;
   796       result = XGetWindowProperty (mDisplay, aWindow, mMozResponseAtom,
   797                                    0, (65536 / sizeof (long)),
   798                                    True, /* atomic delete after */
   799                                    XA_STRING,
   800                                    &actual_type, &actual_format,
   801                                    &nitems, &bytes_after,
   802                                    &data);
   803       if (result != Success) {
   804         PR_LOG(sRemoteLm, PR_LOG_DEBUG,
   805                ("failed reading " MOZILLA_RESPONSE_PROP
   806                 " from window 0x%0x.\n",
   807                 (unsigned int) aWindow));
   808         *aResponse = strdup("Internal error reading response from window.");
   809         done = true;
   810       }
   811       else if (!data || strlen((char *) data) < 5) {
   812         PR_LOG(sRemoteLm, PR_LOG_DEBUG,
   813                ("invalid data on " MOZILLA_RESPONSE_PROP
   814                 " property of window 0x%0x.\n",
   815                 (unsigned int) aWindow));
   816         *aResponse = strdup("Server returned invalid data in response.");
   817         done = true;
   818       }
   819       else if (*data == '1') {  /* positive preliminary reply */
   820         PR_LOG(sRemoteLm, PR_LOG_DEBUG,  ("%s\n", data + 4));
   821         /* keep going */
   822         done = false;
   823       }
   825       else if (!strncmp ((char *)data, "200", 3)) { /* positive completion */
   826         *aResponse = strdup((char *)data);
   827         accepted = true;
   828         done = true;
   829       }
   831       else if (*data == '2') {  /* positive completion */
   832         PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("%s\n", data + 4));
   833         *aResponse = strdup((char *)data);
   834         accepted = true;
   835         done = true;
   836       }
   838       else if (*data == '3') {  /* positive intermediate reply */
   839         PR_LOG(sRemoteLm, PR_LOG_DEBUG,
   840                ("internal error: "
   841                 "server wants more information?  (%s)\n",
   842                 data));
   843         *aResponse = strdup((char *)data);
   844         done = true;
   845       }
   847       else if (*data == '4' ||  /* transient negative completion */
   848                *data == '5') {  /* permanent negative completion */
   849         PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("%s\n", data + 4));
   850         *aResponse = strdup((char *)data);
   851         done = true;
   852       }
   854       else {
   855         PR_LOG(sRemoteLm, PR_LOG_DEBUG,
   856                ("unrecognised " MOZILLA_RESPONSE_PROP
   857                 " from window 0x%x: %s\n",
   858                 (unsigned int) aWindow, data));
   859         *aResponse = strdup((char *)data);
   860         done = true;
   861       }
   863       if (data)
   864         XFree(data);
   865     }
   867     else if (event.xany.type == PropertyNotify &&
   868              event.xproperty.window == aWindow &&
   869              event.xproperty.state == PropertyDelete &&
   870              event.xproperty.atom == aCommandAtom) {
   871       PR_LOG(sRemoteLm, PR_LOG_DEBUG,
   872              ("(server 0x%x has accepted "
   873               MOZILLA_COMMAND_PROP ".)\n",
   874               (unsigned int) aWindow));
   875     }
   877   }
   879   return accepted;
   880 }

mercurial