1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
6
7 #ifndef mozilla_CmdLineAndEnvUtils_h
8 #define mozilla_CmdLineAndEnvUtils_h
9
10 // NB: This code may be used outside of xul and thus must not depend on XPCOM
11
12 #if defined(MOZILLA_INTERNAL_API)
13 # include "prenv.h"
14 # include "prprf.h"
15 # include <string.h>
16 #elif defined(XP_WIN)
17 # include <stdlib.h>
18 #endif
19
20 #if defined(XP_WIN)
21 # include "mozilla/UniquePtr.h"
22 # include "mozilla/Vector.h"
23 # include "mozilla/WinHeaderOnlyUtils.h"
24
25 # include <wchar.h>
26 # include <windows.h>
27 #endif // defined(XP_WIN)
28
29 #include "mozilla/MemoryChecking.h"
30 #include "mozilla/TypedEnumBits.h"
31
32 #include <ctype.h>
33 #include <stdint.h>
34
35 #ifndef NS_NO_XPCOM
36 # include "nsIFile.h"
37 # include "mozilla/AlreadyAddRefed.h"
38 #endif
39
40 // Undo X11/X.h's definition of None
41 #undef None
42
43 namespace mozilla {
44
45 enum ArgResult {
46 ARG_NONE = 0,
47 ARG_FOUND = 1,
48 ARG_BAD = 2 // you wanted a param, but there isn't one
49 };
50
51 template <typename CharT>
RemoveArg(int & argc,CharT ** argv)52 inline void RemoveArg(int& argc, CharT** argv) {
53 do {
54 *argv = *(argv + 1);
55 ++argv;
56 } while (*argv);
57
58 --argc;
59 }
60
61 namespace internal {
62
63 template <typename FuncT, typename CharT>
strimatch(FuncT aToLowerFn,const CharT * lowerstr,const CharT * mixedstr)64 static inline bool strimatch(FuncT aToLowerFn, const CharT* lowerstr,
65 const CharT* mixedstr) {
66 while (*lowerstr) {
67 if (!*mixedstr) return false; // mixedstr is shorter
68 if (static_cast<CharT>(aToLowerFn(*mixedstr)) != *lowerstr)
69 return false; // no match
70
71 ++lowerstr;
72 ++mixedstr;
73 }
74
75 if (*mixedstr) return false; // lowerstr is shorter
76
77 return true;
78 }
79
80 } // namespace internal
81
strimatch(const char * lowerstr,const char * mixedstr)82 inline bool strimatch(const char* lowerstr, const char* mixedstr) {
83 return internal::strimatch(&tolower, lowerstr, mixedstr);
84 }
85
strimatch(const wchar_t * lowerstr,const wchar_t * mixedstr)86 inline bool strimatch(const wchar_t* lowerstr, const wchar_t* mixedstr) {
87 return internal::strimatch(&towlower, lowerstr, mixedstr);
88 }
89
90 enum class FlagLiteral { osint, safemode };
91
92 template <typename CharT, FlagLiteral Literal>
93 inline const CharT* GetLiteral();
94
95 #define DECLARE_FLAG_LITERAL(enum_name, literal) \
96 template <> \
97 inline const char* GetLiteral<char, FlagLiteral::enum_name>() { \
98 return literal; \
99 } \
100 \
101 template <> \
102 inline const wchar_t* GetLiteral<wchar_t, FlagLiteral::enum_name>() { \
103 return L##literal; \
104 }
105
106 DECLARE_FLAG_LITERAL(osint, "osint")
107 DECLARE_FLAG_LITERAL(safemode, "safe-mode")
108
109 enum class CheckArgFlag : uint32_t {
110 None = 0,
111 // (1 << 0) Used to be CheckOSInt
112 RemoveArg = (1 << 1) // Remove the argument from the argv array.
113 };
114
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CheckArgFlag)115 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CheckArgFlag)
116
117 /**
118 * Check for a commandline flag. If the flag takes a parameter, the
119 * parameter is returned in aParam. Flags may be in the form -arg or
120 * --arg (or /arg on win32).
121 *
122 * @param aArgc The argc value.
123 * @param aArgv The original argv.
124 * @param aArg the parameter to check. Must be lowercase.
125 * @param aParam if non-null, the -arg <data> will be stored in this pointer.
126 * This is *not* allocated, but rather a pointer to the argv data.
127 * @param aFlags Flags @see CheckArgFlag
128 */
129 template <typename CharT>
130 inline ArgResult CheckArg(int& aArgc, CharT** aArgv, const CharT* aArg,
131 const CharT** aParam = nullptr,
132 CheckArgFlag aFlags = CheckArgFlag::RemoveArg) {
133 MOZ_ASSERT(aArgv && aArg);
134
135 CharT** curarg = aArgv + 1; // skip argv[0]
136 ArgResult ar = ARG_NONE;
137
138 while (*curarg) {
139 CharT* arg = curarg[0];
140
141 if (arg[0] == '-'
142 #if defined(XP_WIN)
143 || *arg == '/'
144 #endif
145 ) {
146 ++arg;
147
148 if (*arg == '-') {
149 ++arg;
150 }
151
152 if (strimatch(aArg, arg)) {
153 if (aFlags & CheckArgFlag::RemoveArg) {
154 RemoveArg(aArgc, curarg);
155 } else {
156 ++curarg;
157 }
158
159 if (!aParam) {
160 ar = ARG_FOUND;
161 break;
162 }
163
164 if (*curarg) {
165 if (**curarg == '-'
166 #if defined(XP_WIN)
167 || **curarg == '/'
168 #endif
169 ) {
170 return ARG_BAD;
171 }
172
173 *aParam = *curarg;
174
175 if (aFlags & CheckArgFlag::RemoveArg) {
176 RemoveArg(aArgc, curarg);
177 }
178
179 ar = ARG_FOUND;
180 break;
181 }
182
183 return ARG_BAD;
184 }
185 }
186
187 ++curarg;
188 }
189
190 return ar;
191 }
192
193 template <typename CharT>
EnsureCommandlineSafe(int & aArgc,CharT ** aArgv,const CharT ** aAcceptableArgs)194 inline void EnsureCommandlineSafe(int& aArgc, CharT** aArgv,
195 const CharT** aAcceptableArgs) {
196 // We expect either no -osint, or the full commandline to be:
197 // app -osint
198 // followed by one of the arguments listed in aAcceptableArgs,
199 // followed by one parameter for that arg.
200 // If this varies, we abort to avoid abuse of other commandline handlers
201 // from apps that do a poor job escaping links they give to the OS.
202
203 const CharT* osintLit = GetLiteral<CharT, FlagLiteral::osint>();
204
205 if (CheckArg(aArgc, aArgv, osintLit, static_cast<const CharT**>(nullptr),
206 CheckArgFlag::None) == ARG_FOUND) {
207 // There should be 4 items left (app name + -osint + (acceptable arg) +
208 // param)
209 if (aArgc != 4) {
210 exit(127);
211 }
212
213 // The first should be osint.
214 CharT* arg = aArgv[1];
215 if (*arg != '-'
216 #ifdef XP_WIN
217 && *arg != '/'
218 #endif
219 ) {
220 exit(127);
221 }
222 ++arg;
223 if (*arg == '-') {
224 ++arg;
225 }
226 if (!strimatch(osintLit, arg)) {
227 exit(127);
228 }
229 // Strip it:
230 RemoveArg(aArgc, aArgv + 1);
231
232 // Now only an acceptable argument and a parameter for it should be left:
233 arg = aArgv[1];
234 if (*arg != '-'
235 #ifdef XP_WIN
236 && *arg != '/'
237 #endif
238 ) {
239 exit(127);
240 }
241 ++arg;
242 if (*arg == '-') {
243 ++arg;
244 }
245 bool haveAcceptableArg = false;
246 const CharT** acceptableArg = aAcceptableArgs;
247 while (*acceptableArg) {
248 if (strimatch(*acceptableArg, arg)) {
249 haveAcceptableArg = true;
250 break;
251 }
252 acceptableArg++;
253 }
254 if (!haveAcceptableArg) {
255 exit(127);
256 }
257 // The param that is passed afterwards shouldn't be another switch:
258 arg = aArgv[2];
259 if (*arg == '-'
260 #ifdef XP_WIN
261 || *arg == '/'
262 #endif
263 ) {
264 exit(127);
265 }
266 }
267 // Either no osint, so nothing to do, or we ensured nothing nefarious was
268 // passed.
269 }
270
271 #if defined(XP_WIN)
272
273 namespace internal {
274
275 /**
276 * Get the length that the string will take and takes into account the
277 * additional length if the string needs to be quoted and if characters need to
278 * be escaped.
279 */
ArgStrLen(const wchar_t * s)280 inline int ArgStrLen(const wchar_t* s) {
281 int backslashes = 0;
282 int i = wcslen(s);
283 bool hasDoubleQuote = wcschr(s, L'"') != nullptr;
284 // Only add doublequotes if the string contains a space or a tab
285 bool addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
286
287 if (addDoubleQuotes) {
288 i += 2; // initial and final duoblequote
289 }
290
291 if (hasDoubleQuote) {
292 while (*s) {
293 if (*s == '\\') {
294 ++backslashes;
295 } else {
296 if (*s == '"') {
297 // Escape the doublequote and all backslashes preceding the
298 // doublequote
299 i += backslashes + 1;
300 }
301
302 backslashes = 0;
303 }
304
305 ++s;
306 }
307 }
308
309 return i;
310 }
311
312 /**
313 * Copy string "s" to string "d", quoting the argument as appropriate and
314 * escaping doublequotes along with any backslashes that immediately precede
315 * doublequotes.
316 * The CRT parses this to retrieve the original argc/argv that we meant,
317 * see STDARGV.C in the MSVC CRT sources.
318 *
319 * @return the end of the string
320 */
ArgToString(wchar_t * d,const wchar_t * s)321 inline wchar_t* ArgToString(wchar_t* d, const wchar_t* s) {
322 int backslashes = 0;
323 bool hasDoubleQuote = wcschr(s, L'"') != nullptr;
324 // Only add doublequotes if the string contains a space or a tab
325 bool addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
326
327 if (addDoubleQuotes) {
328 *d = '"'; // initial doublequote
329 ++d;
330 }
331
332 if (hasDoubleQuote) {
333 int i;
334 while (*s) {
335 if (*s == '\\') {
336 ++backslashes;
337 } else {
338 if (*s == '"') {
339 // Escape the doublequote and all backslashes preceding the
340 // doublequote
341 for (i = 0; i <= backslashes; ++i) {
342 *d = '\\';
343 ++d;
344 }
345 }
346
347 backslashes = 0;
348 }
349
350 *d = *s;
351 ++d;
352 ++s;
353 }
354 } else {
355 wcscpy(d, s);
356 d += wcslen(s);
357 }
358
359 if (addDoubleQuotes) {
360 *d = '"'; // final doublequote
361 ++d;
362 }
363
364 return d;
365 }
366
367 } // namespace internal
368
369 /**
370 * Creates a command line from a list of arguments.
371 *
372 * @param argc Number of elements in |argv|
373 * @param argv Array of arguments
374 * @param aArgcExtra Number of elements in |aArgvExtra|
375 * @param aArgvExtra Optional array of arguments to be appended to the resulting
376 * command line after those provided by |argv|.
377 */
378 inline UniquePtr<wchar_t[]> MakeCommandLine(
379 int argc, const wchar_t* const* argv, int aArgcExtra = 0,
380 const wchar_t* const* aArgvExtra = nullptr) {
381 int i;
382 int len = 0;
383
384 // The + 1 for each argument reserves space for either a ' ' or the null
385 // terminator, depending on the position of the argument.
386 for (i = 0; i < argc; ++i) {
387 len += internal::ArgStrLen(argv[i]) + 1;
388 }
389
390 for (i = 0; i < aArgcExtra; ++i) {
391 len += internal::ArgStrLen(aArgvExtra[i]) + 1;
392 }
393
394 // Protect against callers that pass 0 arguments
395 if (len == 0) {
396 len = 1;
397 }
398
399 auto s = MakeUnique<wchar_t[]>(len);
400 if (!s) {
401 return s;
402 }
403
404 int totalArgc = argc + aArgcExtra;
405
406 wchar_t* c = s.get();
407 for (i = 0; i < argc; ++i) {
408 c = internal::ArgToString(c, argv[i]);
409 if (i + 1 != totalArgc) {
410 *c = ' ';
411 ++c;
412 }
413 }
414
415 for (i = 0; i < aArgcExtra; ++i) {
416 c = internal::ArgToString(c, aArgvExtra[i]);
417 if (i + 1 != aArgcExtra) {
418 *c = ' ';
419 ++c;
420 }
421 }
422
423 *c = '\0';
424
425 return s;
426 }
427
SetArgv0ToFullBinaryPath(wchar_t * aArgv[])428 inline bool SetArgv0ToFullBinaryPath(wchar_t* aArgv[]) {
429 if (!aArgv) {
430 return false;
431 }
432
433 UniquePtr<wchar_t[]> newArgv_0(GetFullBinaryPath());
434 if (!newArgv_0) {
435 return false;
436 }
437
438 // We intentionally leak newArgv_0 into argv[0]
439 aArgv[0] = newArgv_0.release();
440 MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(aArgv[0]);
441 return true;
442 }
443
444 #endif // defined(XP_WIN)
445
446 // SaveToEnv and EnvHasValue are only available on Windows or when
447 // MOZILLA_INTERNAL_API is defined
448 #if defined(MOZILLA_INTERNAL_API) || defined(XP_WIN)
449
450 // Save literal putenv string to environment variable.
SaveToEnv(const char * aEnvString)451 MOZ_NEVER_INLINE inline void SaveToEnv(const char* aEnvString) {
452 # if defined(MOZILLA_INTERNAL_API)
453 char* expr = strdup(aEnvString);
454 if (expr) {
455 PR_SetEnv(expr);
456 }
457
458 // We intentionally leak |expr| here since it is required by PR_SetEnv.
459 MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(expr);
460 # elif defined(XP_WIN)
461 // This is the same as the NSPR implementation
462 // (Note that we don't need to do a strdup for this case; the CRT makes a
463 // copy)
464 _putenv(aEnvString);
465 # endif
466 }
467
EnvHasValue(const char * aVarName)468 inline bool EnvHasValue(const char* aVarName) {
469 # if defined(MOZILLA_INTERNAL_API)
470 const char* val = PR_GetEnv(aVarName);
471 return val && *val;
472 # elif defined(XP_WIN)
473 // This is the same as the NSPR implementation
474 const char* val = getenv(aVarName);
475 return val && *val;
476 # endif
477 }
478
479 #endif // end windows/internal_api-only definitions
480
481 #ifndef NS_NO_XPCOM
482 already_AddRefed<nsIFile> GetFileFromEnv(const char* name);
483 #endif
484
485 } // namespace mozilla
486
487 #endif // mozilla_CmdLineAndEnvUtils_h
488