Skip to content

Commit

Permalink
Bug 1279240 - move path parsing of commandline handlers for mimetypes…
Browse files Browse the repository at this point in the history
…/protocols to nsILocalFileWin, r=froydnj

MozReview-Commit-ID: 4CENm3iqGUH
  • Loading branch information
gijsk committed Sep 30, 2016
1 parent 9880a36 commit 50df3b1
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 133 deletions.
7 changes: 3 additions & 4 deletions uriloader/exthandler/win/nsMIMEInfoWin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#include "nsArrayEnumerator.h"
#include "nsCOMArray.h"
#include "nsIFile.h"
#include "nsLocalFile.h"
#include "nsMIMEInfoWin.h"
#include "nsNetUtil.h"
#include <windows.h>
Expand All @@ -18,7 +18,6 @@
#include "windows.h"
#include "nsIWindowsRegKey.h"
#include "nsIProcess.h"
#include "nsOSHelperAppService.h"
#include "nsUnicharUtils.h"
#include "nsITextToSubURI.h"
#include "nsVariant.h"
Expand Down Expand Up @@ -348,7 +347,7 @@ bool nsMIMEInfoWin::GetAppsVerbCommandHandler(const nsAString& appExeName,
appFilesystemCommand))) {

// Expand environment vars, clean up any misc.
if (!nsOSHelperAppService::CleanupCmdHandlerPath(appFilesystemCommand))
if (!nsLocalFile::CleanupCmdHandlerPath(appFilesystemCommand))
return false;

applicationPath = appFilesystemCommand;
Expand Down Expand Up @@ -493,7 +492,7 @@ bool nsMIMEInfoWin::GetProgIDVerbCommandHandler(const nsAString& appProgIDName,
if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(), appFilesystemCommand))) {

// Expand environment vars, clean up any misc.
if (!nsOSHelperAppService::CleanupCmdHandlerPath(appFilesystemCommand))
if (!nsLocalFile::CleanupCmdHandlerPath(appFilesystemCommand))
return false;

applicationPath = appFilesystemCommand;
Expand Down
134 changes: 8 additions & 126 deletions uriloader/exthandler/win/nsOSHelperAppService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
#include "nsIMIMEInfo.h"
#include "nsMIMEInfoWin.h"
#include "nsMimeTypes.h"
#include "nsILocalFileWin.h"
#include "nsIProcess.h"
#include "plstr.h"
#include "nsAutoPtr.h"
#include "nsNativeCharsetUtils.h"
#include "nsLocalFile.h"
#include "nsIWindowsRegKey.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/WindowsVersion.h"
Expand Down Expand Up @@ -286,114 +286,6 @@ nsOSHelperAppService::typeFromExtEquals(const char16_t* aExt, const char *aType)
return eq;
}

// Strip a handler command string of its quotes and parameters.
static void CleanupHandlerPath(nsString& aPath)
{
// Example command strings passed into this routine:

// 1) C:\Program Files\Company\some.exe -foo -bar
// 2) C:\Program Files\Company\some.dll
// 3) C:\Windows\some.dll,-foo -bar
// 4) C:\Windows\some.cpl,-foo -bar

int32_t lastCommaPos = aPath.RFindChar(',');
if (lastCommaPos != kNotFound)
aPath.Truncate(lastCommaPos);

aPath.Append(' ');

// case insensitive
uint32_t index = aPath.Find(".exe ", true);
if (index == kNotFound)
index = aPath.Find(".dll ", true);
if (index == kNotFound)
index = aPath.Find(".cpl ", true);

if (index != kNotFound)
aPath.Truncate(index + 4);
aPath.Trim(" ", true, true);
}

// Strip the windows host process bootstrap executable rundll32.exe
// from a handler's command string if it exists.
static void StripRundll32(nsString& aCommandString)
{
// Example rundll formats:
// C:\Windows\System32\rundll32.exe "path to dll"
// rundll32.exe "path to dll"
// C:\Windows\System32\rundll32.exe "path to dll", var var
// rundll32.exe "path to dll", var var

NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe ");
NS_NAMED_LITERAL_STRING(rundllSegmentShort, "rundll32 ");

// case insensitive
int32_t strLen = rundllSegment.Length();
int32_t index = aCommandString.Find(rundllSegment, true);
if (index == kNotFound) {
strLen = rundllSegmentShort.Length();
index = aCommandString.Find(rundllSegmentShort, true);
}

if (index != kNotFound) {
uint32_t rundllSegmentLength = index + strLen;
aCommandString.Cut(0, rundllSegmentLength);
}
}

