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