PageRenderTime 51ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/widget/src/xremoteclient/XRemoteClient.cpp

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