// Returns the fully qualified path to an application handler based on
// a parameterized command string. Note this routine should not be used
// to launch the associated application as it strips parameters and
// rundll.exe from the string. Designed for retrieving display information
// on a particular handler.
/* static */ bool nsOSHelperAppService::CleanupCmdHandlerPath(nsAString& aCommandHandler)
{
nsAutoString handlerCommand(aCommandHandler);

// Straight command path:
//
// %SystemRoot%\system32\NOTEPAD.EXE var
// "C:\Program Files\iTunes\iTunes.exe" var var
// C:\Program Files\iTunes\iTunes.exe var var
//
// Example rundll handlers:
//
// rundll32.exe "%ProgramFiles%\Win...ery\PhotoViewer.dll", var var
// rundll32.exe "%ProgramFiles%\Windows Photo Gallery\PhotoViewer.dll"
// C:\Windows\System32\rundll32.exe "path to dll", var var
// %SystemRoot%\System32\rundll32.exe "%ProgramFiles%\Win...ery\Photo
// Viewer.dll", var var

// Expand environment variables so we have full path strings.
uint32_t bufLength = ::ExpandEnvironmentStringsW(handlerCommand.get(),
L"", 0);
if (bufLength == 0) // Error
return false;

auto destination = mozilla::MakeUniqueFallible<wchar_t[]>(bufLength);
if (!destination)
return false;
if (!::ExpandEnvironmentStringsW(handlerCommand.get(), destination.get(),
bufLength))
return false;

handlerCommand.Assign(destination.get());

// Remove quotes around paths
handlerCommand.StripChars("\"");

// Strip windows host process bootstrap so we can get to the actual
// handler.
StripRundll32(handlerCommand);

// Trim any command parameters so that we have a native path we can
// initialize a local file with.
CleanupHandlerPath(handlerCommand);

aCommandHandler.Assign(handlerCommand);
return true;
}

// The "real" name of a given helper app (as specified by the path to the
// executable file held in various registry keys) is stored n the VERSIONINFO
// block in the file's resources. We need to find the path to the executable
Expand Down Expand Up @@ -482,28 +374,18 @@ nsOSHelperAppService::GetDefaultAppInfo(const nsAString& aAppInfo,
}
}

if (!CleanupCmdHandlerPath(handlerCommand))
return NS_ERROR_FAILURE;

// XXX FIXME: If this fails, the UI will display the full command
// string.
// There are some rare cases this can happen - ["url.dll" -foo]
// for example won't resolve correctly to the system dir. The
// subsequent launch of the helper app will work though.
nsCOMPtr<nsIFile> lf;
NS_NewLocalFile(handlerCommand, true, getter_AddRefs(lf));
if (!lf)
return NS_ERROR_FILE_NOT_FOUND;

nsILocalFileWin* lfw = nullptr;
CallQueryInterface(lf, &lfw);

if (lfw) {
// The "FileDescription" field contains the actual name of the application.
lfw->GetVersionInfoField("FileDescription", aDefaultDescription);
// QI addref'ed for us.
*aDefaultApplication = lfw;
}
nsCOMPtr<nsILocalFileWin> lf = new nsLocalFile();
rv = lf->InitWithCommandLine(handlerCommand);
NS_ENSURE_SUCCESS(rv, rv);

// The "FileDescription" field contains the actual name of the application.
lf->GetVersionInfoField("FileDescription", aDefaultDescription);
lf.forget(aDefaultApplication);

return NS_OK;
}
Expand Down
3 changes: 0 additions & 3 deletions uriloader/exthandler/win/nsOSHelperAppService.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ class nsOSHelperAppService : public nsExternalHelperAppService
*/
static bool GetValueString(HKEY hKey, const char16_t* pValueName, nsAString& result);

// Removes registry command handler parameters, quotes, and expands environment strings.
static bool CleanupCmdHandlerPath(nsAString& aCommandHandler);

