Bug 1658563 - Support .lnk files in file: protocol r=necko-reviewers,dragana

Differential Revision: https://phabricator.services.mozilla.com/D90283
This commit is contained in:
Kagami Sascha Rosylight 2020-09-22 07:26:33 +00:00
parent a4a8cb224d
commit bcf37d1286
12 changed files with 127 additions and 17 deletions

View File

@ -334,8 +334,8 @@ nsresult nsFileChannel::OpenContentStream(bool async, nsIInputStream** result,
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIURI> newURI;
rv = fileHandler->ReadURLFile(file, getter_AddRefs(newURI));
if (NS_SUCCEEDED(rv)) {
if (NS_SUCCEEDED(fileHandler->ReadURLFile(file, getter_AddRefs(newURI))) ||
NS_SUCCEEDED(fileHandler->ReadShellLink(file, getter_AddRefs(newURI)))) {
nsCOMPtr<nsIChannel> newChannel;
rv = NS_NewChannel(getter_AddRefs(newChannel), newURI,
nsContentUtils::GetSystemPrincipal(),

View File

@ -15,6 +15,8 @@
#include "FileChannelChild.h"
#include "mozilla/ResultExtensions.h"
// URL file handling, copied and modified from
// xpfe/components/bookmarks/src/nsBookmarksService.cpp
#ifdef XP_WIN
@ -123,6 +125,38 @@ nsFileProtocolHandler::ReadURLFile(nsIFile* aFile, nsIURI** aURI) {
}
#endif // ReadURLFile()
NS_IMETHODIMP
nsFileProtocolHandler::ReadShellLink(nsIFile* aFile, nsIURI** aURI) {
#if defined(XP_WIN)
nsAutoString path;
MOZ_TRY(aFile->GetPath(path));
if (path.Length() < 4 ||
!StringTail(path, 4).LowerCaseEqualsLiteral(".lnk")) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<IPersistFile> persistFile;
RefPtr<IShellLinkW> shellLink;
WCHAR lpTemp[MAX_PATH];
// Get a pointer to the IPersistFile interface.
if (FAILED(CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
IID_IShellLinkW, getter_AddRefs(shellLink))) ||
FAILED(shellLink->QueryInterface(IID_IPersistFile,
getter_AddRefs(persistFile))) ||
FAILED(persistFile->Load(path.get(), STGM_READ)) ||
FAILED(shellLink->Resolve(nullptr, SLR_NO_UI)) ||
FAILED(shellLink->GetPath(lpTemp, MAX_PATH, nullptr, SLGP_UNCPRIORITY))) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIFile> linkedFile;
MOZ_TRY(NS_NewLocalFile(nsDependentString(lpTemp), false,
getter_AddRefs(linkedFile)));
return NS_NewFileURI(aURI, linkedFile);
#else
return NS_ERROR_NOT_AVAILABLE;
#endif
}
NS_IMETHODIMP
nsFileProtocolHandler::GetScheme(nsACString& result) {
result.AssignLiteral("file");

View File

@ -73,4 +73,15 @@ interface nsIFileProtocolHandler : nsIProtocolHandler
* @throw NS_ERROR_NOT_AVAILABLE if this file is not an internet shortcut.
*/
nsIURI readURLFile(in nsIFile file);
/**
* Takes a local file and tries to interpret it as a shell link file
* (.lnk files on Windows)
* @param file The local file to read
* @return The URI the file refers to
*
* @throw NS_ERROR_NOT_AVAILABLE if the OS does not support such files.
* @throw NS_ERROR_NOT_AVAILABLE if this file is not a shell link.
*/
nsIURI readShellLink(in nsIFile file);
};

View File

@ -22,3 +22,7 @@ support-files =
damonbowling.jpg
damonbowling.jpg^headers^
file_favicon.html
[browser_fetch_lnk.js]
run-if = os == "win"
support-files =
file_lnk.lnk

View File

@ -0,0 +1,27 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(async () => {
await SpecialPowers.pushPrefEnv({
set: [["privacy.file_unique_origin", false]],
});
const FILE_PAGE = Services.io.newFileURI(
new FileUtils.File(getTestFilePath("dummy.html"))
).spec;
await BrowserTestUtils.withNewTab(FILE_PAGE, async browser => {
try {
await SpecialPowers.spawn(browser, [], () =>
content.fetch("./file_lnk.lnk")
);
ok(
false,
"Loading lnk must fail if it links to a file from other directory"
);
} catch (err) {
is(err.constructor.name, "TypeError", "Should fail on Windows");
}
});
});

Binary file not shown.

Binary file not shown.

View File

@ -37,6 +37,7 @@ support-files =
file_image_inner.html^headers^
file_image_inner_inner.html
file_image_inner_inner.html^headers^
file_lnk.lnk
file_loadflags_inner.html
file_loadflags_inner.html^headers^
file_localhost_inner.html
@ -84,6 +85,7 @@ support-files = sw_1502055.js file_1502055.sjs iframe_1502055.html
support-files = test_accept_header.sjs
[test_different_domain_in_hierarchy.html]
[test_differentdomain.html]
[test_fetch_lnk.html]
[test_image.html]
[test_loadflags.html]
[test_same_base_domain.html]

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Downloading .lnk through HTTP should always download the file without parsing it</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script>
SimpleTest.waitForExplicitFinish();
// Download .lnk which points to a system executable
fetch("file_lnk.lnk").then(async res => {
ok(res.ok, "Download success");
ok(res.url.endsWith("file_lnk.lnk"), "file name should be of the lnk file");
is(res.headers.get("Content-Length"), "1531", "The size should be of the lnk file");
SimpleTest.finish();
}, () => {
ok(false, "Unreachable code");
})
</script>

View File

@ -1,6 +1,6 @@
"use strict";
function getLinkFile() {
function getUrlLinkFile() {
if (mozinfo.os == "win") {
return do_get_file("test_link.url");
}
@ -12,9 +12,6 @@ function getLinkFile() {
}
const ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var link;
var linkURI;
const newURI = ios.newURI("http://www.mozilla.org/");
function NotificationCallbacks(origURI, newURI) {
this._origURI = origURI;
@ -74,7 +71,7 @@ RequestObserver.prototype = {
},
};
function test_cancel() {
function test_cancel(linkURI, newURI) {
var chan = NetUtil.newChannel({
uri: linkURI,
loadUsingSystemPrincipal: true,
@ -87,12 +84,27 @@ function test_cancel() {
Assert.ok(chan.isPending());
}
function test_channel(linkURI, newURI) {
const chan = NetUtil.newChannel({
uri: linkURI,
loadUsingSystemPrincipal: true,
});
Assert.equal(chan.URI, linkURI);
Assert.equal(chan.originalURI, linkURI);
chan.notificationCallbacks = new NotificationCallbacks(linkURI, newURI);
chan.asyncOpen(
new RequestObserver(linkURI, newURI, () => test_cancel(linkURI, newURI))
);
Assert.ok(chan.isPending());
}
function run_test() {
if (mozinfo.os != "win" && mozinfo.os != "linux") {
return;
}
link = getLinkFile();
let link = getUrlLinkFile();
let linkURI;
if (link.isSymlink()) {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(link.target);
@ -102,13 +114,15 @@ function run_test() {
}
do_test_pending();
var chan = NetUtil.newChannel({
uri: linkURI,
loadUsingSystemPrincipal: true,
});
Assert.equal(chan.URI, linkURI);
Assert.equal(chan.originalURI, linkURI);
chan.notificationCallbacks = new NotificationCallbacks(linkURI, newURI);
chan.asyncOpen(new RequestObserver(linkURI, newURI, test_cancel));
Assert.ok(chan.isPending());
test_channel(linkURI, ios.newURI("http://www.mozilla.org/"));
if (mozinfo.os != "win") {
return;
}
link = do_get_file("test_link.lnk");
test_channel(
ios.newFileURI(link),
ios.newURI("file:///Z:/moz-nonexistent/index.html")
);
}

Binary file not shown.

View File

@ -20,6 +20,7 @@ support-files =
socks_client_subprocess.js
test_link.desktop
test_link.url
test_link.lnk
../../dns/effective_tld_names.dat
test_alt-data_cross_process.js