PageRenderTime 60ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/widget/src/xremoteclient/XRemoteClient.cpp

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