widget/xremoteclient/XRemoteClient.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial