|
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/. */ |
|
8 |
|
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> |
|
26 |
|
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" |
|
35 |
|
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 |
|
43 |
|
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 |
|
51 |
|
52 #define ARRAY_LENGTH(array_) (sizeof(array_)/sizeof(array_[0])) |
|
53 |
|
54 static PRLogModuleInfo *sRemoteLm = nullptr; |
|
55 |
|
56 static int (*sOldHandler)(Display *, XErrorEvent *); |
|
57 static bool sGotBadWindow; |
|
58 |
|
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 } |
|
74 |
|
75 XRemoteClient::~XRemoteClient() |
|
76 { |
|
77 PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::~XRemoteClient")); |
|
78 if (mInitialized) |
|
79 Shutdown(); |
|
80 } |
|
81 |
|
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)]; |
|
95 |
|
96 nsresult |
|
97 XRemoteClient::Init() |
|
98 { |
|
99 PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::Init")); |
|
100 |
|
101 if (mInitialized) |
|
102 return NS_OK; |
|
103 |
|
104 // try to open the display |
|
105 mDisplay = XOpenDisplay(0); |
|
106 if (!mDisplay) |
|
107 return NS_ERROR_FAILURE; |
|
108 |
|
109 // get our atoms |
|
110 XInternAtoms(mDisplay, const_cast<char**>(XAtomNames), |
|
111 ARRAY_LENGTH(XAtomNames), False, XAtoms); |
|
112 |
|
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++]; |
|
123 |
|
124 mInitialized = true; |
|
125 |
|
126 return NS_OK; |
|
127 } |
|
128 |
|
129 void |
|
130 XRemoteClient::Shutdown (void) |
|
131 { |
|
132 PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::Shutdown")); |
|
133 |
|
134 if (!mInitialized) |
|
135 return; |
|
136 |
|
137 // shut everything down |
|
138 XCloseDisplay(mDisplay); |
|
139 mDisplay = 0; |
|
140 mInitialized = false; |
|
141 if (mLockData) { |
|
142 free(mLockData); |
|
143 mLockData = 0; |
|
144 } |
|
145 } |
|
146 |
|
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")); |
|
154 |
|
155 return SendCommandInternal(aProgram, aUsername, aProfile, |
|
156 aCommand, 0, nullptr, |
|
157 aDesktopStartupID, |
|
158 aResponse, aWindowFound); |
|
159 } |
|
160 |
|
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")); |
|
169 |
|
170 return SendCommandInternal(aProgram, aUsername, aProfile, |
|
171 nullptr, argc, argv, |
|
172 aDesktopStartupID, |
|
173 aResponse, aWindowFound); |
|
174 } |
|
175 |
|
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 } |
|
187 |
|
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; |
|
197 |
|
198 // FindBestWindow() iterates down the window hierarchy, so catch X errors |
|
199 // when windows get destroyed before being accessed. |
|
200 sOldHandler = XSetErrorHandler(HandleBadWindow); |
|
201 |
|
202 Window w = FindBestWindow(aProgram, aUsername, aProfile, isCommandLine); |
|
203 |
|
204 nsresult rv = NS_OK; |
|
205 |
|
206 if (w) { |
|
207 // ok, let the caller know that we at least found a window. |
|
208 *aWindowFound = true; |
|
209 |
|
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; |
|
214 |
|
215 // make sure we get the right events on that window |
|
216 XSelectInput(mDisplay, w, |
|
217 (PropertyChangeMask|StructureNotifyMask)); |
|
218 |
|
219 bool destroyed = false; |
|
220 |
|
221 // get the lock on the window |
|
222 rv = GetLock(w, &destroyed); |
|
223 |
|
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 } |
|
234 |
|
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 |
|
239 |
|
240 } |
|
241 } |
|
242 |
|
243 XSetErrorHandler(sOldHandler); |
|
244 |
|
245 PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("SendCommandInternal returning 0x%x\n", rv)); |
|
246 |
|
247 return rv; |
|
248 } |
|
249 |
|
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; |
|
258 |
|
259 XGetWindowProperty(mDisplay, aWindow, mMozWMStateAtom, |
|
260 0, 0, False, AnyPropertyType, |
|
261 &type, &format, &nitems, &bytesafter, &data); |
|
262 |
|
263 if (type) { |
|
264 XFree(data); |
|
265 return aWindow; |
|
266 } |
|
267 |
|
268 // didn't find it here so check the children of this window |
|
269 innerWindow = CheckChildren(aWindow); |
|
270 |
|
271 if (innerWindow) |
|
272 return innerWindow; |
|
273 |
|
274 return aWindow; |
|
275 } |
|
276 |
|
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; |
|
289 |
|
290 if (!XQueryTree(mDisplay, aWindow, &root, &parent, &children, |
|
291 &nchildren)) |
|
292 return None; |
|
293 |
|
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 } |
|
305 |
|
306 // otherwise recurse into the list |
|
307 for (i=0; !retval && (i < nchildren); i++) { |
|
308 retval = CheckChildren(children[i]); |
|
309 } |
|
310 |
|
311 if (children) |
|
312 XFree((char *)children); |
|
313 |
|
314 return retval; |
|
315 } |
|
316 |
|
317 nsresult |
|
318 XRemoteClient::GetLock(Window aWindow, bool *aDestroyed) |
|
319 { |
|
320 bool locked = false; |
|
321 bool waited = false; |
|
322 *aDestroyed = false; |
|
323 |
|
324 nsresult rv = NS_OK; |
|
325 |
|
326 if (!mLockData) { |
|
327 |
|
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 } |
|
337 |
|
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; |
|
343 |
|
344 strcpy(mLockData, pidstr); |
|
345 if (!strcat(mLockData, sysinfobuf)) |
|
346 return NS_ERROR_FAILURE; |
|
347 } |
|
348 |
|
349 do { |
|
350 int result; |
|
351 Atom actual_type; |
|
352 int actual_format; |
|
353 unsigned long nitems, bytes_after; |
|
354 unsigned char *data = 0; |
|
355 |
|
356 XGrabServer(mDisplay); |
|
357 |
|
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); |
|
365 |
|
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 } |
|
382 |
|
383 XUngrabServer(mDisplay); |
|
384 XFlush(mDisplay); // ungrab now! |
|
385 |
|
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; |
|
401 |
|
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)); |
|
437 |
|
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 } |
|
445 |
|
446 return rv; |
|
447 } |
|
448 |
|
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; |
|
458 |
|
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 } |
|
466 |
|
467 if (!(kids && nkids)) { |
|
468 PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("root window has no children")); |
|
469 return 0; |
|
470 } |
|
471 |
|
472 // We'll walk the list of windows looking for a window that best |
|
473 // fits the criteria here. |
|
474 |
|
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); |
|
484 |
|
485 int status = XGetWindowProperty(mDisplay, w, mMozVersionAtom, |
|
486 0, (65536 / sizeof (long)), |
|
487 False, XA_STRING, |
|
488 &type, &format, &nitems, &bytesafter, |
|
489 &data_return); |
|
490 |
|
491 if (!data_return) |
|
492 continue; |
|
493 |
|
494 double version = PR_strtod((char*) data_return, nullptr); |
|
495 XFree(data_return); |
|
496 |
|
497 if (aSupportsCommandLine && !(version >= 5.1 && version < 6)) |
|
498 continue; |
|
499 |
|
500 data_return = 0; |
|
501 |
|
502 if (status != Success || type == None) |
|
503 continue; |
|
504 |
|
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); |
|
515 |
|
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 } |
|
523 |
|
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 } |
|
533 |
|
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 } |
|
543 |
|
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); |
|
550 |
|
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 } |
|
558 |
|
559 XFree(data_return); |
|
560 } |
|
561 } |
|
562 |
|
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); |
|
572 |
|
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 } |
|
580 |
|
581 XFree(data_return); |
|
582 } |
|
583 } |
|
584 |
|
585 // Check to see if the window supports the new command-line passing |
|
586 // protocol, if that is requested. |
|
587 |
|
588 // If we got this far, this is the best window. It passed |
|
589 // all the tests. |
|
590 bestWindow = w; |
|
591 break; |
|
592 } |
|
593 |
|
594 if (kids) |
|
595 XFree((char *) kids); |
|
596 |
|
597 return bestWindow; |
|
598 } |
|
599 |
|
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; |
|
608 |
|
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 } |
|
635 |
|
636 if (data) |
|
637 XFree(data); |
|
638 return NS_OK; |
|
639 } |
|
640 |
|
641 nsresult |
|
642 XRemoteClient::DoSendCommand(Window aWindow, const char *aCommand, |
|
643 const char* aDesktopStartupID, |
|
644 char **aResponse, bool *aDestroyed) |
|
645 { |
|
646 *aDestroyed = false; |
|
647 |
|
648 PR_LOG(sRemoteLm, PR_LOG_DEBUG, |
|
649 ("(writing " MOZILLA_COMMAND_PROP " \"%s\" to 0x%x)\n", |
|
650 aCommand, (unsigned int) aWindow)); |
|
651 |
|
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="; |
|
655 |
|
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; |
|
663 |
|
664 strcpy(buffer, aCommand); |
|
665 if (aDesktopStartupID) { |
|
666 strcat(buffer, desktopStartupPrefix); |
|
667 strcat(buffer, aDesktopStartupID); |
|
668 } |
|
669 |
|
670 XChangeProperty (mDisplay, aWindow, mMozCommandAtom, XA_STRING, 8, |
|
671 PropModeReplace, (unsigned char *)buffer, len); |
|
672 |
|
673 free(buffer); |
|
674 |
|
675 if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandAtom)) |
|
676 return NS_ERROR_FAILURE; |
|
677 |
|
678 return NS_OK; |
|
679 } |
|
680 |
|
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++; |
|
687 |
|
688 *d++ = '\0'; |
|
689 return d; |
|
690 } |
|
691 |
|
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; |
|
698 |
|
699 char cwdbuf[MAX_PATH]; |
|
700 if (!getcwd(cwdbuf, MAX_PATH)) |
|
701 return NS_ERROR_UNEXPECTED; |
|
702 |
|
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) |
|
708 |
|
709 static char desktopStartupPrefix[] = " DESKTOP_STARTUP_ID="; |
|
710 |
|
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 } |
|
719 |
|
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; |
|
724 |
|
725 buffer[0] = TO_LITTLE_ENDIAN32(argc); |
|
726 |
|
727 char *bufend = (char*) (buffer + argc + 1); |
|
728 |
|
729 bufend = estrcpy(cwdbuf, bufend); |
|
730 |
|
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 } |
|
739 |
|
740 #ifdef DEBUG_bsmedberg |
|
741 int32_t debug_argc = TO_LITTLE_ENDIAN32(*buffer); |
|
742 char *debug_workingdir = (char*) (buffer + argc + 1); |
|
743 |
|
744 printf("Sending command line:\n" |
|
745 " working dir: %s\n" |
|
746 " argc:\t%i", |
|
747 debug_workingdir, |
|
748 debug_argc); |
|
749 |
|
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 |
|
755 |
|
756 XChangeProperty (mDisplay, aWindow, mMozCommandLineAtom, XA_STRING, 8, |
|
757 PropModeReplace, (unsigned char *) buffer, |
|
758 bufend - ((char*) buffer)); |
|
759 free(buffer); |
|
760 |
|
761 if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandLineAtom)) |
|
762 return NS_ERROR_FAILURE; |
|
763 |
|
764 return NS_OK; |
|
765 } |
|
766 |
|
767 bool |
|
768 XRemoteClient::WaitForResponse(Window aWindow, char **aResponse, |
|
769 bool *aDestroyed, Atom aCommandAtom) |
|
770 { |
|
771 bool done = false; |
|
772 bool accepted = false; |
|
773 |
|
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 } |
|
824 |
|
825 else if (!strncmp ((char *)data, "200", 3)) { /* positive completion */ |
|
826 *aResponse = strdup((char *)data); |
|
827 accepted = true; |
|
828 done = true; |
|
829 } |
|
830 |
|
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 } |
|
837 |
|
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 } |
|
846 |
|
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 } |
|
853 |
|
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 } |
|
862 |
|
863 if (data) |
|
864 XFree(data); |
|
865 } |
|
866 |
|
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 } |
|
876 |
|
877 } |
|
878 |
|
879 return accepted; |
|
880 } |