protected:
nsresult GetDefaultAppInfo(const nsAString& aTypeName, nsAString& aDefaultDescription, nsIFile** aDefaultApplication);
// Lookup a mime info by extension, using an optional type hint
Expand Down
10 changes: 10 additions & 0 deletions xpcom/io/nsILocalFileWin.idl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ struct PRFileDesc;
[scriptable, builtinclass, uuid(e7a3a954-384b-4aeb-a5f7-55626b0de9be)]
interface nsILocalFileWin : nsILocalFile
{
/**
* initWithCommandLine
*
* Initialize this object based on the main app path of a commandline
* handler.
*
* @param aCommandLine
* the commandline to parse an app path out of.
*/
void initWithCommandLine(in AString aCommandLine);
/**
* getVersionInfoValue
*
Expand Down
123 changes: 123 additions & 0 deletions xpcom/io/nsLocalFileWin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "mozilla/ArrayUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/WindowsVersion.h"

#include "nsCOMPtr.h"
Expand Down Expand Up @@ -1191,6 +1192,128 @@ nsLocalFile::InitWithPath(const nsAString& aFilePath)

}

// Strip a handler command string of its quotes and parameters.
static void
CleanupHandlerPath(nsString& aPath)
{
// Example command strings passed into this routine:

// 1) C:\Program Files\Company\some.exe -foo -bar
// 2) C:\Program Files\Company\some.dll
// 3) C:\Windows\some.dll,-foo -bar
// 4) C:\Windows\some.cpl,-foo -bar

int32_t lastCommaPos = aPath.RFindChar(',');
if (lastCommaPos != kNotFound)
aPath.Truncate(lastCommaPos);

aPath.Append(' ');

// case insensitive
uint32_t index = aPath.Find(".exe ", true);
if (index == kNotFound)
index = aPath.Find(".dll ", true);
if (index == kNotFound)
index = aPath.Find(".cpl ", true);

if (index != kNotFound)
aPath.Truncate(index + 4);
aPath.Trim(" ", true, true);
}

// Strip the windows host process bootstrap executable rundll32.exe
// from a handler's command string if it exists.
static void
StripRundll32(nsString& aCommandString)
{
// Example rundll formats:
// C:\Windows\System32\rundll32.exe "path to dll"
// rundll32.exe "path to dll"
// C:\Windows\System32\rundll32.exe "path to dll", var var
// rundll32.exe "path to dll", var var

NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe ");
NS_NAMED_LITERAL_STRING(rundllSegmentShort, "rundll32 ");

// case insensitive
int32_t strLen = rundllSegment.Length();
int32_t index = aCommandString.Find(rundllSegment, true);
if (index == kNotFound) {
strLen = rundllSegmentShort.Length();
index = aCommandString.Find(rundllSegmentShort, true);
}

if (index != kNotFound) {
uint32_t rundllSegmentLength = index + strLen;
aCommandString.Cut(0, rundllSegmentLength);
}
}

// Returns the fully qualified path to an application handler based on
// a parameterized command string. Note this routine should not be used
// to launch the associated application as it strips parameters and
// rundll.exe from the string. Designed for retrieving display information
// on a particular handler.
/* static */ bool
nsLocalFile::CleanupCmdHandlerPath(nsAString& aCommandHandler)
{
nsAutoString handlerCommand(aCommandHandler);

// Straight command path:
//
// %SystemRoot%\system32\NOTEPAD.EXE var
// "C:\Program Files\iTunes\iTunes.exe" var var
// C:\Program Files\iTunes\iTunes.exe var var
//
// Example rundll handlers:
//
// rundll32.exe "%ProgramFiles%\Win...ery\PhotoViewer.dll", var var
// rundll32.exe "%ProgramFiles%\Windows Photo Gallery\PhotoViewer.dll"
// C:\Windows\System32\rundll32.exe "path to dll", var var
// %SystemRoot%\System32\rundll32.exe "%ProgramFiles%\Win...ery\Photo
// Viewer.dll", var var

// Expand environment variables so we have full path strings.
uint32_t bufLength = ::ExpandEnvironmentStringsW(handlerCommand.get(),
L"", 0);
if (bufLength == 0) // Error
return false;

auto destination = mozilla::MakeUniqueFallible<wchar_t[]>(bufLength);
if (!destination)
return false;
if (!::ExpandEnvironmentStringsW(handlerCommand.get(), destination.get(),
bufLength))
return false;

handlerCommand.Assign(destination.get());

// Remove quotes around paths
handlerCommand.StripChars("\"");

// Strip windows host process bootstrap so we can get to the actual
// handler.
StripRundll32(handlerCommand);

// Trim any command parameters so that we have a native path we can
// initialize a local file with.
CleanupHandlerPath(handlerCommand);

aCommandHandler.Assign(handlerCommand);
return true;
}


NS_IMETHODIMP
nsLocalFile::InitWithCommandLine(const nsAString& aCommandLine)
{
nsAutoString commandLine(aCommandLine);
if (!CleanupCmdHandlerPath(commandLine)) {
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
}
return InitWithPath(commandLine);
}

NS_IMETHODIMP
nsLocalFile::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode,
PRFileDesc** aResult)
Expand Down
3 changes: 3 additions & 0 deletions xpcom/io/nsLocalFileWin.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ class nsLocalFile final
static void GlobalInit();
static void GlobalShutdown();

// Removes registry command handler parameters, quotes, and expands environment strings.
static bool CleanupCmdHandlerPath(nsAString& aCommandHandler);

private:
// CopyMove and CopySingleFile constants for |options| parameter:
enum CopyFileOption {
Expand Down
21 changes: 21 additions & 0 deletions xpcom/tests/unit/test_windows_cmdline_file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
let { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");

let executableFile = Services.dirsvc.get("CurProcD", Ci.nsIFile);
executableFile.append("xpcshell.exe");
function run_test() {
let quote = '"'; // Windows' cmd processor doesn't actually use single quotes.
for (let suffix of ["", " -osint", ` --blah "%PROGRAMFILES%"`]) {
let cmdline = quote + executableFile.path + quote + suffix;
do_print(`Testing with ${cmdline}`);
let f = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFileWin);
f.initWithCommandLine(cmdline);
Assert.equal(f.path, executableFile.path, "Should be able to recover executable path");
}

let f = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFileWin);
f.initWithCommandLine("%ComSpec% -c echo 'hi'");
let cmd = Services.dirsvc.get("SysD", Ci.nsIFile);
cmd.append("cmd.exe");
Assert.equal(f.path, cmd.path, "Should be able to replace env vars.");
}
Loading

0 comments on commit 50df3b1

Please sign in to comment.