michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:expandtab:shiftwidth=2:tabstop=8: michael@0: */ michael@0: /* vim:set ts=8 sw=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "XRemoteClient.h" michael@0: #include "prmem.h" michael@0: #include "prprf.h" michael@0: #include "plstr.h" michael@0: #include "prsystem.h" michael@0: #include "prlog.h" michael@0: #include "prenv.h" michael@0: #include "prdtoa.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #define MOZILLA_VERSION_PROP "_MOZILLA_VERSION" michael@0: #define MOZILLA_LOCK_PROP "_MOZILLA_LOCK" michael@0: #define MOZILLA_COMMAND_PROP "_MOZILLA_COMMAND" michael@0: #define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE" michael@0: #define MOZILLA_RESPONSE_PROP "_MOZILLA_RESPONSE" michael@0: #define MOZILLA_USER_PROP "_MOZILLA_USER" michael@0: #define MOZILLA_PROFILE_PROP "_MOZILLA_PROFILE" michael@0: #define MOZILLA_PROGRAM_PROP "_MOZILLA_PROGRAM" michael@0: michael@0: #ifdef IS_BIG_ENDIAN michael@0: #define TO_LITTLE_ENDIAN32(x) \ michael@0: ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ michael@0: (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) michael@0: #else michael@0: #define TO_LITTLE_ENDIAN32(x) (x) michael@0: #endif michael@0: michael@0: #ifndef MAX_PATH michael@0: #ifdef PATH_MAX michael@0: #define MAX_PATH PATH_MAX michael@0: #else michael@0: #define MAX_PATH 1024 michael@0: #endif michael@0: #endif michael@0: michael@0: #define ARRAY_LENGTH(array_) (sizeof(array_)/sizeof(array_[0])) michael@0: michael@0: static PRLogModuleInfo *sRemoteLm = nullptr; michael@0: michael@0: static int (*sOldHandler)(Display *, XErrorEvent *); michael@0: static bool sGotBadWindow; michael@0: michael@0: XRemoteClient::XRemoteClient() michael@0: { michael@0: mDisplay = 0; michael@0: mInitialized = false; michael@0: mMozVersionAtom = 0; michael@0: mMozLockAtom = 0; michael@0: mMozCommandAtom = 0; michael@0: mMozResponseAtom = 0; michael@0: mMozWMStateAtom = 0; michael@0: mMozUserAtom = 0; michael@0: mLockData = 0; michael@0: if (!sRemoteLm) michael@0: sRemoteLm = PR_NewLogModule("XRemoteClient"); michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::XRemoteClient")); michael@0: } michael@0: michael@0: XRemoteClient::~XRemoteClient() michael@0: { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::~XRemoteClient")); michael@0: if (mInitialized) michael@0: Shutdown(); michael@0: } michael@0: michael@0: // Minimize the roundtrips to the X-server michael@0: static const char *XAtomNames[] = { michael@0: MOZILLA_VERSION_PROP, michael@0: MOZILLA_LOCK_PROP, michael@0: MOZILLA_COMMAND_PROP, michael@0: MOZILLA_RESPONSE_PROP, michael@0: "WM_STATE", michael@0: MOZILLA_USER_PROP, michael@0: MOZILLA_PROFILE_PROP, michael@0: MOZILLA_PROGRAM_PROP, michael@0: MOZILLA_COMMANDLINE_PROP michael@0: }; michael@0: static Atom XAtoms[ARRAY_LENGTH(XAtomNames)]; michael@0: michael@0: nsresult michael@0: XRemoteClient::Init() michael@0: { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::Init")); michael@0: michael@0: if (mInitialized) michael@0: return NS_OK; michael@0: michael@0: // try to open the display michael@0: mDisplay = XOpenDisplay(0); michael@0: if (!mDisplay) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // get our atoms michael@0: XInternAtoms(mDisplay, const_cast(XAtomNames), michael@0: ARRAY_LENGTH(XAtomNames), False, XAtoms); michael@0: michael@0: int i = 0; michael@0: mMozVersionAtom = XAtoms[i++]; michael@0: mMozLockAtom = XAtoms[i++]; michael@0: mMozCommandAtom = XAtoms[i++]; michael@0: mMozResponseAtom = XAtoms[i++]; michael@0: mMozWMStateAtom = XAtoms[i++]; michael@0: mMozUserAtom = XAtoms[i++]; michael@0: mMozProfileAtom = XAtoms[i++]; michael@0: mMozProgramAtom = XAtoms[i++]; michael@0: mMozCommandLineAtom = XAtoms[i++]; michael@0: michael@0: mInitialized = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: XRemoteClient::Shutdown (void) michael@0: { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::Shutdown")); michael@0: michael@0: if (!mInitialized) michael@0: return; michael@0: michael@0: // shut everything down michael@0: XCloseDisplay(mDisplay); michael@0: mDisplay = 0; michael@0: mInitialized = false; michael@0: if (mLockData) { michael@0: free(mLockData); michael@0: mLockData = 0; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: XRemoteClient::SendCommand (const char *aProgram, const char *aUsername, michael@0: const char *aProfile, const char *aCommand, michael@0: const char* aDesktopStartupID, michael@0: char **aResponse, bool *aWindowFound) michael@0: { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommand")); michael@0: michael@0: return SendCommandInternal(aProgram, aUsername, aProfile, michael@0: aCommand, 0, nullptr, michael@0: aDesktopStartupID, michael@0: aResponse, aWindowFound); michael@0: } michael@0: michael@0: nsresult michael@0: XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername, michael@0: const char *aProfile, michael@0: int32_t argc, char **argv, michael@0: const char* aDesktopStartupID, michael@0: char **aResponse, bool *aWindowFound) michael@0: { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommandLine")); michael@0: michael@0: return SendCommandInternal(aProgram, aUsername, aProfile, michael@0: nullptr, argc, argv, michael@0: aDesktopStartupID, michael@0: aResponse, aWindowFound); michael@0: } michael@0: michael@0: static int michael@0: HandleBadWindow(Display *display, XErrorEvent *event) michael@0: { michael@0: if (event->error_code == BadWindow) { michael@0: sGotBadWindow = true; michael@0: return 0; // ignored michael@0: } michael@0: else { michael@0: return (*sOldHandler)(display, event); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: XRemoteClient::SendCommandInternal(const char *aProgram, const char *aUsername, michael@0: const char *aProfile, const char *aCommand, michael@0: int32_t argc, char **argv, michael@0: const char* aDesktopStartupID, michael@0: char **aResponse, bool *aWindowFound) michael@0: { michael@0: *aWindowFound = false; michael@0: bool isCommandLine = !aCommand; michael@0: michael@0: // FindBestWindow() iterates down the window hierarchy, so catch X errors michael@0: // when windows get destroyed before being accessed. michael@0: sOldHandler = XSetErrorHandler(HandleBadWindow); michael@0: michael@0: Window w = FindBestWindow(aProgram, aUsername, aProfile, isCommandLine); michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (w) { michael@0: // ok, let the caller know that we at least found a window. michael@0: *aWindowFound = true; michael@0: michael@0: // Ignore BadWindow errors up to this point. The last request from michael@0: // FindBestWindow() was a synchronous XGetWindowProperty(), so no need to michael@0: // Sync. Leave the error handler installed to detect if w gets destroyed. michael@0: sGotBadWindow = false; michael@0: michael@0: // make sure we get the right events on that window michael@0: XSelectInput(mDisplay, w, michael@0: (PropertyChangeMask|StructureNotifyMask)); michael@0: michael@0: bool destroyed = false; michael@0: michael@0: // get the lock on the window michael@0: rv = GetLock(w, &destroyed); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // send our command michael@0: if (isCommandLine) { michael@0: rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse, michael@0: &destroyed); michael@0: } michael@0: else { michael@0: rv = DoSendCommand(w, aCommand, aDesktopStartupID, aResponse, michael@0: &destroyed); michael@0: } michael@0: michael@0: // if the window was destroyed, don't bother trying to free the michael@0: // lock. michael@0: if (!destroyed) michael@0: FreeLock(w); // doesn't really matter what this returns michael@0: michael@0: } michael@0: } michael@0: michael@0: XSetErrorHandler(sOldHandler); michael@0: michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("SendCommandInternal returning 0x%x\n", rv)); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: Window michael@0: XRemoteClient::CheckWindow(Window aWindow) michael@0: { michael@0: Atom type = None; michael@0: int format; michael@0: unsigned long nitems, bytesafter; michael@0: unsigned char *data; michael@0: Window innerWindow; michael@0: michael@0: XGetWindowProperty(mDisplay, aWindow, mMozWMStateAtom, michael@0: 0, 0, False, AnyPropertyType, michael@0: &type, &format, &nitems, &bytesafter, &data); michael@0: michael@0: if (type) { michael@0: XFree(data); michael@0: return aWindow; michael@0: } michael@0: michael@0: // didn't find it here so check the children of this window michael@0: innerWindow = CheckChildren(aWindow); michael@0: michael@0: if (innerWindow) michael@0: return innerWindow; michael@0: michael@0: return aWindow; michael@0: } michael@0: michael@0: Window michael@0: XRemoteClient::CheckChildren(Window aWindow) michael@0: { michael@0: Window root, parent; michael@0: Window *children; michael@0: unsigned int nchildren; michael@0: unsigned int i; michael@0: Atom type = None; michael@0: int format; michael@0: unsigned long nitems, after; michael@0: unsigned char *data; michael@0: Window retval = None; michael@0: michael@0: if (!XQueryTree(mDisplay, aWindow, &root, &parent, &children, michael@0: &nchildren)) michael@0: return None; michael@0: michael@0: // scan the list first before recursing into the list of windows michael@0: // which can get quite deep. michael@0: for (i=0; !retval && (i < nchildren); i++) { michael@0: XGetWindowProperty(mDisplay, children[i], mMozWMStateAtom, michael@0: 0, 0, False, AnyPropertyType, &type, &format, michael@0: &nitems, &after, &data); michael@0: if (type) { michael@0: XFree(data); michael@0: retval = children[i]; michael@0: } michael@0: } michael@0: michael@0: // otherwise recurse into the list michael@0: for (i=0; !retval && (i < nchildren); i++) { michael@0: retval = CheckChildren(children[i]); michael@0: } michael@0: michael@0: if (children) michael@0: XFree((char *)children); michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: nsresult michael@0: XRemoteClient::GetLock(Window aWindow, bool *aDestroyed) michael@0: { michael@0: bool locked = false; michael@0: bool waited = false; michael@0: *aDestroyed = false; michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (!mLockData) { michael@0: michael@0: char pidstr[32]; michael@0: char sysinfobuf[SYS_INFO_BUFFER_LENGTH]; michael@0: PR_snprintf(pidstr, sizeof(pidstr), "pid%d@", getpid()); michael@0: PRStatus status; michael@0: status = PR_GetSystemInfo(PR_SI_HOSTNAME, sysinfobuf, michael@0: SYS_INFO_BUFFER_LENGTH); michael@0: if (status != PR_SUCCESS) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // allocate enough space for the string plus the terminating michael@0: // char michael@0: mLockData = (char *)malloc(strlen(pidstr) + strlen(sysinfobuf) + 1); michael@0: if (!mLockData) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: strcpy(mLockData, pidstr); michael@0: if (!strcat(mLockData, sysinfobuf)) michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: do { michael@0: int result; michael@0: Atom actual_type; michael@0: int actual_format; michael@0: unsigned long nitems, bytes_after; michael@0: unsigned char *data = 0; michael@0: michael@0: XGrabServer(mDisplay); michael@0: michael@0: result = XGetWindowProperty (mDisplay, aWindow, mMozLockAtom, michael@0: 0, (65536 / sizeof (long)), michael@0: False, /* don't delete */ michael@0: XA_STRING, michael@0: &actual_type, &actual_format, michael@0: &nitems, &bytes_after, michael@0: &data); michael@0: michael@0: // aWindow may have been destroyed before XSelectInput was processed, in michael@0: // which case there may not be any DestroyNotify event in the queue to michael@0: // tell us. XGetWindowProperty() was synchronous so error responses have michael@0: // now been processed, setting sGotBadWindow. michael@0: if (sGotBadWindow) { michael@0: *aDestroyed = true; michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: else if (result != Success || actual_type == None) { michael@0: /* It's not now locked - lock it. */ michael@0: XChangeProperty (mDisplay, aWindow, mMozLockAtom, XA_STRING, 8, michael@0: PropModeReplace, michael@0: (unsigned char *)mLockData, michael@0: strlen(mLockData)); michael@0: locked = True; michael@0: } michael@0: michael@0: XUngrabServer(mDisplay); michael@0: XFlush(mDisplay); // ungrab now! michael@0: michael@0: if (!locked && !NS_FAILED(rv)) { michael@0: /* We tried to grab the lock this time, and failed because someone michael@0: else is holding it already. So, wait for a PropertyDelete event michael@0: to come in, and try again. */ michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: ("window 0x%x is locked by %s; waiting...\n", michael@0: (unsigned int) aWindow, data)); michael@0: waited = True; michael@0: while (1) { michael@0: XEvent event; michael@0: int select_retval; michael@0: fd_set select_set; michael@0: struct timeval delay; michael@0: delay.tv_sec = 10; michael@0: delay.tv_usec = 0; michael@0: michael@0: FD_ZERO(&select_set); michael@0: // add the x event queue to the select set michael@0: FD_SET(ConnectionNumber(mDisplay), &select_set); michael@0: select_retval = select(ConnectionNumber(mDisplay) + 1, michael@0: &select_set, nullptr, nullptr, &delay); michael@0: // did we time out? michael@0: if (select_retval == 0) { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("timed out waiting for window\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: break; michael@0: } michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("xevent...\n")); michael@0: XNextEvent (mDisplay, &event); michael@0: if (event.xany.type == DestroyNotify && michael@0: event.xdestroywindow.window == aWindow) { michael@0: *aDestroyed = true; michael@0: rv = NS_ERROR_FAILURE; michael@0: break; michael@0: } michael@0: else if (event.xany.type == PropertyNotify && michael@0: event.xproperty.state == PropertyDelete && michael@0: event.xproperty.window == aWindow && michael@0: event.xproperty.atom == mMozLockAtom) { michael@0: /* Ok! Someone deleted their lock, so now we can try michael@0: again. */ michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: ("(0x%x unlocked, trying again...)\n", michael@0: (unsigned int) aWindow)); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: if (data) michael@0: XFree(data); michael@0: } while (!locked && !NS_FAILED(rv)); michael@0: michael@0: if (waited && locked) { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("obtained lock.\n")); michael@0: } else if (*aDestroyed) { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: ("window 0x%x unexpectedly destroyed.\n", michael@0: (unsigned int) aWindow)); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: Window michael@0: XRemoteClient::FindBestWindow(const char *aProgram, const char *aUsername, michael@0: const char *aProfile, michael@0: bool aSupportsCommandLine) michael@0: { michael@0: Window root = RootWindowOfScreen(DefaultScreenOfDisplay(mDisplay)); michael@0: Window bestWindow = 0; michael@0: Window root2, parent, *kids; michael@0: unsigned int nkids; michael@0: michael@0: // Get a list of the children of the root window, walk the list michael@0: // looking for the best window that fits the criteria. michael@0: if (!XQueryTree(mDisplay, root, &root2, &parent, &kids, &nkids)) { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: ("XQueryTree failed in XRemoteClient::FindBestWindow")); michael@0: return 0; michael@0: } michael@0: michael@0: if (!(kids && nkids)) { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("root window has no children")); michael@0: return 0; michael@0: } michael@0: michael@0: // We'll walk the list of windows looking for a window that best michael@0: // fits the criteria here. michael@0: michael@0: for (unsigned int i = 0; i < nkids; i++) { michael@0: Atom type; michael@0: int format; michael@0: unsigned long nitems, bytesafter; michael@0: unsigned char *data_return = 0; michael@0: Window w; michael@0: w = kids[i]; michael@0: // find the inner window with WM_STATE on it michael@0: w = CheckWindow(w); michael@0: michael@0: int status = XGetWindowProperty(mDisplay, w, mMozVersionAtom, michael@0: 0, (65536 / sizeof (long)), michael@0: False, XA_STRING, michael@0: &type, &format, &nitems, &bytesafter, michael@0: &data_return); michael@0: michael@0: if (!data_return) michael@0: continue; michael@0: michael@0: double version = PR_strtod((char*) data_return, nullptr); michael@0: XFree(data_return); michael@0: michael@0: if (aSupportsCommandLine && !(version >= 5.1 && version < 6)) michael@0: continue; michael@0: michael@0: data_return = 0; michael@0: michael@0: if (status != Success || type == None) michael@0: continue; michael@0: michael@0: // If someone passed in a program name, check it against this one michael@0: // unless it's "any" in which case, we don't care. If someone did michael@0: // pass in a program name and this window doesn't support that michael@0: // protocol, we don't include it in our list. michael@0: if (aProgram && strcmp(aProgram, "any")) { michael@0: status = XGetWindowProperty(mDisplay, w, mMozProgramAtom, michael@0: 0, (65536 / sizeof(long)), michael@0: False, XA_STRING, michael@0: &type, &format, &nitems, &bytesafter, michael@0: &data_return); michael@0: michael@0: // If the return name is not the same as what someone passed in, michael@0: // we don't want this window. michael@0: if (data_return) { michael@0: if (strcmp(aProgram, (const char *)data_return)) { michael@0: XFree(data_return); michael@0: continue; michael@0: } michael@0: michael@0: // This is actually the success condition. michael@0: XFree(data_return); michael@0: } michael@0: else { michael@0: // Doesn't support the protocol, even though the user michael@0: // requested it. So we're not going to use this window. michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: // Check to see if it has the user atom on that window. If there michael@0: // is then we need to make sure that it matches what we have. michael@0: const char *username; michael@0: if (aUsername) { michael@0: username = aUsername; michael@0: } michael@0: else { michael@0: username = PR_GetEnv("LOGNAME"); michael@0: } michael@0: michael@0: if (username) { michael@0: status = XGetWindowProperty(mDisplay, w, mMozUserAtom, michael@0: 0, (65536 / sizeof(long)), michael@0: False, XA_STRING, michael@0: &type, &format, &nitems, &bytesafter, michael@0: &data_return); michael@0: michael@0: // if there's a username compare it with what we have michael@0: if (data_return) { michael@0: // If the IDs aren't equal, we don't want this window. michael@0: if (strcmp(username, (const char *)data_return)) { michael@0: XFree(data_return); michael@0: continue; michael@0: } michael@0: michael@0: XFree(data_return); michael@0: } michael@0: } michael@0: michael@0: // Check to see if there's a profile name on this window. If michael@0: // there is, then we need to make sure it matches what someone michael@0: // passed in. michael@0: if (aProfile) { michael@0: status = XGetWindowProperty(mDisplay, w, mMozProfileAtom, michael@0: 0, (65536 / sizeof(long)), michael@0: False, XA_STRING, michael@0: &type, &format, &nitems, &bytesafter, michael@0: &data_return); michael@0: michael@0: // If there's a profile compare it with what we have michael@0: if (data_return) { michael@0: // If the profiles aren't equal, we don't want this window. michael@0: if (strcmp(aProfile, (const char *)data_return)) { michael@0: XFree(data_return); michael@0: continue; michael@0: } michael@0: michael@0: XFree(data_return); michael@0: } michael@0: } michael@0: michael@0: // Check to see if the window supports the new command-line passing michael@0: // protocol, if that is requested. michael@0: michael@0: // If we got this far, this is the best window. It passed michael@0: // all the tests. michael@0: bestWindow = w; michael@0: break; michael@0: } michael@0: michael@0: if (kids) michael@0: XFree((char *) kids); michael@0: michael@0: return bestWindow; michael@0: } michael@0: michael@0: nsresult michael@0: XRemoteClient::FreeLock(Window aWindow) michael@0: { michael@0: int result; michael@0: Atom actual_type; michael@0: int actual_format; michael@0: unsigned long nitems, bytes_after; michael@0: unsigned char *data = 0; michael@0: michael@0: result = XGetWindowProperty(mDisplay, aWindow, mMozLockAtom, michael@0: 0, (65536 / sizeof(long)), michael@0: True, /* atomic delete after */ michael@0: XA_STRING, michael@0: &actual_type, &actual_format, michael@0: &nitems, &bytes_after, michael@0: &data); michael@0: if (result != Success) { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: ("unable to read and delete " MOZILLA_LOCK_PROP michael@0: " property\n")); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: else if (!data || !*data){ michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: ("invalid data on " MOZILLA_LOCK_PROP michael@0: " of window 0x%x.\n", michael@0: (unsigned int) aWindow)); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: else if (strcmp((char *)data, mLockData)) { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: (MOZILLA_LOCK_PROP " was stolen! Expected \"%s\", saw \"%s\"!\n", michael@0: mLockData, data)); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (data) michael@0: XFree(data); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XRemoteClient::DoSendCommand(Window aWindow, const char *aCommand, michael@0: const char* aDesktopStartupID, michael@0: char **aResponse, bool *aDestroyed) michael@0: { michael@0: *aDestroyed = false; michael@0: michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: ("(writing " MOZILLA_COMMAND_PROP " \"%s\" to 0x%x)\n", michael@0: aCommand, (unsigned int) aWindow)); michael@0: michael@0: // We add the DESKTOP_STARTUP_ID setting as an extra line of michael@0: // the command string. Firefox ignores all lines but the first. michael@0: static char desktopStartupPrefix[] = "\nDESKTOP_STARTUP_ID="; michael@0: michael@0: int32_t len = strlen(aCommand); michael@0: if (aDesktopStartupID) { michael@0: len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID); michael@0: } michael@0: char* buffer = (char*)malloc(len + 1); michael@0: if (!buffer) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: strcpy(buffer, aCommand); michael@0: if (aDesktopStartupID) { michael@0: strcat(buffer, desktopStartupPrefix); michael@0: strcat(buffer, aDesktopStartupID); michael@0: } michael@0: michael@0: XChangeProperty (mDisplay, aWindow, mMozCommandAtom, XA_STRING, 8, michael@0: PropModeReplace, (unsigned char *)buffer, len); michael@0: michael@0: free(buffer); michael@0: michael@0: if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandAtom)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* like strcpy, but return the char after the final null */ michael@0: static char* michael@0: estrcpy(const char* s, char* d) michael@0: { michael@0: while (*s) michael@0: *d++ = *s++; michael@0: michael@0: *d++ = '\0'; michael@0: return d; michael@0: } michael@0: michael@0: nsresult michael@0: XRemoteClient::DoSendCommandLine(Window aWindow, int32_t argc, char **argv, michael@0: const char* aDesktopStartupID, michael@0: char **aResponse, bool *aDestroyed) michael@0: { michael@0: *aDestroyed = false; michael@0: michael@0: char cwdbuf[MAX_PATH]; michael@0: if (!getcwd(cwdbuf, MAX_PATH)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // the commandline property is constructed as an array of int32_t michael@0: // followed by a series of null-terminated strings: michael@0: // michael@0: // [argc][offsetargv0][offsetargv1...]\0\0argv[1]...\0 michael@0: // (offset is from the beginning of the buffer) michael@0: michael@0: static char desktopStartupPrefix[] = " DESKTOP_STARTUP_ID="; michael@0: michael@0: int32_t argvlen = strlen(cwdbuf); michael@0: for (int i = 0; i < argc; ++i) { michael@0: int32_t len = strlen(argv[i]); michael@0: if (i == 0 && aDesktopStartupID) { michael@0: len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID); michael@0: } michael@0: argvlen += len; michael@0: } michael@0: michael@0: int32_t* buffer = (int32_t*) malloc(argvlen + argc + 1 + michael@0: sizeof(int32_t) * (argc + 1)); michael@0: if (!buffer) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: buffer[0] = TO_LITTLE_ENDIAN32(argc); michael@0: michael@0: char *bufend = (char*) (buffer + argc + 1); michael@0: michael@0: bufend = estrcpy(cwdbuf, bufend); michael@0: michael@0: for (int i = 0; i < argc; ++i) { michael@0: buffer[i + 1] = TO_LITTLE_ENDIAN32(bufend - ((char*) buffer)); michael@0: bufend = estrcpy(argv[i], bufend); michael@0: if (i == 0 && aDesktopStartupID) { michael@0: bufend = estrcpy(desktopStartupPrefix, bufend - 1); michael@0: bufend = estrcpy(aDesktopStartupID, bufend - 1); michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG_bsmedberg michael@0: int32_t debug_argc = TO_LITTLE_ENDIAN32(*buffer); michael@0: char *debug_workingdir = (char*) (buffer + argc + 1); michael@0: michael@0: printf("Sending command line:\n" michael@0: " working dir: %s\n" michael@0: " argc:\t%i", michael@0: debug_workingdir, michael@0: debug_argc); michael@0: michael@0: int32_t *debug_offset = buffer + 1; michael@0: for (int debug_i = 0; debug_i < debug_argc; ++debug_i) michael@0: printf(" argv[%i]:\t%s\n", debug_i, michael@0: ((char*) buffer) + TO_LITTLE_ENDIAN32(debug_offset[debug_i])); michael@0: #endif michael@0: michael@0: XChangeProperty (mDisplay, aWindow, mMozCommandLineAtom, XA_STRING, 8, michael@0: PropModeReplace, (unsigned char *) buffer, michael@0: bufend - ((char*) buffer)); michael@0: free(buffer); michael@0: michael@0: if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandLineAtom)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: XRemoteClient::WaitForResponse(Window aWindow, char **aResponse, michael@0: bool *aDestroyed, Atom aCommandAtom) michael@0: { michael@0: bool done = false; michael@0: bool accepted = false; michael@0: michael@0: while (!done) { michael@0: XEvent event; michael@0: XNextEvent (mDisplay, &event); michael@0: if (event.xany.type == DestroyNotify && michael@0: event.xdestroywindow.window == aWindow) { michael@0: /* Print to warn user...*/ michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: ("window 0x%x was destroyed.\n", michael@0: (unsigned int) aWindow)); michael@0: *aResponse = strdup("Window was destroyed while reading response."); michael@0: *aDestroyed = true; michael@0: return false; michael@0: } michael@0: else if (event.xany.type == PropertyNotify && michael@0: event.xproperty.state == PropertyNewValue && michael@0: event.xproperty.window == aWindow && michael@0: event.xproperty.atom == mMozResponseAtom) { michael@0: Atom actual_type; michael@0: int actual_format; michael@0: unsigned long nitems, bytes_after; michael@0: unsigned char *data = 0; michael@0: Bool result; michael@0: result = XGetWindowProperty (mDisplay, aWindow, mMozResponseAtom, michael@0: 0, (65536 / sizeof (long)), michael@0: True, /* atomic delete after */ michael@0: XA_STRING, michael@0: &actual_type, &actual_format, michael@0: &nitems, &bytes_after, michael@0: &data); michael@0: if (result != Success) { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: ("failed reading " MOZILLA_RESPONSE_PROP michael@0: " from window 0x%0x.\n", michael@0: (unsigned int) aWindow)); michael@0: *aResponse = strdup("Internal error reading response from window."); michael@0: done = true; michael@0: } michael@0: else if (!data || strlen((char *) data) < 5) { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: ("invalid data on " MOZILLA_RESPONSE_PROP michael@0: " property of window 0x%0x.\n", michael@0: (unsigned int) aWindow)); michael@0: *aResponse = strdup("Server returned invalid data in response."); michael@0: done = true; michael@0: } michael@0: else if (*data == '1') { /* positive preliminary reply */ michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("%s\n", data + 4)); michael@0: /* keep going */ michael@0: done = false; michael@0: } michael@0: michael@0: else if (!strncmp ((char *)data, "200", 3)) { /* positive completion */ michael@0: *aResponse = strdup((char *)data); michael@0: accepted = true; michael@0: done = true; michael@0: } michael@0: michael@0: else if (*data == '2') { /* positive completion */ michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("%s\n", data + 4)); michael@0: *aResponse = strdup((char *)data); michael@0: accepted = true; michael@0: done = true; michael@0: } michael@0: michael@0: else if (*data == '3') { /* positive intermediate reply */ michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: ("internal error: " michael@0: "server wants more information? (%s)\n", michael@0: data)); michael@0: *aResponse = strdup((char *)data); michael@0: done = true; michael@0: } michael@0: michael@0: else if (*data == '4' || /* transient negative completion */ michael@0: *data == '5') { /* permanent negative completion */ michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("%s\n", data + 4)); michael@0: *aResponse = strdup((char *)data); michael@0: done = true; michael@0: } michael@0: michael@0: else { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: ("unrecognised " MOZILLA_RESPONSE_PROP michael@0: " from window 0x%x: %s\n", michael@0: (unsigned int) aWindow, data)); michael@0: *aResponse = strdup((char *)data); michael@0: done = true; michael@0: } michael@0: michael@0: if (data) michael@0: XFree(data); michael@0: } michael@0: michael@0: else if (event.xany.type == PropertyNotify && michael@0: event.xproperty.window == aWindow && michael@0: event.xproperty.state == PropertyDelete && michael@0: event.xproperty.atom == aCommandAtom) { michael@0: PR_LOG(sRemoteLm, PR_LOG_DEBUG, michael@0: ("(server 0x%x has accepted " michael@0: MOZILLA_COMMAND_PROP ".)\n", michael@0: (unsigned int) aWindow)); michael@0: } michael@0: michael@0: } michael@0: michael@0: return accepted; michael@0: }