mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
3c6a2a135e
A fix for Bug 1588975 replaced the call to `LaunchWithIProcess` in `nsMIMEInfoWin::LaunchWithFile` with the call to `ShellExecuteWithIFile` to use `mozilla::ShellExecuteByExplorer`. This caused a regression that if a filepath contains whitespaces, we pass it to a target application without quoting it while the old codepath `ShellExecuteWithIFile` quoted a path accordingly in `assembleCmdLine`. A proposed fix is to quote a path in the same way as `assembleCmdLine` does. This patch also moves `assembleCmdLine` to an exported header so that we can add a unittest to cover both of `assembleCmdLine` and a new function `assembleSingleArgument. It would be better to refactor these functions in the future because many lines are duplicated. Differential Revision: https://phabricator.services.mozilla.com/D56646 --HG-- extra : moz-landing-system : lando
229 lines
5.8 KiB
C++
229 lines
5.8 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef mozilla_AssembleCmdLine_h
|
|
#define mozilla_AssembleCmdLine_h
|
|
|
|
#if defined(XP_WIN)
|
|
|
|
# include "mozilla/UniquePtr.h"
|
|
|
|
# include <stdlib.h>
|
|
# include <windows.h>
|
|
|
|
# ifdef MOZILLA_INTERNAL_API
|
|
# include "nsString.h"
|
|
# endif // MOZILLA_INTERNAL_API
|
|
|
|
namespace mozilla {
|
|
|
|
// Out param `aWideCmdLine` must be free()d by the caller.
|
|
inline int assembleCmdLine(const char* const* aArgv, wchar_t** aWideCmdLine,
|
|
UINT aCodePage) {
|
|
const char* const* arg;
|
|
char* p;
|
|
const char* q;
|
|
char* cmdLine;
|
|
int cmdLineSize;
|
|
int numBackslashes;
|
|
int i;
|
|
int argNeedQuotes;
|
|
|
|
/*
|
|
* Find out how large the command line buffer should be.
|
|
*/
|
|
cmdLineSize = 0;
|
|
for (arg = aArgv; *arg; ++arg) {
|
|
/*
|
|
* \ and " need to be escaped by a \. In the worst case,
|
|
* every character is a \ or ", so the string of length
|
|
* may double. If we quote an argument, that needs two ".
|
|
* Finally, we need a space between arguments, and
|
|
* a null byte at the end of command line.
|
|
*/
|
|
cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */
|
|
+ 2 /* we quote every argument */
|
|
+ 1; /* space in between, or final null */
|
|
}
|
|
p = cmdLine = (char*)malloc(cmdLineSize * sizeof(char));
|
|
if (!p) {
|
|
return -1;
|
|
}
|
|
|
|
for (arg = aArgv; *arg; ++arg) {
|
|
/* Add a space to separates the arguments */
|
|
if (arg != aArgv) {
|
|
*p++ = ' ';
|
|
}
|
|
q = *arg;
|
|
numBackslashes = 0;
|
|
argNeedQuotes = 0;
|
|
|
|
/* If the argument contains white space, it needs to be quoted. */
|
|
if (strpbrk(*arg, " \f\n\r\t\v")) {
|
|
argNeedQuotes = 1;
|
|
}
|
|
|
|
if (argNeedQuotes) {
|
|
*p++ = '"';
|
|
}
|
|
while (*q) {
|
|
if (*q == '\\') {
|
|
numBackslashes++;
|
|
q++;
|
|
} else if (*q == '"') {
|
|
if (numBackslashes) {
|
|
/*
|
|
* Double the backslashes since they are followed
|
|
* by a quote
|
|
*/
|
|
for (i = 0; i < 2 * numBackslashes; i++) {
|
|
*p++ = '\\';
|
|
}
|
|
numBackslashes = 0;
|
|
}
|
|
/* To escape the quote */
|
|
*p++ = '\\';
|
|
*p++ = *q++;
|
|
} else {
|
|
if (numBackslashes) {
|
|
/*
|
|
* Backslashes are not followed by a quote, so
|
|
* don't need to double the backslashes.
|
|
*/
|
|
for (i = 0; i < numBackslashes; i++) {
|
|
*p++ = '\\';
|
|
}
|
|
numBackslashes = 0;
|
|
}
|
|
*p++ = *q++;
|
|
}
|
|
}
|
|
|
|
/* Now we are at the end of this argument */
|
|
if (numBackslashes) {
|
|
/*
|
|
* Double the backslashes if we have a quote string
|
|
* delimiter at the end.
|
|
*/
|
|
if (argNeedQuotes) {
|
|
numBackslashes *= 2;
|
|
}
|
|
for (i = 0; i < numBackslashes; i++) {
|
|
*p++ = '\\';
|
|
}
|
|
}
|
|
if (argNeedQuotes) {
|
|
*p++ = '"';
|
|
}
|
|
}
|
|
|
|
*p = '\0';
|
|
int numChars = MultiByteToWideChar(aCodePage, 0, cmdLine, -1, nullptr, 0);
|
|
*aWideCmdLine = (wchar_t*)malloc(numChars * sizeof(wchar_t));
|
|
MultiByteToWideChar(aCodePage, 0, cmdLine, -1, *aWideCmdLine, numChars);
|
|
free(cmdLine);
|
|
return 0;
|
|
}
|
|
|
|
# ifdef MOZILLA_INTERNAL_API
|
|
inline UniquePtr<wchar_t[]> assembleSingleArgument(const nsString& aArg) {
|
|
static_assert(sizeof(char16_t) == sizeof(wchar_t),
|
|
"char16_t and wchar_t sizes differ");
|
|
|
|
/*
|
|
* \ and " need to be escaped by a \. In the worst case,
|
|
* every character is a \ or ", so the string of length
|
|
* may double. If we quote an argument, that needs two ".
|
|
* Finally, we need a space between arguments, and
|
|
* a null byte at the end of command line.
|
|
*/
|
|
int cmdLineSize = 2 * aArg.Length() /* \ and " need to be escaped */
|
|
+ 2 /* we quote every argument */
|
|
+ 1; /* space in between, or final null */
|
|
|
|
auto assembledArg = MakeUnique<wchar_t[]>(cmdLineSize);
|
|
if (!assembledArg) {
|
|
return nullptr;
|
|
}
|
|
|
|
/* If the argument contains white space, it needs to be quoted. */
|
|
int argNeedQuotes = 0;
|
|
if (aArg.FindCharInSet(u" \f\n\r\t\v") != kNotFound) {
|
|
argNeedQuotes = 1;
|
|
}
|
|
|
|
wchar_t* p = assembledArg.get();
|
|
|
|
if (argNeedQuotes) {
|
|
*p++ = '"';
|
|
}
|
|
|
|
const char16_t* q = aArg.get();
|
|
|
|
int numBackslashes = 0;
|
|
while (*q) {
|
|
if (*q == '\\') {
|
|
numBackslashes++;
|
|
q++;
|
|
} else if (*q == '"') {
|
|
if (numBackslashes) {
|
|
/*
|
|
* Double the backslashes since they are followed
|
|
* by a quote
|
|
*/
|
|
for (int i = 0; i < 2 * numBackslashes; i++) {
|
|
*p++ = '\\';
|
|
}
|
|
numBackslashes = 0;
|
|
}
|
|
/* To escape the quote */
|
|
*p++ = '\\';
|
|
*p++ = *q++;
|
|
} else {
|
|
if (numBackslashes) {
|
|
/*
|
|
* Backslashes are not followed by a quote, so
|
|
* don't need to double the backslashes.
|
|
*/
|
|
for (int i = 0; i < numBackslashes; i++) {
|
|
*p++ = '\\';
|
|
}
|
|
numBackslashes = 0;
|
|
}
|
|
*p++ = *q++;
|
|
}
|
|
}
|
|
|
|
/* Now we are at the end of this argument */
|
|
if (numBackslashes) {
|
|
/*
|
|
* Double the backslashes if we have a quote string
|
|
* delimiter at the end.
|
|
*/
|
|
if (argNeedQuotes) {
|
|
numBackslashes *= 2;
|
|
}
|
|
for (int i = 0; i < numBackslashes; i++) {
|
|
*p++ = '\\';
|
|
}
|
|
}
|
|
if (argNeedQuotes) {
|
|
*p++ = '"';
|
|
}
|
|
|
|
*p = '\0';
|
|
|
|
return assembledArg;
|
|
}
|
|
# endif // MOZILLA_INTERNAL_API
|
|
|
|
} // namespace mozilla
|
|
|
|
#endif // defined(XP_WIN)
|
|
|
|
#endif // mozilla_AssembleCmdLine_h
|