PageRenderTime 58ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/widget/src/xremoteclient/XRemoteClient.cpp

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