Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:expandtab:shiftwidth=2:tabstop=8:
3 : */
4 : /* This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 :
8 : #include "mozilla/ArrayUtils.h"
9 :
10 : #include "nsXRemoteService.h"
11 : #include "nsIObserverService.h"
12 : #include "nsCOMPtr.h"
13 : #include "nsIServiceManager.h"
14 : #include "nsICommandLineRunner.h"
15 : #include "nsICommandLine.h"
16 :
17 : #include "nsIBaseWindow.h"
18 : #include "nsIDocShell.h"
19 : #include "nsIFile.h"
20 : #include "nsIServiceManager.h"
21 : #include "nsIWeakReference.h"
22 : #include "nsIWidget.h"
23 : #include "nsIAppShellService.h"
24 : #include "nsAppShellCID.h"
25 : #include "nsPIDOMWindow.h"
26 : #include "mozilla/X11Util.h"
27 :
28 : #include "nsCOMPtr.h"
29 : #include "nsString.h"
30 : #include "prenv.h"
31 : #include "nsCRT.h"
32 :
33 : #include "nsXULAppAPI.h"
34 :
35 : #include <X11/Xlib.h>
36 : #include <X11/Xatom.h>
37 :
38 : using namespace mozilla;
39 :
40 : #define MOZILLA_VERSION_PROP "_MOZILLA_VERSION"
41 : #define MOZILLA_LOCK_PROP "_MOZILLA_LOCK"
42 : #define MOZILLA_RESPONSE_PROP "_MOZILLA_RESPONSE"
43 : #define MOZILLA_USER_PROP "_MOZILLA_USER"
44 : #define MOZILLA_PROFILE_PROP "_MOZILLA_PROFILE"
45 : #define MOZILLA_PROGRAM_PROP "_MOZILLA_PROGRAM"
46 : #define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
47 :
48 : const unsigned char kRemoteVersion[] = "5.1";
49 :
50 : #ifdef IS_BIG_ENDIAN
51 : #define TO_LITTLE_ENDIAN32(x) \
52 : ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
53 : (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
54 : #else
55 : #define TO_LITTLE_ENDIAN32(x) (x)
56 : #endif
57 :
58 : // Minimize the roundtrips to the X server by getting all the atoms at once
59 : static const char *XAtomNames[] = {
60 : MOZILLA_VERSION_PROP,
61 : MOZILLA_LOCK_PROP,
62 : MOZILLA_RESPONSE_PROP,
63 : MOZILLA_USER_PROP,
64 : MOZILLA_PROFILE_PROP,
65 : MOZILLA_PROGRAM_PROP,
66 : MOZILLA_COMMANDLINE_PROP
67 : };
68 : static Atom XAtoms[MOZ_ARRAY_LENGTH(XAtomNames)];
69 :
70 : Atom nsXRemoteService::sMozVersionAtom;
71 : Atom nsXRemoteService::sMozLockAtom;
72 : Atom nsXRemoteService::sMozResponseAtom;
73 : Atom nsXRemoteService::sMozUserAtom;
74 : Atom nsXRemoteService::sMozProfileAtom;
75 : Atom nsXRemoteService::sMozProgramAtom;
76 : Atom nsXRemoteService::sMozCommandLineAtom;
77 :
78 : nsXRemoteService * nsXRemoteService::sRemoteImplementation = 0;
79 :
80 :
81 : static bool
82 0 : FindExtensionParameterInCommand(const char* aParameterName,
83 : const nsACString& aCommand,
84 : char aSeparator,
85 : nsACString* aValue)
86 : {
87 0 : nsAutoCString searchFor;
88 0 : searchFor.Append(aSeparator);
89 0 : searchFor.Append(aParameterName);
90 0 : searchFor.Append('=');
91 :
92 0 : nsACString::const_iterator start, end;
93 0 : aCommand.BeginReading(start);
94 0 : aCommand.EndReading(end);
95 0 : if (!FindInReadable(searchFor, start, end))
96 0 : return false;
97 :
98 0 : nsACString::const_iterator charStart, charEnd;
99 0 : charStart = end;
100 0 : aCommand.EndReading(charEnd);
101 0 : nsACString::const_iterator idStart = charStart, idEnd;
102 0 : if (FindCharInReadable(aSeparator, charStart, charEnd)) {
103 0 : idEnd = charStart;
104 : } else {
105 0 : idEnd = charEnd;
106 : }
107 0 : *aValue = nsDependentCSubstring(idStart, idEnd);
108 0 : return true;
109 : }
110 :
111 :
112 : nsXRemoteService::nsXRemoteService() = default;
113 :
114 : void
115 0 : nsXRemoteService::XRemoteBaseStartup(const char *aAppName, const char *aProfileName)
116 : {
117 0 : EnsureAtoms();
118 :
119 0 : mAppName = aAppName;
120 0 : ToLowerCase(mAppName);
121 :
122 0 : mProfileName = aProfileName;
123 :
124 0 : nsCOMPtr<nsIObserverService> obs(do_GetService("@mozilla.org/observer-service;1"));
125 0 : if (obs) {
126 0 : obs->AddObserver(this, "xpcom-shutdown", false);
127 0 : obs->AddObserver(this, "quit-application", false);
128 : }
129 0 : }
130 :
131 : void
132 0 : nsXRemoteService::HandleCommandsFor(Window aWindowId)
133 : {
134 : // set our version
135 0 : XChangeProperty(mozilla::DefaultXDisplay(), aWindowId, sMozVersionAtom, XA_STRING,
136 0 : 8, PropModeReplace, kRemoteVersion, sizeof(kRemoteVersion) - 1);
137 :
138 : // get our username
139 : unsigned char *logname;
140 0 : logname = (unsigned char*) PR_GetEnv("LOGNAME");
141 0 : if (logname) {
142 : // set the property on the window if it's available
143 0 : XChangeProperty(mozilla::DefaultXDisplay(), aWindowId, sMozUserAtom, XA_STRING,
144 0 : 8, PropModeReplace, logname, strlen((char*) logname));
145 : }
146 :
147 0 : XChangeProperty(mozilla::DefaultXDisplay(), aWindowId, sMozProgramAtom, XA_STRING,
148 0 : 8, PropModeReplace, (unsigned char*) mAppName.get(), mAppName.Length());
149 :
150 0 : if (!mProfileName.IsEmpty()) {
151 0 : XChangeProperty(mozilla::DefaultXDisplay(),
152 : aWindowId, sMozProfileAtom, XA_STRING,
153 : 8, PropModeReplace,
154 0 : (unsigned char*) mProfileName.get(), mProfileName.Length());
155 : }
156 :
157 0 : }
158 :
159 : NS_IMETHODIMP
160 0 : nsXRemoteService::Observe(nsISupports* aSubject,
161 : const char *aTopic,
162 : const char16_t *aData)
163 : {
164 : // This can be xpcom-shutdown or quit-application, but it's the same either
165 : // way.
166 0 : Shutdown();
167 0 : return NS_OK;
168 : }
169 :
170 : bool
171 0 : nsXRemoteService::HandleNewProperty(XID aWindowId, Display* aDisplay,
172 : Time aEventTime,
173 : Atom aChangedAtom,
174 : nsIWeakReference* aDomWindow)
175 : {
176 :
177 0 : nsCOMPtr<nsIDOMWindow> window (do_QueryReferent(aDomWindow));
178 :
179 0 : if (aChangedAtom == sMozCommandLineAtom) {
180 : // We got a new command atom.
181 : int result;
182 : Atom actual_type;
183 : int actual_format;
184 : unsigned long nitems, bytes_after;
185 0 : char *data = 0;
186 :
187 : result = XGetWindowProperty (aDisplay,
188 : aWindowId,
189 : aChangedAtom,
190 : 0, /* long_offset */
191 : (65536 / sizeof (long)), /* long_length */
192 : True, /* atomic delete after */
193 : XA_STRING, /* req_type */
194 : &actual_type, /* actual_type return */
195 : &actual_format, /* actual_format_return */
196 : &nitems, /* nitems_return */
197 : &bytes_after, /* bytes_after_return */
198 0 : (unsigned char **)&data); /* prop_return
199 : (we only care
200 : about the first ) */
201 :
202 : // Failed to get property off the window?
203 0 : if (result != Success)
204 0 : return false;
205 :
206 : // Failed to get the data off the window or it was the wrong type?
207 0 : if (!data || !TO_LITTLE_ENDIAN32(*reinterpret_cast<int32_t*>(data)))
208 0 : return false;
209 :
210 : // cool, we got the property data.
211 0 : const char *response = HandleCommandLine(data, window, aEventTime);
212 :
213 : // put the property onto the window as the response
214 0 : XChangeProperty (aDisplay, aWindowId,
215 : sMozResponseAtom, XA_STRING,
216 : 8, PropModeReplace,
217 : (const unsigned char *)response,
218 0 : strlen (response));
219 0 : XFree(data);
220 0 : return true;
221 : }
222 :
223 0 : if (aChangedAtom == sMozResponseAtom) {
224 : // client accepted the response. party on wayne.
225 0 : return true;
226 : }
227 :
228 0 : else if (aChangedAtom == sMozLockAtom) {
229 : // someone locked the window
230 0 : return true;
231 : }
232 :
233 0 : return false;
234 : }
235 :
236 : const char*
237 0 : nsXRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow,
238 : uint32_t aTimestamp)
239 : {
240 : nsresult rv;
241 :
242 : nsCOMPtr<nsICommandLineRunner> cmdline
243 0 : (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
244 0 : if (NS_FAILED(rv))
245 0 : return "509 internal error";
246 :
247 : // the commandline property is constructed as an array of int32_t
248 : // followed by a series of null-terminated strings:
249 : //
250 : // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
251 : // (offset is from the beginning of the buffer)
252 :
253 0 : int32_t argc = TO_LITTLE_ENDIAN32(*reinterpret_cast<int32_t*>(aBuffer));
254 0 : char *wd = aBuffer + ((argc + 1) * sizeof(int32_t));
255 :
256 0 : nsCOMPtr<nsIFile> lf;
257 0 : rv = NS_NewNativeLocalFile(nsDependentCString(wd), true,
258 0 : getter_AddRefs(lf));
259 0 : if (NS_FAILED(rv))
260 0 : return "509 internal error";
261 :
262 0 : nsAutoCString desktopStartupID;
263 :
264 0 : char **argv = (char**) malloc(sizeof(char*) * argc);
265 0 : if (!argv) return "509 internal error";
266 :
267 0 : int32_t *offset = reinterpret_cast<int32_t*>(aBuffer) + 1;
268 :
269 0 : for (int i = 0; i < argc; ++i) {
270 0 : argv[i] = aBuffer + TO_LITTLE_ENDIAN32(offset[i]);
271 :
272 0 : if (i == 0) {
273 0 : nsDependentCString cmd(argv[0]);
274 : FindExtensionParameterInCommand("DESKTOP_STARTUP_ID",
275 : cmd, ' ',
276 0 : &desktopStartupID);
277 : }
278 : }
279 :
280 0 : rv = cmdline->Init(argc, argv, lf, nsICommandLine::STATE_REMOTE_AUTO);
281 :
282 0 : free (argv);
283 0 : if (NS_FAILED(rv)) {
284 0 : return "509 internal error";
285 : }
286 :
287 0 : if (aWindow)
288 0 : cmdline->SetWindowContext(aWindow);
289 :
290 0 : if (sRemoteImplementation)
291 0 : sRemoteImplementation->SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp);
292 :
293 0 : rv = cmdline->Run();
294 :
295 0 : if (NS_ERROR_ABORT == rv)
296 0 : return "500 command not parseable";
297 :
298 0 : if (NS_FAILED(rv))
299 0 : return "509 internal error";
300 :
301 0 : return "200 executed command";
302 : }
303 :
304 : void
305 0 : nsXRemoteService::EnsureAtoms(void)
306 : {
307 0 : if (sMozVersionAtom)
308 0 : return;
309 :
310 0 : XInternAtoms(mozilla::DefaultXDisplay(), const_cast<char**>(XAtomNames),
311 0 : ArrayLength(XAtomNames), False, XAtoms);
312 :
313 0 : int i = 0;
314 0 : sMozVersionAtom = XAtoms[i++];
315 0 : sMozLockAtom = XAtoms[i++];
316 0 : sMozResponseAtom = XAtoms[i++];
317 0 : sMozUserAtom = XAtoms[i++];
318 0 : sMozProfileAtom = XAtoms[i++];
319 0 : sMozProgramAtom = XAtoms[i++];
320 0 : sMozCommandLineAtom = XAtoms[i++];
321 : }
|