1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:expandtab:shiftwidth=2:tabstop=8:
3 : */
4 : /* ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is mozilla.org code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Christopher Blizzard.
21 : * Portions created by the Initial Developer are Copyright (C) 2001
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Christopher Blizzard <blizzard@mozilla.org>
26 : * Benjamin Smedberg <benjamin@smedbergs.us>
27 : * Miika Jarvinen <mjarvin@gmail.com>
28 : *
29 : * Alternatively, the contents of this file may be used under the terms of
30 : * either the GNU General Public License Version 2 or later (the "GPL"), or
31 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 : * in which case the provisions of the GPL or the LGPL are applicable instead
33 : * of those above. If you wish to allow use of your version of this file only
34 : * under the terms of either the GPL or the LGPL, and not to allow others to
35 : * use your version of this file under the terms of the MPL, indicate your
36 : * decision by deleting the provisions above and replace them with the notice
37 : * and other provisions required by the GPL or the LGPL. If you do not delete
38 : * the provisions above, a recipient may use your version of this file under
39 : * the terms of any one of the MPL, the GPL or the LGPL.
40 : *
41 : * ***** END LICENSE BLOCK ***** */
42 :
43 : #include "mozilla/Util.h"
44 :
45 : #include "nsXRemoteService.h"
46 : #include "nsIObserverService.h"
47 : #include "nsCOMPtr.h"
48 : #include "nsIServiceManager.h"
49 : #include "nsICommandLineRunner.h"
50 : #include "nsICommandLine.h"
51 :
52 : #include "nsIBaseWindow.h"
53 : #include "nsIDocShell.h"
54 : #include "nsILocalFile.h"
55 : #include "nsIServiceManager.h"
56 : #include "nsIWeakReference.h"
57 : #include "nsIWidget.h"
58 : #include "nsIAppShellService.h"
59 : #include "nsAppShellCID.h"
60 : #include "nsPIDOMWindow.h"
61 : #include "mozilla/X11Util.h"
62 :
63 : #include "nsCOMPtr.h"
64 : #include "nsString.h"
65 : #include "prprf.h"
66 : #include "prenv.h"
67 : #include "nsCRT.h"
68 :
69 : #include "nsXULAppAPI.h"
70 :
71 : #include <X11/Xlib.h>
72 : #include <X11/Xatom.h>
73 :
74 : using namespace mozilla;
75 :
76 : #define MOZILLA_VERSION_PROP "_MOZILLA_VERSION"
77 : #define MOZILLA_LOCK_PROP "_MOZILLA_LOCK"
78 : #define MOZILLA_COMMAND_PROP "_MOZILLA_COMMAND"
79 : #define MOZILLA_RESPONSE_PROP "_MOZILLA_RESPONSE"
80 : #define MOZILLA_USER_PROP "_MOZILLA_USER"
81 : #define MOZILLA_PROFILE_PROP "_MOZILLA_PROFILE"
82 : #define MOZILLA_PROGRAM_PROP "_MOZILLA_PROGRAM"
83 : #define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
84 :
85 : const unsigned char kRemoteVersion[] = "5.1";
86 :
87 : #ifdef IS_BIG_ENDIAN
88 : #define TO_LITTLE_ENDIAN32(x) \
89 : ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
90 : (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
91 : #else
92 : #define TO_LITTLE_ENDIAN32(x) (x)
93 : #endif
94 :
95 : // Minimize the roundtrips to the X server by getting all the atoms at once
96 : static char *XAtomNames[] = {
97 : MOZILLA_VERSION_PROP,
98 : MOZILLA_LOCK_PROP,
99 : MOZILLA_COMMAND_PROP,
100 : MOZILLA_RESPONSE_PROP,
101 : MOZILLA_USER_PROP,
102 : MOZILLA_PROFILE_PROP,
103 : MOZILLA_PROGRAM_PROP,
104 : MOZILLA_COMMANDLINE_PROP
105 : };
106 : static Atom XAtoms[NS_ARRAY_LENGTH(XAtomNames)];
107 :
108 : Atom nsXRemoteService::sMozVersionAtom;
109 : Atom nsXRemoteService::sMozLockAtom;
110 : Atom nsXRemoteService::sMozCommandAtom;
111 : Atom nsXRemoteService::sMozResponseAtom;
112 : Atom nsXRemoteService::sMozUserAtom;
113 : Atom nsXRemoteService::sMozProfileAtom;
114 : Atom nsXRemoteService::sMozProgramAtom;
115 : Atom nsXRemoteService::sMozCommandLineAtom;
116 :
117 : nsXRemoteService * nsXRemoteService::sRemoteImplementation = 0;
118 :
119 :
120 : static bool
121 0 : FindExtensionParameterInCommand(const char* aParameterName,
122 : const nsACString& aCommand,
123 : char aSeparator,
124 : nsACString* aValue)
125 : {
126 0 : nsCAutoString searchFor;
127 0 : searchFor.Append(aSeparator);
128 0 : searchFor.Append(aParameterName);
129 0 : searchFor.Append('=');
130 :
131 0 : nsACString::const_iterator start, end;
132 0 : aCommand.BeginReading(start);
133 0 : aCommand.EndReading(end);
134 0 : if (!FindInReadable(searchFor, start, end))
135 0 : return false;
136 :
137 0 : nsACString::const_iterator charStart, charEnd;
138 0 : charStart = end;
139 0 : aCommand.EndReading(charEnd);
140 0 : nsACString::const_iterator idStart = charStart, idEnd;
141 0 : if (FindCharInReadable(aSeparator, charStart, charEnd)) {
142 0 : idEnd = charStart;
143 : } else {
144 0 : idEnd = charEnd;
145 : }
146 0 : *aValue = nsDependentCSubstring(idStart, idEnd);
147 0 : return true;
148 : }
149 :
150 :
151 0 : nsXRemoteService::nsXRemoteService()
152 : {
153 0 : }
154 :
155 : void
156 0 : nsXRemoteService::XRemoteBaseStartup(const char *aAppName, const char *aProfileName)
157 : {
158 0 : EnsureAtoms();
159 :
160 0 : mAppName = aAppName;
161 0 : ToLowerCase(mAppName);
162 :
163 0 : mProfileName = aProfileName;
164 :
165 0 : nsCOMPtr<nsIObserverService> obs(do_GetService("@mozilla.org/observer-service;1"));
166 0 : if (obs) {
167 0 : obs->AddObserver(this, "xpcom-shutdown", false);
168 0 : obs->AddObserver(this, "quit-application", false);
169 : }
170 0 : }
171 :
172 : void
173 0 : nsXRemoteService::HandleCommandsFor(Window aWindowId)
174 : {
175 : // set our version
176 : XChangeProperty(mozilla::DefaultXDisplay(), aWindowId, sMozVersionAtom, XA_STRING,
177 0 : 8, PropModeReplace, kRemoteVersion, sizeof(kRemoteVersion) - 1);
178 :
179 : // get our username
180 : unsigned char *logname;
181 0 : logname = (unsigned char*) PR_GetEnv("LOGNAME");
182 0 : if (logname) {
183 : // set the property on the window if it's available
184 : XChangeProperty(mozilla::DefaultXDisplay(), aWindowId, sMozUserAtom, XA_STRING,
185 0 : 8, PropModeReplace, logname, strlen((char*) logname));
186 : }
187 :
188 : XChangeProperty(mozilla::DefaultXDisplay(), aWindowId, sMozProgramAtom, XA_STRING,
189 0 : 8, PropModeReplace, (unsigned char*) mAppName.get(), mAppName.Length());
190 :
191 0 : if (!mProfileName.IsEmpty()) {
192 : XChangeProperty(mozilla::DefaultXDisplay(),
193 : aWindowId, sMozProfileAtom, XA_STRING,
194 : 8, PropModeReplace,
195 0 : (unsigned char*) mProfileName.get(), mProfileName.Length());
196 : }
197 :
198 0 : }
199 :
200 : NS_IMETHODIMP
201 0 : nsXRemoteService::Observe(nsISupports* aSubject,
202 : const char *aTopic,
203 : const PRUnichar *aData)
204 : {
205 : // This can be xpcom-shutdown or quit-application, but it's the same either
206 : // way.
207 0 : Shutdown();
208 0 : return NS_OK;
209 : }
210 :
211 : bool
212 0 : nsXRemoteService::HandleNewProperty(XID aWindowId, Display* aDisplay,
213 : Time aEventTime,
214 : Atom aChangedAtom,
215 : nsIWeakReference* aDomWindow)
216 : {
217 :
218 0 : nsCOMPtr<nsIDOMWindow> window (do_QueryReferent(aDomWindow));
219 :
220 0 : if (aChangedAtom == sMozCommandAtom || aChangedAtom == sMozCommandLineAtom) {
221 : // We got a new command atom.
222 : int result;
223 : Atom actual_type;
224 : int actual_format;
225 : unsigned long nitems, bytes_after;
226 0 : char *data = 0;
227 :
228 : result = XGetWindowProperty (aDisplay,
229 : aWindowId,
230 : aChangedAtom,
231 : 0, /* long_offset */
232 : (65536 / sizeof (long)), /* long_length */
233 : True, /* atomic delete after */
234 : XA_STRING, /* req_type */
235 : &actual_type, /* actual_type return */
236 : &actual_format, /* actual_format_return */
237 : &nitems, /* nitems_return */
238 : &bytes_after, /* bytes_after_return */
239 0 : (unsigned char **)&data); /* prop_return
240 : (we only care
241 : about the first ) */
242 :
243 : // Failed to get property off the window?
244 0 : if (result != Success)
245 0 : return false;
246 :
247 : // Failed to get the data off the window or it was the wrong type?
248 0 : if (!data || !TO_LITTLE_ENDIAN32(*reinterpret_cast<PRInt32*>(data)))
249 0 : return false;
250 :
251 : // cool, we got the property data.
252 0 : const char *response = NULL;
253 0 : if (aChangedAtom == sMozCommandAtom)
254 0 : response = HandleCommand(data, window, aEventTime);
255 0 : else if (aChangedAtom == sMozCommandLineAtom)
256 0 : response = HandleCommandLine(data, window, aEventTime);
257 :
258 : // put the property onto the window as the response
259 : XChangeProperty (aDisplay, aWindowId,
260 : sMozResponseAtom, XA_STRING,
261 : 8, PropModeReplace,
262 : (const unsigned char *)response,
263 0 : strlen (response));
264 0 : XFree(data);
265 0 : return true;
266 : }
267 :
268 0 : else if (aChangedAtom == sMozResponseAtom) {
269 : // client accepted the response. party on wayne.
270 0 : return true;
271 : }
272 :
273 0 : else if (aChangedAtom == sMozLockAtom) {
274 : // someone locked the window
275 0 : return true;
276 : }
277 :
278 0 : return false;
279 : }
280 :
281 : const char*
282 0 : nsXRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow,
283 : PRUint32 aTimestamp)
284 : {
285 : nsresult rv;
286 :
287 : nsCOMPtr<nsICommandLineRunner> cmdline
288 0 : (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
289 0 : if (NS_FAILED(rv))
290 0 : return "509 internal error";
291 :
292 : // 1) Make sure that it looks remotely valid with parens
293 : // 2) Treat ping() immediately and specially
294 :
295 0 : nsCAutoString command(aCommand);
296 : PRInt32 p1, p2;
297 0 : p1 = command.FindChar('(');
298 0 : p2 = command.FindChar(')');
299 :
300 0 : if (p1 == kNotFound || p2 == kNotFound || p1 == 0 || p2 < p1) {
301 0 : return "500 command not parseable";
302 : }
303 :
304 0 : command.Truncate(p1);
305 0 : command.Trim(" ", true, true);
306 0 : ToLowerCase(command);
307 :
308 0 : if (!command.EqualsLiteral("ping")) {
309 0 : nsCAutoString desktopStartupID;
310 0 : nsDependentCString cmd(aCommand);
311 : FindExtensionParameterInCommand("DESKTOP_STARTUP_ID",
312 : cmd, '\n',
313 0 : &desktopStartupID);
314 :
315 0 : char* argv[3] = {"dummyappname", "-remote", aCommand};
316 0 : rv = cmdline->Init(3, argv, nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
317 0 : if (NS_FAILED(rv))
318 0 : return "509 internal error";
319 :
320 0 : if (aWindow)
321 0 : cmdline->SetWindowContext(aWindow);
322 :
323 0 : if (sRemoteImplementation)
324 0 : sRemoteImplementation->SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp);
325 :
326 0 : rv = cmdline->Run();
327 0 : if (NS_ERROR_ABORT == rv)
328 0 : return "500 command not parseable";
329 0 : if (NS_FAILED(rv))
330 0 : return "509 internal error";
331 : }
332 :
333 0 : return "200 executed command";
334 : }
335 :
336 : const char*
337 0 : nsXRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow,
338 : PRUint32 aTimestamp)
339 : {
340 : nsresult rv;
341 :
342 : nsCOMPtr<nsICommandLineRunner> cmdline
343 0 : (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
344 0 : if (NS_FAILED(rv))
345 0 : return "509 internal error";
346 :
347 : // the commandline property is constructed as an array of PRInt32
348 : // followed by a series of null-terminated strings:
349 : //
350 : // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
351 : // (offset is from the beginning of the buffer)
352 :
353 0 : PRInt32 argc = TO_LITTLE_ENDIAN32(*reinterpret_cast<PRInt32*>(aBuffer));
354 0 : char *wd = aBuffer + ((argc + 1) * sizeof(PRInt32));
355 :
356 0 : nsCOMPtr<nsILocalFile> lf;
357 0 : rv = NS_NewNativeLocalFile(nsDependentCString(wd), true,
358 0 : getter_AddRefs(lf));
359 0 : if (NS_FAILED(rv))
360 0 : return "509 internal error";
361 :
362 0 : nsCAutoString desktopStartupID;
363 :
364 0 : char **argv = (char**) malloc(sizeof(char*) * argc);
365 0 : if (!argv) return "509 internal error";
366 :
367 0 : PRInt32 *offset = reinterpret_cast<PRInt32*>(aBuffer) + 1;
368 :
369 0 : for (int i = 0; i < argc; ++i) {
370 0 : argv[i] = aBuffer + TO_LITTLE_ENDIAN32(offset[i]);
371 :
372 0 : if (i == 0) {
373 0 : nsDependentCString cmd(argv[0]);
374 : FindExtensionParameterInCommand("DESKTOP_STARTUP_ID",
375 : cmd, ' ',
376 0 : &desktopStartupID);
377 : }
378 : }
379 :
380 0 : rv = cmdline->Init(argc, argv, lf, nsICommandLine::STATE_REMOTE_AUTO);
381 :
382 0 : free (argv);
383 0 : if (NS_FAILED(rv)) {
384 0 : return "509 internal error";
385 : }
386 :
387 0 : if (aWindow)
388 0 : cmdline->SetWindowContext(aWindow);
389 :
390 0 : if (sRemoteImplementation)
391 0 : sRemoteImplementation->SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp);
392 :
393 0 : rv = cmdline->Run();
394 :
395 0 : if (NS_ERROR_ABORT == rv)
396 0 : return "500 command not parseable";
397 :
398 0 : if (NS_FAILED(rv))
399 0 : return "509 internal error";
400 :
401 0 : return "200 executed command";
402 : }
403 :
404 : void
405 0 : nsXRemoteService::EnsureAtoms(void)
406 : {
407 0 : if (sMozVersionAtom)
408 0 : return;
409 :
410 0 : XInternAtoms(mozilla::DefaultXDisplay(), XAtomNames, ArrayLength(XAtomNames),
411 0 : False, XAtoms);
412 :
413 0 : int i = 0;
414 0 : sMozVersionAtom = XAtoms[i++];
415 0 : sMozLockAtom = XAtoms[i++];
416 0 : sMozCommandAtom = XAtoms[i++];
417 0 : sMozResponseAtom = XAtoms[i++];
418 0 : sMozUserAtom = XAtoms[i++];
419 0 : sMozProfileAtom = XAtoms[i++];
420 0 : sMozProgramAtom = XAtoms[i++];
421 0 : sMozCommandLineAtom = XAtoms[i++];
422 : }
|