1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/xremoteclient/XRemoteClient.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,880 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:expandtab:shiftwidth=2:tabstop=8: 1.6 + */ 1.7 +/* vim:set ts=8 sw=2 et cindent: */ 1.8 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.9 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.10 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.11 + 1.12 +#include "XRemoteClient.h" 1.13 +#include "prmem.h" 1.14 +#include "prprf.h" 1.15 +#include "plstr.h" 1.16 +#include "prsystem.h" 1.17 +#include "prlog.h" 1.18 +#include "prenv.h" 1.19 +#include "prdtoa.h" 1.20 +#include <stdlib.h> 1.21 +#include <unistd.h> 1.22 +#include <string.h> 1.23 +#include <strings.h> 1.24 +#include <sys/time.h> 1.25 +#include <sys/types.h> 1.26 +#include <unistd.h> 1.27 +#include <limits.h> 1.28 +#include <X11/Xatom.h> 1.29 + 1.30 +#define MOZILLA_VERSION_PROP "_MOZILLA_VERSION" 1.31 +#define MOZILLA_LOCK_PROP "_MOZILLA_LOCK" 1.32 +#define MOZILLA_COMMAND_PROP "_MOZILLA_COMMAND" 1.33 +#define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE" 1.34 +#define MOZILLA_RESPONSE_PROP "_MOZILLA_RESPONSE" 1.35 +#define MOZILLA_USER_PROP "_MOZILLA_USER" 1.36 +#define MOZILLA_PROFILE_PROP "_MOZILLA_PROFILE" 1.37 +#define MOZILLA_PROGRAM_PROP "_MOZILLA_PROGRAM" 1.38 + 1.39 +#ifdef IS_BIG_ENDIAN 1.40 +#define TO_LITTLE_ENDIAN32(x) \ 1.41 + ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ 1.42 + (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) 1.43 +#else 1.44 +#define TO_LITTLE_ENDIAN32(x) (x) 1.45 +#endif 1.46 + 1.47 +#ifndef MAX_PATH 1.48 +#ifdef PATH_MAX 1.49 +#define MAX_PATH PATH_MAX 1.50 +#else 1.51 +#define MAX_PATH 1024 1.52 +#endif 1.53 +#endif 1.54 + 1.55 +#define ARRAY_LENGTH(array_) (sizeof(array_)/sizeof(array_[0])) 1.56 + 1.57 +static PRLogModuleInfo *sRemoteLm = nullptr; 1.58 + 1.59 +static int (*sOldHandler)(Display *, XErrorEvent *); 1.60 +static bool sGotBadWindow; 1.61 + 1.62 +XRemoteClient::XRemoteClient() 1.63 +{ 1.64 + mDisplay = 0; 1.65 + mInitialized = false; 1.66 + mMozVersionAtom = 0; 1.67 + mMozLockAtom = 0; 1.68 + mMozCommandAtom = 0; 1.69 + mMozResponseAtom = 0; 1.70 + mMozWMStateAtom = 0; 1.71 + mMozUserAtom = 0; 1.72 + mLockData = 0; 1.73 + if (!sRemoteLm) 1.74 + sRemoteLm = PR_NewLogModule("XRemoteClient"); 1.75 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::XRemoteClient")); 1.76 +} 1.77 + 1.78 +XRemoteClient::~XRemoteClient() 1.79 +{ 1.80 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::~XRemoteClient")); 1.81 + if (mInitialized) 1.82 + Shutdown(); 1.83 +} 1.84 + 1.85 +// Minimize the roundtrips to the X-server 1.86 +static const char *XAtomNames[] = { 1.87 + MOZILLA_VERSION_PROP, 1.88 + MOZILLA_LOCK_PROP, 1.89 + MOZILLA_COMMAND_PROP, 1.90 + MOZILLA_RESPONSE_PROP, 1.91 + "WM_STATE", 1.92 + MOZILLA_USER_PROP, 1.93 + MOZILLA_PROFILE_PROP, 1.94 + MOZILLA_PROGRAM_PROP, 1.95 + MOZILLA_COMMANDLINE_PROP 1.96 +}; 1.97 +static Atom XAtoms[ARRAY_LENGTH(XAtomNames)]; 1.98 + 1.99 +nsresult 1.100 +XRemoteClient::Init() 1.101 +{ 1.102 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::Init")); 1.103 + 1.104 + if (mInitialized) 1.105 + return NS_OK; 1.106 + 1.107 + // try to open the display 1.108 + mDisplay = XOpenDisplay(0); 1.109 + if (!mDisplay) 1.110 + return NS_ERROR_FAILURE; 1.111 + 1.112 + // get our atoms 1.113 + XInternAtoms(mDisplay, const_cast<char**>(XAtomNames), 1.114 + ARRAY_LENGTH(XAtomNames), False, XAtoms); 1.115 + 1.116 + int i = 0; 1.117 + mMozVersionAtom = XAtoms[i++]; 1.118 + mMozLockAtom = XAtoms[i++]; 1.119 + mMozCommandAtom = XAtoms[i++]; 1.120 + mMozResponseAtom = XAtoms[i++]; 1.121 + mMozWMStateAtom = XAtoms[i++]; 1.122 + mMozUserAtom = XAtoms[i++]; 1.123 + mMozProfileAtom = XAtoms[i++]; 1.124 + mMozProgramAtom = XAtoms[i++]; 1.125 + mMozCommandLineAtom = XAtoms[i++]; 1.126 + 1.127 + mInitialized = true; 1.128 + 1.129 + return NS_OK; 1.130 +} 1.131 + 1.132 +void 1.133 +XRemoteClient::Shutdown (void) 1.134 +{ 1.135 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::Shutdown")); 1.136 + 1.137 + if (!mInitialized) 1.138 + return; 1.139 + 1.140 + // shut everything down 1.141 + XCloseDisplay(mDisplay); 1.142 + mDisplay = 0; 1.143 + mInitialized = false; 1.144 + if (mLockData) { 1.145 + free(mLockData); 1.146 + mLockData = 0; 1.147 + } 1.148 +} 1.149 + 1.150 +nsresult 1.151 +XRemoteClient::SendCommand (const char *aProgram, const char *aUsername, 1.152 + const char *aProfile, const char *aCommand, 1.153 + const char* aDesktopStartupID, 1.154 + char **aResponse, bool *aWindowFound) 1.155 +{ 1.156 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommand")); 1.157 + 1.158 + return SendCommandInternal(aProgram, aUsername, aProfile, 1.159 + aCommand, 0, nullptr, 1.160 + aDesktopStartupID, 1.161 + aResponse, aWindowFound); 1.162 +} 1.163 + 1.164 +nsresult 1.165 +XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername, 1.166 + const char *aProfile, 1.167 + int32_t argc, char **argv, 1.168 + const char* aDesktopStartupID, 1.169 + char **aResponse, bool *aWindowFound) 1.170 +{ 1.171 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommandLine")); 1.172 + 1.173 + return SendCommandInternal(aProgram, aUsername, aProfile, 1.174 + nullptr, argc, argv, 1.175 + aDesktopStartupID, 1.176 + aResponse, aWindowFound); 1.177 +} 1.178 + 1.179 +static int 1.180 +HandleBadWindow(Display *display, XErrorEvent *event) 1.181 +{ 1.182 + if (event->error_code == BadWindow) { 1.183 + sGotBadWindow = true; 1.184 + return 0; // ignored 1.185 + } 1.186 + else { 1.187 + return (*sOldHandler)(display, event); 1.188 + } 1.189 +} 1.190 + 1.191 +nsresult 1.192 +XRemoteClient::SendCommandInternal(const char *aProgram, const char *aUsername, 1.193 + const char *aProfile, const char *aCommand, 1.194 + int32_t argc, char **argv, 1.195 + const char* aDesktopStartupID, 1.196 + char **aResponse, bool *aWindowFound) 1.197 +{ 1.198 + *aWindowFound = false; 1.199 + bool isCommandLine = !aCommand; 1.200 + 1.201 + // FindBestWindow() iterates down the window hierarchy, so catch X errors 1.202 + // when windows get destroyed before being accessed. 1.203 + sOldHandler = XSetErrorHandler(HandleBadWindow); 1.204 + 1.205 + Window w = FindBestWindow(aProgram, aUsername, aProfile, isCommandLine); 1.206 + 1.207 + nsresult rv = NS_OK; 1.208 + 1.209 + if (w) { 1.210 + // ok, let the caller know that we at least found a window. 1.211 + *aWindowFound = true; 1.212 + 1.213 + // Ignore BadWindow errors up to this point. The last request from 1.214 + // FindBestWindow() was a synchronous XGetWindowProperty(), so no need to 1.215 + // Sync. Leave the error handler installed to detect if w gets destroyed. 1.216 + sGotBadWindow = false; 1.217 + 1.218 + // make sure we get the right events on that window 1.219 + XSelectInput(mDisplay, w, 1.220 + (PropertyChangeMask|StructureNotifyMask)); 1.221 + 1.222 + bool destroyed = false; 1.223 + 1.224 + // get the lock on the window 1.225 + rv = GetLock(w, &destroyed); 1.226 + 1.227 + if (NS_SUCCEEDED(rv)) { 1.228 + // send our command 1.229 + if (isCommandLine) { 1.230 + rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse, 1.231 + &destroyed); 1.232 + } 1.233 + else { 1.234 + rv = DoSendCommand(w, aCommand, aDesktopStartupID, aResponse, 1.235 + &destroyed); 1.236 + } 1.237 + 1.238 + // if the window was destroyed, don't bother trying to free the 1.239 + // lock. 1.240 + if (!destroyed) 1.241 + FreeLock(w); // doesn't really matter what this returns 1.242 + 1.243 + } 1.244 + } 1.245 + 1.246 + XSetErrorHandler(sOldHandler); 1.247 + 1.248 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("SendCommandInternal returning 0x%x\n", rv)); 1.249 + 1.250 + return rv; 1.251 +} 1.252 + 1.253 +Window 1.254 +XRemoteClient::CheckWindow(Window aWindow) 1.255 +{ 1.256 + Atom type = None; 1.257 + int format; 1.258 + unsigned long nitems, bytesafter; 1.259 + unsigned char *data; 1.260 + Window innerWindow; 1.261 + 1.262 + XGetWindowProperty(mDisplay, aWindow, mMozWMStateAtom, 1.263 + 0, 0, False, AnyPropertyType, 1.264 + &type, &format, &nitems, &bytesafter, &data); 1.265 + 1.266 + if (type) { 1.267 + XFree(data); 1.268 + return aWindow; 1.269 + } 1.270 + 1.271 + // didn't find it here so check the children of this window 1.272 + innerWindow = CheckChildren(aWindow); 1.273 + 1.274 + if (innerWindow) 1.275 + return innerWindow; 1.276 + 1.277 + return aWindow; 1.278 +} 1.279 + 1.280 +Window 1.281 +XRemoteClient::CheckChildren(Window aWindow) 1.282 +{ 1.283 + Window root, parent; 1.284 + Window *children; 1.285 + unsigned int nchildren; 1.286 + unsigned int i; 1.287 + Atom type = None; 1.288 + int format; 1.289 + unsigned long nitems, after; 1.290 + unsigned char *data; 1.291 + Window retval = None; 1.292 + 1.293 + if (!XQueryTree(mDisplay, aWindow, &root, &parent, &children, 1.294 + &nchildren)) 1.295 + return None; 1.296 + 1.297 + // scan the list first before recursing into the list of windows 1.298 + // which can get quite deep. 1.299 + for (i=0; !retval && (i < nchildren); i++) { 1.300 + XGetWindowProperty(mDisplay, children[i], mMozWMStateAtom, 1.301 + 0, 0, False, AnyPropertyType, &type, &format, 1.302 + &nitems, &after, &data); 1.303 + if (type) { 1.304 + XFree(data); 1.305 + retval = children[i]; 1.306 + } 1.307 + } 1.308 + 1.309 + // otherwise recurse into the list 1.310 + for (i=0; !retval && (i < nchildren); i++) { 1.311 + retval = CheckChildren(children[i]); 1.312 + } 1.313 + 1.314 + if (children) 1.315 + XFree((char *)children); 1.316 + 1.317 + return retval; 1.318 +} 1.319 + 1.320 +nsresult 1.321 +XRemoteClient::GetLock(Window aWindow, bool *aDestroyed) 1.322 +{ 1.323 + bool locked = false; 1.324 + bool waited = false; 1.325 + *aDestroyed = false; 1.326 + 1.327 + nsresult rv = NS_OK; 1.328 + 1.329 + if (!mLockData) { 1.330 + 1.331 + char pidstr[32]; 1.332 + char sysinfobuf[SYS_INFO_BUFFER_LENGTH]; 1.333 + PR_snprintf(pidstr, sizeof(pidstr), "pid%d@", getpid()); 1.334 + PRStatus status; 1.335 + status = PR_GetSystemInfo(PR_SI_HOSTNAME, sysinfobuf, 1.336 + SYS_INFO_BUFFER_LENGTH); 1.337 + if (status != PR_SUCCESS) { 1.338 + return NS_ERROR_FAILURE; 1.339 + } 1.340 + 1.341 + // allocate enough space for the string plus the terminating 1.342 + // char 1.343 + mLockData = (char *)malloc(strlen(pidstr) + strlen(sysinfobuf) + 1); 1.344 + if (!mLockData) 1.345 + return NS_ERROR_OUT_OF_MEMORY; 1.346 + 1.347 + strcpy(mLockData, pidstr); 1.348 + if (!strcat(mLockData, sysinfobuf)) 1.349 + return NS_ERROR_FAILURE; 1.350 + } 1.351 + 1.352 + do { 1.353 + int result; 1.354 + Atom actual_type; 1.355 + int actual_format; 1.356 + unsigned long nitems, bytes_after; 1.357 + unsigned char *data = 0; 1.358 + 1.359 + XGrabServer(mDisplay); 1.360 + 1.361 + result = XGetWindowProperty (mDisplay, aWindow, mMozLockAtom, 1.362 + 0, (65536 / sizeof (long)), 1.363 + False, /* don't delete */ 1.364 + XA_STRING, 1.365 + &actual_type, &actual_format, 1.366 + &nitems, &bytes_after, 1.367 + &data); 1.368 + 1.369 + // aWindow may have been destroyed before XSelectInput was processed, in 1.370 + // which case there may not be any DestroyNotify event in the queue to 1.371 + // tell us. XGetWindowProperty() was synchronous so error responses have 1.372 + // now been processed, setting sGotBadWindow. 1.373 + if (sGotBadWindow) { 1.374 + *aDestroyed = true; 1.375 + rv = NS_ERROR_FAILURE; 1.376 + } 1.377 + else if (result != Success || actual_type == None) { 1.378 + /* It's not now locked - lock it. */ 1.379 + XChangeProperty (mDisplay, aWindow, mMozLockAtom, XA_STRING, 8, 1.380 + PropModeReplace, 1.381 + (unsigned char *)mLockData, 1.382 + strlen(mLockData)); 1.383 + locked = True; 1.384 + } 1.385 + 1.386 + XUngrabServer(mDisplay); 1.387 + XFlush(mDisplay); // ungrab now! 1.388 + 1.389 + if (!locked && !NS_FAILED(rv)) { 1.390 + /* We tried to grab the lock this time, and failed because someone 1.391 + else is holding it already. So, wait for a PropertyDelete event 1.392 + to come in, and try again. */ 1.393 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.394 + ("window 0x%x is locked by %s; waiting...\n", 1.395 + (unsigned int) aWindow, data)); 1.396 + waited = True; 1.397 + while (1) { 1.398 + XEvent event; 1.399 + int select_retval; 1.400 + fd_set select_set; 1.401 + struct timeval delay; 1.402 + delay.tv_sec = 10; 1.403 + delay.tv_usec = 0; 1.404 + 1.405 + FD_ZERO(&select_set); 1.406 + // add the x event queue to the select set 1.407 + FD_SET(ConnectionNumber(mDisplay), &select_set); 1.408 + select_retval = select(ConnectionNumber(mDisplay) + 1, 1.409 + &select_set, nullptr, nullptr, &delay); 1.410 + // did we time out? 1.411 + if (select_retval == 0) { 1.412 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("timed out waiting for window\n")); 1.413 + rv = NS_ERROR_FAILURE; 1.414 + break; 1.415 + } 1.416 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("xevent...\n")); 1.417 + XNextEvent (mDisplay, &event); 1.418 + if (event.xany.type == DestroyNotify && 1.419 + event.xdestroywindow.window == aWindow) { 1.420 + *aDestroyed = true; 1.421 + rv = NS_ERROR_FAILURE; 1.422 + break; 1.423 + } 1.424 + else if (event.xany.type == PropertyNotify && 1.425 + event.xproperty.state == PropertyDelete && 1.426 + event.xproperty.window == aWindow && 1.427 + event.xproperty.atom == mMozLockAtom) { 1.428 + /* Ok! Someone deleted their lock, so now we can try 1.429 + again. */ 1.430 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.431 + ("(0x%x unlocked, trying again...)\n", 1.432 + (unsigned int) aWindow)); 1.433 + break; 1.434 + } 1.435 + } 1.436 + } 1.437 + if (data) 1.438 + XFree(data); 1.439 + } while (!locked && !NS_FAILED(rv)); 1.440 + 1.441 + if (waited && locked) { 1.442 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("obtained lock.\n")); 1.443 + } else if (*aDestroyed) { 1.444 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.445 + ("window 0x%x unexpectedly destroyed.\n", 1.446 + (unsigned int) aWindow)); 1.447 + } 1.448 + 1.449 + return rv; 1.450 +} 1.451 + 1.452 +Window 1.453 +XRemoteClient::FindBestWindow(const char *aProgram, const char *aUsername, 1.454 + const char *aProfile, 1.455 + bool aSupportsCommandLine) 1.456 +{ 1.457 + Window root = RootWindowOfScreen(DefaultScreenOfDisplay(mDisplay)); 1.458 + Window bestWindow = 0; 1.459 + Window root2, parent, *kids; 1.460 + unsigned int nkids; 1.461 + 1.462 + // Get a list of the children of the root window, walk the list 1.463 + // looking for the best window that fits the criteria. 1.464 + if (!XQueryTree(mDisplay, root, &root2, &parent, &kids, &nkids)) { 1.465 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.466 + ("XQueryTree failed in XRemoteClient::FindBestWindow")); 1.467 + return 0; 1.468 + } 1.469 + 1.470 + if (!(kids && nkids)) { 1.471 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("root window has no children")); 1.472 + return 0; 1.473 + } 1.474 + 1.475 + // We'll walk the list of windows looking for a window that best 1.476 + // fits the criteria here. 1.477 + 1.478 + for (unsigned int i = 0; i < nkids; i++) { 1.479 + Atom type; 1.480 + int format; 1.481 + unsigned long nitems, bytesafter; 1.482 + unsigned char *data_return = 0; 1.483 + Window w; 1.484 + w = kids[i]; 1.485 + // find the inner window with WM_STATE on it 1.486 + w = CheckWindow(w); 1.487 + 1.488 + int status = XGetWindowProperty(mDisplay, w, mMozVersionAtom, 1.489 + 0, (65536 / sizeof (long)), 1.490 + False, XA_STRING, 1.491 + &type, &format, &nitems, &bytesafter, 1.492 + &data_return); 1.493 + 1.494 + if (!data_return) 1.495 + continue; 1.496 + 1.497 + double version = PR_strtod((char*) data_return, nullptr); 1.498 + XFree(data_return); 1.499 + 1.500 + if (aSupportsCommandLine && !(version >= 5.1 && version < 6)) 1.501 + continue; 1.502 + 1.503 + data_return = 0; 1.504 + 1.505 + if (status != Success || type == None) 1.506 + continue; 1.507 + 1.508 + // If someone passed in a program name, check it against this one 1.509 + // unless it's "any" in which case, we don't care. If someone did 1.510 + // pass in a program name and this window doesn't support that 1.511 + // protocol, we don't include it in our list. 1.512 + if (aProgram && strcmp(aProgram, "any")) { 1.513 + status = XGetWindowProperty(mDisplay, w, mMozProgramAtom, 1.514 + 0, (65536 / sizeof(long)), 1.515 + False, XA_STRING, 1.516 + &type, &format, &nitems, &bytesafter, 1.517 + &data_return); 1.518 + 1.519 + // If the return name is not the same as what someone passed in, 1.520 + // we don't want this window. 1.521 + if (data_return) { 1.522 + if (strcmp(aProgram, (const char *)data_return)) { 1.523 + XFree(data_return); 1.524 + continue; 1.525 + } 1.526 + 1.527 + // This is actually the success condition. 1.528 + XFree(data_return); 1.529 + } 1.530 + else { 1.531 + // Doesn't support the protocol, even though the user 1.532 + // requested it. So we're not going to use this window. 1.533 + continue; 1.534 + } 1.535 + } 1.536 + 1.537 + // Check to see if it has the user atom on that window. If there 1.538 + // is then we need to make sure that it matches what we have. 1.539 + const char *username; 1.540 + if (aUsername) { 1.541 + username = aUsername; 1.542 + } 1.543 + else { 1.544 + username = PR_GetEnv("LOGNAME"); 1.545 + } 1.546 + 1.547 + if (username) { 1.548 + status = XGetWindowProperty(mDisplay, w, mMozUserAtom, 1.549 + 0, (65536 / sizeof(long)), 1.550 + False, XA_STRING, 1.551 + &type, &format, &nitems, &bytesafter, 1.552 + &data_return); 1.553 + 1.554 + // if there's a username compare it with what we have 1.555 + if (data_return) { 1.556 + // If the IDs aren't equal, we don't want this window. 1.557 + if (strcmp(username, (const char *)data_return)) { 1.558 + XFree(data_return); 1.559 + continue; 1.560 + } 1.561 + 1.562 + XFree(data_return); 1.563 + } 1.564 + } 1.565 + 1.566 + // Check to see if there's a profile name on this window. If 1.567 + // there is, then we need to make sure it matches what someone 1.568 + // passed in. 1.569 + if (aProfile) { 1.570 + status = XGetWindowProperty(mDisplay, w, mMozProfileAtom, 1.571 + 0, (65536 / sizeof(long)), 1.572 + False, XA_STRING, 1.573 + &type, &format, &nitems, &bytesafter, 1.574 + &data_return); 1.575 + 1.576 + // If there's a profile compare it with what we have 1.577 + if (data_return) { 1.578 + // If the profiles aren't equal, we don't want this window. 1.579 + if (strcmp(aProfile, (const char *)data_return)) { 1.580 + XFree(data_return); 1.581 + continue; 1.582 + } 1.583 + 1.584 + XFree(data_return); 1.585 + } 1.586 + } 1.587 + 1.588 + // Check to see if the window supports the new command-line passing 1.589 + // protocol, if that is requested. 1.590 + 1.591 + // If we got this far, this is the best window. It passed 1.592 + // all the tests. 1.593 + bestWindow = w; 1.594 + break; 1.595 + } 1.596 + 1.597 + if (kids) 1.598 + XFree((char *) kids); 1.599 + 1.600 + return bestWindow; 1.601 +} 1.602 + 1.603 +nsresult 1.604 +XRemoteClient::FreeLock(Window aWindow) 1.605 +{ 1.606 + int result; 1.607 + Atom actual_type; 1.608 + int actual_format; 1.609 + unsigned long nitems, bytes_after; 1.610 + unsigned char *data = 0; 1.611 + 1.612 + result = XGetWindowProperty(mDisplay, aWindow, mMozLockAtom, 1.613 + 0, (65536 / sizeof(long)), 1.614 + True, /* atomic delete after */ 1.615 + XA_STRING, 1.616 + &actual_type, &actual_format, 1.617 + &nitems, &bytes_after, 1.618 + &data); 1.619 + if (result != Success) { 1.620 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.621 + ("unable to read and delete " MOZILLA_LOCK_PROP 1.622 + " property\n")); 1.623 + return NS_ERROR_FAILURE; 1.624 + } 1.625 + else if (!data || !*data){ 1.626 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.627 + ("invalid data on " MOZILLA_LOCK_PROP 1.628 + " of window 0x%x.\n", 1.629 + (unsigned int) aWindow)); 1.630 + return NS_ERROR_FAILURE; 1.631 + } 1.632 + else if (strcmp((char *)data, mLockData)) { 1.633 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.634 + (MOZILLA_LOCK_PROP " was stolen! Expected \"%s\", saw \"%s\"!\n", 1.635 + mLockData, data)); 1.636 + return NS_ERROR_FAILURE; 1.637 + } 1.638 + 1.639 + if (data) 1.640 + XFree(data); 1.641 + return NS_OK; 1.642 +} 1.643 + 1.644 +nsresult 1.645 +XRemoteClient::DoSendCommand(Window aWindow, const char *aCommand, 1.646 + const char* aDesktopStartupID, 1.647 + char **aResponse, bool *aDestroyed) 1.648 +{ 1.649 + *aDestroyed = false; 1.650 + 1.651 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.652 + ("(writing " MOZILLA_COMMAND_PROP " \"%s\" to 0x%x)\n", 1.653 + aCommand, (unsigned int) aWindow)); 1.654 + 1.655 + // We add the DESKTOP_STARTUP_ID setting as an extra line of 1.656 + // the command string. Firefox ignores all lines but the first. 1.657 + static char desktopStartupPrefix[] = "\nDESKTOP_STARTUP_ID="; 1.658 + 1.659 + int32_t len = strlen(aCommand); 1.660 + if (aDesktopStartupID) { 1.661 + len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID); 1.662 + } 1.663 + char* buffer = (char*)malloc(len + 1); 1.664 + if (!buffer) 1.665 + return NS_ERROR_OUT_OF_MEMORY; 1.666 + 1.667 + strcpy(buffer, aCommand); 1.668 + if (aDesktopStartupID) { 1.669 + strcat(buffer, desktopStartupPrefix); 1.670 + strcat(buffer, aDesktopStartupID); 1.671 + } 1.672 + 1.673 + XChangeProperty (mDisplay, aWindow, mMozCommandAtom, XA_STRING, 8, 1.674 + PropModeReplace, (unsigned char *)buffer, len); 1.675 + 1.676 + free(buffer); 1.677 + 1.678 + if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandAtom)) 1.679 + return NS_ERROR_FAILURE; 1.680 + 1.681 + return NS_OK; 1.682 +} 1.683 + 1.684 +/* like strcpy, but return the char after the final null */ 1.685 +static char* 1.686 +estrcpy(const char* s, char* d) 1.687 +{ 1.688 + while (*s) 1.689 + *d++ = *s++; 1.690 + 1.691 + *d++ = '\0'; 1.692 + return d; 1.693 +} 1.694 + 1.695 +nsresult 1.696 +XRemoteClient::DoSendCommandLine(Window aWindow, int32_t argc, char **argv, 1.697 + const char* aDesktopStartupID, 1.698 + char **aResponse, bool *aDestroyed) 1.699 +{ 1.700 + *aDestroyed = false; 1.701 + 1.702 + char cwdbuf[MAX_PATH]; 1.703 + if (!getcwd(cwdbuf, MAX_PATH)) 1.704 + return NS_ERROR_UNEXPECTED; 1.705 + 1.706 + // the commandline property is constructed as an array of int32_t 1.707 + // followed by a series of null-terminated strings: 1.708 + // 1.709 + // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0 1.710 + // (offset is from the beginning of the buffer) 1.711 + 1.712 + static char desktopStartupPrefix[] = " DESKTOP_STARTUP_ID="; 1.713 + 1.714 + int32_t argvlen = strlen(cwdbuf); 1.715 + for (int i = 0; i < argc; ++i) { 1.716 + int32_t len = strlen(argv[i]); 1.717 + if (i == 0 && aDesktopStartupID) { 1.718 + len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID); 1.719 + } 1.720 + argvlen += len; 1.721 + } 1.722 + 1.723 + int32_t* buffer = (int32_t*) malloc(argvlen + argc + 1 + 1.724 + sizeof(int32_t) * (argc + 1)); 1.725 + if (!buffer) 1.726 + return NS_ERROR_OUT_OF_MEMORY; 1.727 + 1.728 + buffer[0] = TO_LITTLE_ENDIAN32(argc); 1.729 + 1.730 + char *bufend = (char*) (buffer + argc + 1); 1.731 + 1.732 + bufend = estrcpy(cwdbuf, bufend); 1.733 + 1.734 + for (int i = 0; i < argc; ++i) { 1.735 + buffer[i + 1] = TO_LITTLE_ENDIAN32(bufend - ((char*) buffer)); 1.736 + bufend = estrcpy(argv[i], bufend); 1.737 + if (i == 0 && aDesktopStartupID) { 1.738 + bufend = estrcpy(desktopStartupPrefix, bufend - 1); 1.739 + bufend = estrcpy(aDesktopStartupID, bufend - 1); 1.740 + } 1.741 + } 1.742 + 1.743 +#ifdef DEBUG_bsmedberg 1.744 + int32_t debug_argc = TO_LITTLE_ENDIAN32(*buffer); 1.745 + char *debug_workingdir = (char*) (buffer + argc + 1); 1.746 + 1.747 + printf("Sending command line:\n" 1.748 + " working dir: %s\n" 1.749 + " argc:\t%i", 1.750 + debug_workingdir, 1.751 + debug_argc); 1.752 + 1.753 + int32_t *debug_offset = buffer + 1; 1.754 + for (int debug_i = 0; debug_i < debug_argc; ++debug_i) 1.755 + printf(" argv[%i]:\t%s\n", debug_i, 1.756 + ((char*) buffer) + TO_LITTLE_ENDIAN32(debug_offset[debug_i])); 1.757 +#endif 1.758 + 1.759 + XChangeProperty (mDisplay, aWindow, mMozCommandLineAtom, XA_STRING, 8, 1.760 + PropModeReplace, (unsigned char *) buffer, 1.761 + bufend - ((char*) buffer)); 1.762 + free(buffer); 1.763 + 1.764 + if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandLineAtom)) 1.765 + return NS_ERROR_FAILURE; 1.766 + 1.767 + return NS_OK; 1.768 +} 1.769 + 1.770 +bool 1.771 +XRemoteClient::WaitForResponse(Window aWindow, char **aResponse, 1.772 + bool *aDestroyed, Atom aCommandAtom) 1.773 +{ 1.774 + bool done = false; 1.775 + bool accepted = false; 1.776 + 1.777 + while (!done) { 1.778 + XEvent event; 1.779 + XNextEvent (mDisplay, &event); 1.780 + if (event.xany.type == DestroyNotify && 1.781 + event.xdestroywindow.window == aWindow) { 1.782 + /* Print to warn user...*/ 1.783 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.784 + ("window 0x%x was destroyed.\n", 1.785 + (unsigned int) aWindow)); 1.786 + *aResponse = strdup("Window was destroyed while reading response."); 1.787 + *aDestroyed = true; 1.788 + return false; 1.789 + } 1.790 + else if (event.xany.type == PropertyNotify && 1.791 + event.xproperty.state == PropertyNewValue && 1.792 + event.xproperty.window == aWindow && 1.793 + event.xproperty.atom == mMozResponseAtom) { 1.794 + Atom actual_type; 1.795 + int actual_format; 1.796 + unsigned long nitems, bytes_after; 1.797 + unsigned char *data = 0; 1.798 + Bool result; 1.799 + result = XGetWindowProperty (mDisplay, aWindow, mMozResponseAtom, 1.800 + 0, (65536 / sizeof (long)), 1.801 + True, /* atomic delete after */ 1.802 + XA_STRING, 1.803 + &actual_type, &actual_format, 1.804 + &nitems, &bytes_after, 1.805 + &data); 1.806 + if (result != Success) { 1.807 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.808 + ("failed reading " MOZILLA_RESPONSE_PROP 1.809 + " from window 0x%0x.\n", 1.810 + (unsigned int) aWindow)); 1.811 + *aResponse = strdup("Internal error reading response from window."); 1.812 + done = true; 1.813 + } 1.814 + else if (!data || strlen((char *) data) < 5) { 1.815 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.816 + ("invalid data on " MOZILLA_RESPONSE_PROP 1.817 + " property of window 0x%0x.\n", 1.818 + (unsigned int) aWindow)); 1.819 + *aResponse = strdup("Server returned invalid data in response."); 1.820 + done = true; 1.821 + } 1.822 + else if (*data == '1') { /* positive preliminary reply */ 1.823 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("%s\n", data + 4)); 1.824 + /* keep going */ 1.825 + done = false; 1.826 + } 1.827 + 1.828 + else if (!strncmp ((char *)data, "200", 3)) { /* positive completion */ 1.829 + *aResponse = strdup((char *)data); 1.830 + accepted = true; 1.831 + done = true; 1.832 + } 1.833 + 1.834 + else if (*data == '2') { /* positive completion */ 1.835 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("%s\n", data + 4)); 1.836 + *aResponse = strdup((char *)data); 1.837 + accepted = true; 1.838 + done = true; 1.839 + } 1.840 + 1.841 + else if (*data == '3') { /* positive intermediate reply */ 1.842 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.843 + ("internal error: " 1.844 + "server wants more information? (%s)\n", 1.845 + data)); 1.846 + *aResponse = strdup((char *)data); 1.847 + done = true; 1.848 + } 1.849 + 1.850 + else if (*data == '4' || /* transient negative completion */ 1.851 + *data == '5') { /* permanent negative completion */ 1.852 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("%s\n", data + 4)); 1.853 + *aResponse = strdup((char *)data); 1.854 + done = true; 1.855 + } 1.856 + 1.857 + else { 1.858 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.859 + ("unrecognised " MOZILLA_RESPONSE_PROP 1.860 + " from window 0x%x: %s\n", 1.861 + (unsigned int) aWindow, data)); 1.862 + *aResponse = strdup((char *)data); 1.863 + done = true; 1.864 + } 1.865 + 1.866 + if (data) 1.867 + XFree(data); 1.868 + } 1.869 + 1.870 + else if (event.xany.type == PropertyNotify && 1.871 + event.xproperty.window == aWindow && 1.872 + event.xproperty.state == PropertyDelete && 1.873 + event.xproperty.atom == aCommandAtom) { 1.874 + PR_LOG(sRemoteLm, PR_LOG_DEBUG, 1.875 + ("(server 0x%x has accepted " 1.876 + MOZILLA_COMMAND_PROP ".)\n", 1.877 + (unsigned int) aWindow)); 1.878 + } 1.879 + 1.880 + } 1.881 + 1.882 + return accepted; 1.883 +}