Thu, 22 Jan 2015 13:21:57 +0100
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 }