Back to index

lightning-sunbird  0.9+nobinonly
XRemoteClient.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim:expandtab:shiftwidth=4:tabstop=4:
00003  */
00004 /* vim:set ts=8 sw=2 et cindent: */
00005 /* ***** BEGIN LICENSE BLOCK *****
00006  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00007  *
00008  * The contents of this file are subject to the Mozilla Public License Version
00009  * 1.1 (the "License"); you may not use this file except in compliance with
00010  * the License. You may obtain a copy of the License at
00011  * http://www.mozilla.org/MPL/
00012  *
00013  * Software distributed under the License is distributed on an "AS IS" basis,
00014  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00015  * for the specific language governing rights and limitations under the
00016  * License.
00017  *
00018  * The Original Code is mozilla.org code.
00019  *
00020  * The Initial Developer of the Original Code is
00021  * Christopher Blizzard and Jamie Zawinski.
00022  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00023  * the Initial Developer. All Rights Reserved.
00024  *
00025  * Contributor(s):
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "XRemoteClient.h"
00042 #include "prmem.h"
00043 #include "prprf.h"
00044 #include "plstr.h"
00045 #include "prsystem.h"
00046 #include "prlog.h"
00047 #include "prenv.h"
00048 #include "prdtoa.h"
00049 #include <stdlib.h>
00050 #include <unistd.h>
00051 #include <string.h>
00052 #include <strings.h>
00053 #include <sys/time.h>
00054 #include <sys/types.h>
00055 #include <unistd.h>
00056 #include <X11/Xatom.h>
00057 #ifdef POLL_WITH_XCONNECTIONNUMBER
00058 #include <poll.h>
00059 #endif
00060 
00061 #define MOZILLA_VERSION_PROP   "_MOZILLA_VERSION"
00062 #define MOZILLA_LOCK_PROP      "_MOZILLA_LOCK"
00063 #define MOZILLA_COMMAND_PROP   "_MOZILLA_COMMAND"
00064 #define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
00065 #define MOZILLA_RESPONSE_PROP  "_MOZILLA_RESPONSE"
00066 #define MOZILLA_USER_PROP      "_MOZILLA_USER"
00067 #define MOZILLA_PROFILE_PROP   "_MOZILLA_PROFILE"
00068 #define MOZILLA_PROGRAM_PROP   "_MOZILLA_PROGRAM"
00069 
00070 #ifdef IS_BIG_ENDIAN
00071 #define TO_LITTLE_ENDIAN32(x) \
00072     ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
00073     (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
00074 #else
00075 #define TO_LITTLE_ENDIAN32(x) (x)
00076 #endif
00077     
00078 #ifndef MAX_PATH
00079 #define MAX_PATH 1024
00080 #endif
00081 
00082 #define ARRAY_LENGTH(array_) (sizeof(array_)/sizeof(array_[0]))
00083 
00084 static PRLogModuleInfo *sRemoteLm = NULL;
00085 
00086 XRemoteClient::XRemoteClient()
00087 {
00088   mDisplay = 0;
00089   mInitialized = PR_FALSE;
00090   mMozVersionAtom = 0;
00091   mMozLockAtom = 0;
00092   mMozCommandAtom = 0;
00093   mMozResponseAtom = 0;
00094   mMozWMStateAtom = 0;
00095   mMozUserAtom = 0;
00096   mLockData = 0;
00097   if (!sRemoteLm)
00098     sRemoteLm = PR_NewLogModule("XRemoteClient");
00099   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::XRemoteClient"));
00100 }
00101 
00102 XRemoteClient::~XRemoteClient()
00103 {
00104   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::~XRemoteClient"));
00105   if (mInitialized)
00106     Shutdown();
00107 }
00108 
00109 // Minimize the roundtrips to the X-server
00110 static char *XAtomNames[] = {
00111   MOZILLA_VERSION_PROP,
00112   MOZILLA_LOCK_PROP,
00113   MOZILLA_COMMAND_PROP,
00114   MOZILLA_RESPONSE_PROP,
00115   "WM_STATE",
00116   MOZILLA_USER_PROP,
00117   MOZILLA_PROFILE_PROP,
00118   MOZILLA_PROGRAM_PROP,
00119   MOZILLA_COMMANDLINE_PROP
00120 };
00121 static Atom XAtoms[ARRAY_LENGTH(XAtomNames)];
00122 
00123 nsresult
00124 XRemoteClient::Init()
00125 {
00126   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::Init"));
00127 
00128   if (mInitialized)
00129     return NS_OK;
00130 
00131   // try to open the display
00132   mDisplay = XOpenDisplay(0);
00133   if (!mDisplay)
00134     return NS_ERROR_FAILURE;
00135 
00136   // get our atoms
00137   XInternAtoms(mDisplay, XAtomNames, ARRAY_LENGTH(XAtomNames), False, XAtoms);
00138 
00139   int i = 0;
00140   mMozVersionAtom  = XAtoms[i++];
00141   mMozLockAtom     = XAtoms[i++];
00142   mMozCommandAtom  = XAtoms[i++];
00143   mMozResponseAtom = XAtoms[i++];
00144   mMozWMStateAtom  = XAtoms[i++];
00145   mMozUserAtom     = XAtoms[i++];
00146   mMozProfileAtom  = XAtoms[i++];
00147   mMozProgramAtom  = XAtoms[i++];
00148   mMozCommandLineAtom = XAtoms[i++];
00149 
00150   mInitialized = PR_TRUE;
00151 
00152   return NS_OK;
00153 }
00154 
00155 void
00156 XRemoteClient::Shutdown (void)
00157 {
00158   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::Shutdown"));
00159 
00160   if (!mInitialized)
00161     return;
00162 
00163   // shut everything down
00164   XCloseDisplay(mDisplay);
00165   mDisplay = 0;
00166   mInitialized = PR_FALSE;
00167   if (mLockData) {
00168     free(mLockData);
00169     mLockData = 0;
00170   }
00171 }
00172 
00173 nsresult
00174 XRemoteClient::SendCommand (const char *aProgram, const char *aUsername,
00175                             const char *aProfile, const char *aCommand,
00176                             char **aResponse, PRBool *aWindowFound)
00177 {
00178   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommand"));
00179 
00180   *aWindowFound = PR_FALSE;
00181 
00182   Window w = FindBestWindow(aProgram, aUsername, aProfile, PR_FALSE);
00183 
00184   nsresult rv = NS_OK;
00185 
00186   if (w) {
00187     // ok, let the caller know that we at least found a window.
00188     *aWindowFound = PR_TRUE;
00189 
00190     // make sure we get the right events on that window
00191     XSelectInput(mDisplay, w,
00192                  (PropertyChangeMask|StructureNotifyMask));
00193         
00194     PRBool destroyed = PR_FALSE;
00195 
00196     // get the lock on the window
00197     rv = GetLock(w, &destroyed);
00198 
00199     if (NS_SUCCEEDED(rv)) {
00200       // send our command
00201       rv = DoSendCommand(w, aCommand, aResponse, &destroyed);
00202 
00203       // if the window was destroyed, don't bother trying to free the
00204       // lock.
00205       if (!destroyed)
00206           FreeLock(w); // doesn't really matter what this returns
00207 
00208     }
00209   }
00210 
00211   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("SendCommand returning 0x%x\n", rv));
00212 
00213   return rv;
00214 }
00215 
00216 nsresult
00217 XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername,
00218                                 const char *aProfile,
00219                                 PRInt32 argc, char **argv,
00220                                 char **aResponse, PRBool *aWindowFound)
00221 {
00222   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommandLine"));
00223 
00224   *aWindowFound = PR_FALSE;
00225 
00226   Window w = FindBestWindow(aProgram, aUsername, aProfile, PR_TRUE);
00227 
00228   nsresult rv = NS_OK;
00229 
00230   if (w) {
00231     // ok, let the caller know that we at least found a window.
00232     *aWindowFound = PR_TRUE;
00233 
00234     // make sure we get the right events on that window
00235     XSelectInput(mDisplay, w,
00236                  (PropertyChangeMask|StructureNotifyMask));
00237         
00238     PRBool destroyed = PR_FALSE;
00239 
00240     // get the lock on the window
00241     rv = GetLock(w, &destroyed);
00242 
00243     if (NS_SUCCEEDED(rv)) {
00244       // send our command
00245       rv = DoSendCommandLine(w, argc, argv, aResponse, &destroyed);
00246 
00247       // if the window was destroyed, don't bother trying to free the
00248       // lock.
00249       if (!destroyed)
00250           FreeLock(w); // doesn't really matter what this returns
00251 
00252     }
00253   }
00254 
00255   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("SendCommandLine returning 0x%x\n", rv));
00256 
00257   return rv;
00258 }
00259 
00260 Window
00261 XRemoteClient::CheckWindow(Window aWindow)
00262 {
00263   Atom type = None;
00264   int  format;
00265   unsigned long nitems, bytesafter;
00266   unsigned char *data;
00267   Window innerWindow;
00268 
00269   XGetWindowProperty(mDisplay, aWindow, mMozWMStateAtom,
00270                    0, 0, False, AnyPropertyType,
00271                    &type, &format, &nitems, &bytesafter, &data);
00272 
00273   if (type) {
00274     XFree(data);
00275     return aWindow;
00276   }
00277 
00278   // didn't find it here so check the children of this window
00279   innerWindow = CheckChildren(aWindow);
00280 
00281   if (innerWindow)
00282     return innerWindow;
00283 
00284   return aWindow;
00285 }
00286 
00287 Window
00288 XRemoteClient::CheckChildren(Window aWindow)
00289 {
00290   Window root, parent;
00291   Window *children;
00292   unsigned int nchildren;
00293   unsigned int i;
00294   Atom type = None;
00295   int format;
00296   unsigned long nitems, after;
00297   unsigned char *data;
00298   Window retval = None;
00299   
00300   if (!XQueryTree(mDisplay, aWindow, &root, &parent, &children,
00301                 &nchildren))
00302     return None;
00303   
00304   // scan the list first before recursing into the list of windows
00305   // which can get quite deep.
00306   for (i=0; !retval && (i < nchildren); i++) {
00307     XGetWindowProperty(mDisplay, children[i], mMozWMStateAtom,
00308                      0, 0, False, AnyPropertyType, &type, &format,
00309                      &nitems, &after, &data);
00310     if (type) {
00311       XFree(data);
00312       retval = children[i];
00313     }
00314   }
00315 
00316   // otherwise recurse into the list
00317   for (i=0; !retval && (i < nchildren); i++) {
00318     retval = CheckChildren(children[i]);
00319   }
00320 
00321   if (children)
00322     XFree((char *)children);
00323 
00324   return retval;
00325 }
00326 
00327 nsresult
00328 XRemoteClient::GetLock(Window aWindow, PRBool *aDestroyed)
00329 {
00330   PRBool locked = PR_FALSE;
00331   PRBool waited = PR_FALSE;
00332   *aDestroyed = PR_FALSE;
00333 
00334   if (!mLockData) {
00335     
00336     char pidstr[32];
00337     char sysinfobuf[SYS_INFO_BUFFER_LENGTH];
00338     PR_snprintf(pidstr, sizeof(pidstr), "pid%d@", getpid());
00339     PRStatus status;
00340     status = PR_GetSystemInfo(PR_SI_HOSTNAME, sysinfobuf,
00341                            SYS_INFO_BUFFER_LENGTH);
00342     if (status != PR_SUCCESS) {
00343       return NS_ERROR_FAILURE;
00344     }
00345     
00346     // allocate enough space for the string plus the terminating
00347     // char
00348     mLockData = (char *)malloc(strlen(pidstr) + strlen(sysinfobuf) + 1);
00349     if (!mLockData)
00350       return NS_ERROR_OUT_OF_MEMORY;
00351 
00352     strcpy(mLockData, pidstr);
00353     if (!strcat(mLockData, sysinfobuf))
00354       return NS_ERROR_FAILURE;
00355   }
00356 
00357   do {
00358     int result;
00359     Atom actual_type;
00360     int actual_format;
00361     unsigned long nitems, bytes_after;
00362     unsigned char *data = 0;
00363 
00364     XGrabServer(mDisplay);
00365 
00366     result = XGetWindowProperty (mDisplay, aWindow, mMozLockAtom,
00367                              0, (65536 / sizeof (long)),
00368                              False, /* don't delete */
00369                              XA_STRING,
00370                              &actual_type, &actual_format,
00371                              &nitems, &bytes_after,
00372                              &data);
00373     if (result != Success || actual_type == None) {
00374       /* It's not now locked - lock it. */
00375       XChangeProperty (mDisplay, aWindow, mMozLockAtom, XA_STRING, 8,
00376                      PropModeReplace,
00377                      (unsigned char *)mLockData,
00378                      strlen(mLockData));
00379       locked = True;
00380     }
00381 
00382     XUngrabServer(mDisplay);
00383     XSync(mDisplay, False);
00384 
00385     if (!locked) {
00386       /* We tried to grab the lock this time, and failed because someone
00387         else is holding it already.  So, wait for a PropertyDelete event
00388         to come in, and try again. */
00389       PR_LOG(sRemoteLm, PR_LOG_DEBUG, 
00390             ("window 0x%x is locked by %s; waiting...\n",
00391              (unsigned int) aWindow, data));
00392       waited = True;
00393       while (1) {
00394        XEvent event;
00395        int select_retval;
00396 #ifdef POLL_WITH_XCONNECTIONNUMBER
00397        struct pollfd fds[1];
00398        fds[0].fd = XConnectionNumber(mDisplay);
00399        fds[0].events = POLLIN;
00400        select_retval = poll(fds,1,10*1000);
00401 #else
00402        fd_set select_set;
00403        struct timeval delay;
00404        delay.tv_sec = 10;
00405        delay.tv_usec = 0;
00406 
00407        FD_ZERO(&select_set);
00408        // add the x event queue to the select set
00409        FD_SET(ConnectionNumber(mDisplay), &select_set);
00410        select_retval = select(ConnectionNumber(mDisplay) + 1,
00411                             &select_set, NULL, NULL, &delay);
00412 #endif
00413        // did we time out?
00414        if (select_retval == 0) {
00415          PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("timed out waiting for window\n"));
00416          return NS_ERROR_FAILURE;
00417        }
00418        PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("xevent...\n"));
00419        XNextEvent (mDisplay, &event);
00420        if (event.xany.type == DestroyNotify &&
00421            event.xdestroywindow.window == aWindow) {
00422          PR_LOG(sRemoteLm, PR_LOG_DEBUG,
00423                ("window 0x%x unexpectedly destroyed.\n",
00424                 (unsigned int) aWindow));
00425          *aDestroyed = PR_TRUE;
00426          return NS_ERROR_FAILURE;
00427        }
00428        else if (event.xany.type == PropertyNotify &&
00429                event.xproperty.state == PropertyDelete &&
00430                event.xproperty.window == aWindow &&
00431                event.xproperty.atom == mMozLockAtom) {
00432          /* Ok!  Someone deleted their lock, so now we can try
00433             again. */
00434          PR_LOG(sRemoteLm, PR_LOG_DEBUG,
00435                ("(0x%x unlocked, trying again...)\n",
00436                 (unsigned int) aWindow));
00437                 break;
00438        }
00439       }
00440     }
00441     if (data)
00442       XFree(data);
00443   } while (!locked);
00444 
00445   if (waited) {
00446     PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("obtained lock.\n"));
00447   }
00448 
00449   return NS_OK;
00450 }
00451 
00452 Window
00453 XRemoteClient::FindBestWindow(const char *aProgram, const char *aUsername,
00454                               const char *aProfile,
00455                               PRBool aSupportsCommandLine)
00456 {
00457   Window root = RootWindowOfScreen(DefaultScreenOfDisplay(mDisplay));
00458   Window bestWindow = 0;
00459   Window root2, parent, *kids;
00460   unsigned int nkids;
00461   int i;
00462 
00463   // Get a list of the children of the root window, walk the list
00464   // looking for the best window that fits the criteria.
00465   if (!XQueryTree(mDisplay, root, &root2, &parent, &kids, &nkids)) {
00466     PR_LOG(sRemoteLm, PR_LOG_DEBUG,
00467            ("XQueryTree failed in XRemoteClient::FindBestWindow"));
00468     return 0;
00469   }
00470 
00471   if (!(kids && nkids)) {
00472     PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("root window has no children"));
00473     return 0;
00474   }
00475 
00476   // We'll walk the list of windows looking for a window that best
00477   // fits the criteria here.
00478 
00479   for (i=nkids-1; i >= 0; i--) {
00480     Atom type;
00481     int format;
00482     unsigned long nitems, bytesafter;
00483     unsigned char *data_return = 0;
00484     Window w;
00485     w = kids[i];
00486     // find the inner window with WM_STATE on it
00487     w = CheckWindow(w);
00488 
00489     int status = XGetWindowProperty(mDisplay, w, mMozVersionAtom,
00490                                     0, (65536 / sizeof (long)),
00491                                     False, XA_STRING,
00492                                     &type, &format, &nitems, &bytesafter,
00493                                     &data_return);
00494 
00495     if (!data_return)
00496       continue;
00497 
00498     PRFloat64 version = PR_strtod((char*) data_return, nsnull);
00499     XFree(data_return);
00500 
00501     if (aSupportsCommandLine && !(version >= 5.1 && version < 6))
00502       continue;
00503 
00504     data_return = 0;
00505 
00506     if (status != Success || type == None)
00507       continue;
00508 
00509     // If someone passed in a program name, check it against this one
00510     // unless it's "any" in which case, we don't care.  If someone did
00511     // pass in a program name and this window doesn't support that
00512     // protocol, we don't include it in our list.
00513     if (aProgram && strcmp(aProgram, "any")) {
00514         status = XGetWindowProperty(mDisplay, w, mMozProgramAtom,
00515                                     0, (65536 / sizeof(long)),
00516                                     False, XA_STRING,
00517                                     &type, &format, &nitems, &bytesafter,
00518                                     &data_return);
00519         
00520         // If the return name is not the same as what someone passed in,
00521         // we don't want this window.
00522         if (data_return) {
00523             if (strcmp(aProgram, (const char *)data_return)) {
00524                 XFree(data_return);
00525                 continue;
00526             }
00527 
00528             // This is actually the success condition.
00529             XFree(data_return);
00530         }
00531         else {
00532             // Doesn't support the protocol, even though the user
00533             // requested it.  So we're not going to use this window.
00534             continue;
00535         }
00536     }
00537 
00538     // Check to see if it has the user atom on that window.  If there
00539     // is then we need to make sure that it matches what we have.
00540     const char *username;
00541     if (aUsername) {
00542       username = aUsername;
00543     }
00544     else {
00545       username = PR_GetEnv("LOGNAME");
00546     }
00547 
00548     if (username) {
00549         status = XGetWindowProperty(mDisplay, w, mMozUserAtom,
00550                                     0, (65536 / sizeof(long)),
00551                                     False, XA_STRING,
00552                                     &type, &format, &nitems, &bytesafter,
00553                                     &data_return);
00554 
00555         // if there's a username compare it with what we have
00556         if (data_return) {
00557             // If the IDs aren't equal, we don't want this window.
00558             if (strcmp(username, (const char *)data_return)) {
00559                 XFree(data_return);
00560                 continue;
00561             }
00562 
00563             XFree(data_return);
00564         }
00565     }
00566 
00567     // Check to see if there's a profile name on this window.  If
00568     // there is, then we need to make sure it matches what someone
00569     // passed in.
00570     if (aProfile) {
00571         status = XGetWindowProperty(mDisplay, w, mMozProfileAtom,
00572                                     0, (65536 / sizeof(long)),
00573                                     False, XA_STRING,
00574                                     &type, &format, &nitems, &bytesafter,
00575                                     &data_return);
00576 
00577         // If there's a profile compare it with what we have
00578         if (data_return) {
00579             // If the profiles aren't equal, we don't want this window.
00580             if (strcmp(aProfile, (const char *)data_return)) {
00581                 XFree(data_return);
00582                 continue;
00583             }
00584 
00585             XFree(data_return);
00586         }
00587     }
00588 
00589     // Check to see if the window supports the new command-line passing
00590     // protocol, if that is requested.
00591 
00592     // If we got this far, this is the best window so far.  It passed
00593     // all the tests.
00594     bestWindow = w;
00595   }
00596 
00597   if (kids)
00598     XFree((char *) kids);
00599 
00600   return bestWindow;
00601 }
00602 
00603 nsresult
00604 XRemoteClient::FreeLock(Window aWindow)
00605 {
00606   int result;
00607   Atom actual_type;
00608   int actual_format;
00609   unsigned long nitems, bytes_after;
00610   unsigned char *data = 0;
00611 
00612   result = XGetWindowProperty(mDisplay, aWindow, mMozLockAtom,
00613                               0, (65536 / sizeof(long)),
00614                               True, /* atomic delete after */
00615                               XA_STRING,
00616                               &actual_type, &actual_format,
00617                               &nitems, &bytes_after,
00618                               &data);
00619   if (result != Success) {
00620       PR_LOG(sRemoteLm, PR_LOG_DEBUG,
00621              ("unable to read and delete " MOZILLA_LOCK_PROP
00622               " property\n"));
00623       return NS_ERROR_FAILURE;
00624   }
00625   else if (!data || !*data){
00626       PR_LOG(sRemoteLm, PR_LOG_DEBUG,
00627              ("invalid data on " MOZILLA_LOCK_PROP
00628               " of window 0x%x.\n",
00629               (unsigned int) aWindow));
00630       return NS_ERROR_FAILURE;
00631   }
00632   else if (strcmp((char *)data, mLockData)) {
00633       PR_LOG(sRemoteLm, PR_LOG_DEBUG,
00634              (MOZILLA_LOCK_PROP " was stolen!  Expected \"%s\", saw \"%s\"!\n",
00635               mLockData, data));
00636       return NS_ERROR_FAILURE;
00637   }
00638 
00639   if (data)
00640       XFree(data);
00641   return NS_OK;
00642 }
00643 
00644 nsresult
00645 XRemoteClient::DoSendCommand(Window aWindow, const char *aCommand,
00646                              char **aResponse, PRBool *aDestroyed)
00647 {
00648   *aDestroyed = PR_FALSE;
00649 
00650   PR_LOG(sRemoteLm, PR_LOG_DEBUG,
00651      ("(writing " MOZILLA_COMMAND_PROP " \"%s\" to 0x%x)\n",
00652       aCommand, (unsigned int) aWindow));
00653 
00654   XChangeProperty (mDisplay, aWindow, mMozCommandAtom, XA_STRING, 8,
00655            PropModeReplace, (unsigned char *)aCommand,
00656            strlen(aCommand));
00657 
00658   if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandAtom))
00659     return NS_ERROR_FAILURE;
00660   
00661   return NS_OK;
00662 }
00663 
00664 /* like strcpy, but return the char after the final null */
00665 static char*
00666 estrcpy(char* s, char* d)
00667 {
00668   while (*s)
00669     *d++ = *s++;
00670 
00671   *d++ = '\0';
00672   return d;
00673 }
00674 
00675 nsresult
00676 XRemoteClient::DoSendCommandLine(Window aWindow, PRInt32 argc, char **argv,
00677                                  char **aResponse, PRBool *aDestroyed)
00678 {
00679   int i;
00680 
00681   *aDestroyed = PR_FALSE;
00682 
00683   char cwdbuf[MAX_PATH];
00684   if (!getcwd(cwdbuf, MAX_PATH))
00685     return NS_ERROR_UNEXPECTED;
00686 
00687   // the commandline property is constructed as an array of PRInt32
00688   // followed by a series of null-terminated strings:
00689   //
00690   // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
00691   // (offset is from the beginning of the buffer)
00692 
00693   PRInt32 argvlen = strlen(cwdbuf);
00694   for (i = 0; i < argc; ++i)
00695     argvlen += strlen(argv[i]);
00696 
00697   PRInt32* buffer = (PRInt32*) malloc(argvlen + argc + 1 +
00698                                       sizeof(PRInt32) * (argc + 1));
00699   if (!buffer)
00700     return NS_ERROR_OUT_OF_MEMORY;
00701 
00702   buffer[0] = TO_LITTLE_ENDIAN32(argc);
00703 
00704   char *bufend = (char*) (buffer + argc + 1);
00705 
00706   bufend = estrcpy(cwdbuf, bufend);
00707 
00708   for (int i = 0; i < argc; ++i) {
00709     buffer[i + 1] = TO_LITTLE_ENDIAN32(bufend - ((char*) buffer));
00710     bufend = estrcpy(argv[i], bufend);
00711   }
00712 
00713 #ifdef DEBUG_bsmedberg
00714   PRInt32   debug_argc   = TO_LITTLE_ENDIAN32(*buffer);
00715   char *debug_workingdir = (char*) (buffer + argc + 1);
00716 
00717   printf("Sending command line:\n"
00718          "  working dir: %s\n"
00719          "  argc:\t%i",
00720          debug_workingdir,
00721          debug_argc);
00722 
00723   PRInt32  *debug_offset = buffer + 1;
00724   for (int debug_i = 0; debug_i < debug_argc; ++debug_i)
00725     printf("  argv[%i]:\t%s\n", debug_i,
00726            ((char*) buffer) + TO_LITTLE_ENDIAN32(debug_offset[debug_i]));
00727 #endif
00728 
00729   XChangeProperty (mDisplay, aWindow, mMozCommandLineAtom, XA_STRING, 8,
00730                    PropModeReplace, (unsigned char *) buffer,
00731                    bufend - ((char*) buffer));
00732 
00733   if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandLineAtom))
00734     return NS_ERROR_FAILURE;
00735   
00736   return NS_OK;
00737 }
00738 
00739 PRBool
00740 XRemoteClient::WaitForResponse(Window aWindow, char **aResponse,
00741                                PRBool *aDestroyed, Atom aCommandAtom)
00742 {
00743   PRBool done = PR_FALSE;
00744   PRBool accepted = PR_FALSE;
00745 
00746   while (!done) {
00747     XEvent event;
00748     XNextEvent (mDisplay, &event);
00749     if (event.xany.type == DestroyNotify &&
00750         event.xdestroywindow.window == aWindow) {
00751       /* Print to warn user...*/
00752       PR_LOG(sRemoteLm, PR_LOG_DEBUG,
00753              ("window 0x%x was destroyed.\n",
00754               (unsigned int) aWindow));
00755       *aResponse = strdup("Window was destroyed while reading response.");
00756       *aDestroyed = PR_TRUE;
00757       return PR_FALSE;
00758     }
00759     else if (event.xany.type == PropertyNotify &&
00760              event.xproperty.state == PropertyNewValue &&
00761              event.xproperty.window == aWindow &&
00762              event.xproperty.atom == mMozResponseAtom) {
00763       Atom actual_type;
00764       int actual_format;
00765       unsigned long nitems, bytes_after;
00766       unsigned char *data = 0;
00767       Bool result;
00768       result = XGetWindowProperty (mDisplay, aWindow, mMozResponseAtom,
00769                                    0, (65536 / sizeof (long)),
00770                                    True, /* atomic delete after */
00771                                    XA_STRING,
00772                                    &actual_type, &actual_format,
00773                                    &nitems, &bytes_after,
00774                                    &data);
00775       if (result != Success) {
00776         PR_LOG(sRemoteLm, PR_LOG_DEBUG,
00777                ("failed reading " MOZILLA_RESPONSE_PROP
00778                 " from window 0x%0x.\n",
00779                 (unsigned int) aWindow));
00780         *aResponse = strdup("Internal error reading response from window.");
00781         done = PR_TRUE;
00782       }
00783       else if (!data || strlen((char *) data) < 5) {
00784         PR_LOG(sRemoteLm, PR_LOG_DEBUG,
00785                ("invalid data on " MOZILLA_RESPONSE_PROP
00786                 " property of window 0x%0x.\n",
00787                 (unsigned int) aWindow));
00788         *aResponse = strdup("Server returned invalid data in response.");
00789         done = PR_TRUE;
00790       }
00791       else if (*data == '1') {  /* positive preliminary reply */
00792         PR_LOG(sRemoteLm, PR_LOG_DEBUG,  ("%s\n", data + 4));
00793         /* keep going */
00794         done = PR_FALSE;
00795       }
00796 
00797       else if (!strncmp ((char *)data, "200", 3)) { /* positive completion */
00798         *aResponse = strdup((char *)data);
00799         accepted = PR_TRUE;
00800         done = PR_TRUE;
00801       }
00802 
00803       else if (*data == '2') {  /* positive completion */
00804         PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("%s\n", data + 4));
00805         *aResponse = strdup((char *)data);
00806         accepted = PR_TRUE;
00807         done = PR_TRUE;
00808       }
00809 
00810       else if (*data == '3') {  /* positive intermediate reply */
00811         PR_LOG(sRemoteLm, PR_LOG_DEBUG,
00812                ("internal error: "
00813                 "server wants more information?  (%s)\n",
00814                 data));
00815         *aResponse = strdup((char *)data);
00816         done = PR_TRUE;
00817       }
00818 
00819       else if (*data == '4' ||  /* transient negative completion */
00820                *data == '5') {  /* permanent negative completion */
00821         PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("%s\n", data + 4));
00822         *aResponse = strdup((char *)data);
00823         done = PR_TRUE;
00824       }
00825 
00826       else {
00827         PR_LOG(sRemoteLm, PR_LOG_DEBUG,
00828                ("unrecognised " MOZILLA_RESPONSE_PROP
00829                 " from window 0x%x: %s\n",
00830                 (unsigned int) aWindow, data));
00831         *aResponse = strdup((char *)data);
00832         done = PR_TRUE;
00833       }
00834 
00835       if (data)
00836         XFree(data);
00837     }
00838 
00839     else if (event.xany.type == PropertyNotify &&
00840              event.xproperty.window == aWindow &&
00841              event.xproperty.state == PropertyDelete &&
00842              event.xproperty.atom == aCommandAtom) {
00843       PR_LOG(sRemoteLm, PR_LOG_DEBUG,
00844              ("(server 0x%x has accepted "
00845               MOZILLA_COMMAND_PROP ".)\n",
00846               (unsigned int) aWindow));
00847     }
00848     
00849   }
00850 
00851   return accepted;
00852 }