Merged PR 1609222: Unpack appx bundles
In this change: - Enable unpacking appx bundles and recursive unpacking of the packages in the bundle. - Generate PublsiherId using the Windows implementation - Take out the bundles xsds from the RESOURCES_APPXBUNDLEMANIFEST set - Remove cache semantics in DirectoryObject to avoid errono = 24 (too many files open) while unpacking a bundle - Add appx bundle packages for testing - Enable some tests on non-mobile platforms. On non-mobile we use the public APIs. This functionality will be enable in another PR.
This commit is contained in:
Родитель
bf2e8560d0
Коммит
41b3df6e61
|
@ -10,7 +10,7 @@ xcuserdata/
|
|||
*.xccheckout
|
||||
|
||||
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
|
||||
build/
|
||||
build*/
|
||||
DerivedData/
|
||||
*.moved-aside
|
||||
*.pbxuser
|
||||
|
|
|
@ -8,6 +8,7 @@ set(RESOURCES_CERTS)
|
|||
set(RESOURCES_BLOCKMAP)
|
||||
set(RESOURCES_CONTENTTYPE)
|
||||
set(RESOURCES_APPXMANIFEST)
|
||||
set(RESOURCES_APPXBUNDLEMANIFEST)
|
||||
|
||||
if(NOT WIN32) # Always add the certs for non-Windows.
|
||||
list(APPEND RESOURCES_CERTS
|
||||
|
@ -34,8 +35,6 @@ if(HAVE_MSXML6)
|
|||
list(APPEND RESOURCES_APPXMANIFEST
|
||||
"AppxPackaging/Manifest/Schema/2015/AppxManifestTypes.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2015/AppxPhoneManifestSchema2014.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2015/BundleManifestSchema2013.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2015/BundleManifestSchema2014.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2015/ComManifestSchema.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2015/DesktopManifestSchema.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2015/FoundationManifestSchema.xsd"
|
||||
|
@ -52,12 +51,10 @@ if(HAVE_MSXML6)
|
|||
"AppxPackaging/Manifest/Schema/2015/WindowsCapabilitiesManifestSchema.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2015/WindowsCapabilitiesManifestSchema_v2.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2015/XboxManifestSchema.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2016/BundleManifestSchema2016.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2016/DesktopManifestSchema_v2.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2016/RestrictedCapabilitiesManifestSchema_v3.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2016/UapManifestSchema_v4.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2016/WindowsCapabilitiesManifestSchema_v3.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2017/BundleManifestSchema2017.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2017/ComManifestSchema_v2.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2017/DesktopManifestSchema_v3.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2017/DesktopManifestSchema_v4.xsd"
|
||||
|
@ -69,16 +66,22 @@ else() # xerces
|
|||
# TODO: make changes required to make the xsds WC3 compliant.
|
||||
endif()
|
||||
|
||||
endif(USE_VALIDATION_PARSER)
|
||||
list(APPEND RESOURCES_APPXBUNDLEMANIFEST
|
||||
"AppxPackaging/Manifest/Schema/2015/BundleManifestSchema2013.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2015/BundleManifestSchema2014.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2016/BundleManifestSchema2016.xsd"
|
||||
"AppxPackaging/Manifest/Schema/2017/BundleManifestSchema2017.xsd")
|
||||
|
||||
endif(USE_VALIDATION_PARSER)
|
||||
|
||||
# Create zip file. Use execute_process to run the command while CMake is procesing.
|
||||
message(STATUS "Resource files:")
|
||||
foreach(FILE ${RESOURCES_BLOCKMAP} ${RESOURCES_CONTENTTYPE} ${RESOURCES_APPXMANIFEST} ${RESOURCES_CERTS})
|
||||
foreach(FILE ${RESOURCES_BLOCKMAP} ${RESOURCES_CONTENTTYPE} ${RESOURCES_APPXMANIFEST} ${RESOURCES_CERTS} ${RESOURCES_APPXBUNDLEMANIFEST})
|
||||
message(STATUS "\t${FILE}")
|
||||
endforeach(FILE)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E tar cvf "${CMAKE_BINARY_DIR}/resources.zip" --format=zip -- ${RESOURCES_BLOCKMAP} ${RESOURCES_CONTENTTYPE} ${RESOURCES_APPXMANIFEST} ${RESOURCES_CERTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E tar cvf "${CMAKE_BINARY_DIR}/resources.zip" --format=zip -- ${RESOURCES_BLOCKMAP} ${RESOURCES_CONTENTTYPE} ${RESOURCES_APPXMANIFEST} ${RESOURCES_CERTS} ${RESOURCES_APPXBUNDLEMANIFEST}
|
||||
WORKING_DIRECTORY "${CMAKE_PROJECT_ROOT}/resources"
|
||||
OUTPUT_QUIET
|
||||
)
|
||||
|
@ -98,10 +101,11 @@ function(GetResourceHpp LIST OUTPUT)
|
|||
set(${OUTPUT} ${RESULT} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
GetResourceHpp("${RESOURCES_BLOCKMAP}" BLOCKMAP_HPP)
|
||||
GetResourceHpp("${RESOURCES_CONTENTTYPE}" CONTENTTYPE_HPP)
|
||||
GetResourceHpp("${RESOURCES_APPXMANIFEST}" APPXMANIFEST_HPP)
|
||||
GetResourceHpp("${RESOURCES_CERTS}" CERTS_HPP)
|
||||
GetResourceHpp("${RESOURCES_BLOCKMAP}" BLOCKMAP_HPP)
|
||||
GetResourceHpp("${RESOURCES_CONTENTTYPE}" CONTENTTYPE_HPP)
|
||||
GetResourceHpp("${RESOURCES_APPXMANIFEST}" APPXMANIFEST_HPP)
|
||||
GetResourceHpp("${RESOURCES_CERTS}" CERTS_HPP)
|
||||
GetResourceHpp("${RESOURCES_APPXBUNDLEMANIFEST}" APPXBUNDLEMANIFEST_HPP)
|
||||
|
||||
set(RESOURCE_HEADER "//
|
||||
//
|
||||
|
@ -122,7 +126,8 @@ namespace MSIX {
|
|||
Certificates,
|
||||
ContentType,
|
||||
BlockMap,
|
||||
AppxManifest
|
||||
AppxManifest,
|
||||
AppxBundleManifest
|
||||
};
|
||||
|
||||
const size_t resourceLength = ${RESOURCE_LENGTH};
|
||||
|
@ -146,6 +151,9 @@ namespace MSIX {
|
|||
case Resource::AppxManifest:
|
||||
${APPXMANIFEST_HPP}
|
||||
break;
|
||||
case Resource::AppxBundleManifest:
|
||||
${APPXBUNDLEMANIFEST_HPP}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -214,10 +214,11 @@ namespace MSIX {
|
|||
AppxBlockMapObject(IMSIXFactory* factory, const ComPtr<IStream>& stream);
|
||||
|
||||
// IVerifierObject
|
||||
const std::string& GetPublisher() override {NOTSUPPORTED;}
|
||||
const std::string& GetPublisher() override { NOTSUPPORTED; }
|
||||
bool HasStream() override { return !!m_stream; }
|
||||
ComPtr<IStream> GetStream() override { return m_stream; }
|
||||
ComPtr<IStream> GetValidationStream(const std::string& part, const ComPtr<IStream>& stream) override;
|
||||
const std::string GetPackageFullName() override { NOTSUPPORTED; }
|
||||
|
||||
// IAppxBlockMapReader
|
||||
HRESULT STDMETHODCALLTYPE GetFile(LPCWSTR filename, IAppxBlockMapFile **file) noexcept override;
|
||||
|
|
|
@ -56,16 +56,16 @@ namespace MSIX {
|
|||
std::string ResourceId;
|
||||
std::string Architecture;
|
||||
std::string Publisher;
|
||||
std::string PublisherHash;
|
||||
std::string PublisherId;
|
||||
|
||||
std::string GetPackageFullName()
|
||||
{
|
||||
return Name + "_" + Version + "_" + Architecture + "_" + ResourceId + "_" + PublisherHash;
|
||||
return Name + "_" + Version + "_" + Architecture + "_" + ResourceId + "_" + PublisherId;
|
||||
}
|
||||
|
||||
std::string GetPackageFamilyName()
|
||||
{
|
||||
return Name + "_" + PublisherHash;
|
||||
return Name + "_" + PublisherId;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -80,9 +80,9 @@ namespace MSIX {
|
|||
bool HasStream() override { return !!m_stream; }
|
||||
ComPtr<IStream> GetStream() override { return m_stream; }
|
||||
ComPtr<IStream> GetValidationStream(const std::string& part, const ComPtr<IStream>&) override { NOTSUPPORTED; }
|
||||
const std::string GetPackageFullName() override { return m_packageId->GetPackageFullName(); }
|
||||
|
||||
AppxPackageId* GetPackageId() { return m_packageId.get(); }
|
||||
std::string GetPackageFullName() { return m_packageId->GetPackageFullName(); }
|
||||
|
||||
protected:
|
||||
ComPtr<IStream> m_stream;
|
||||
|
@ -123,9 +123,11 @@ namespace MSIX {
|
|||
ComPtr<IStream> GetFile(const std::string& fileName) override;
|
||||
|
||||
ComPtr<IStream> OpenFile(const std::string& fileName, MSIX::FileStream::Mode mode) override;
|
||||
void CommitChanges() override;
|
||||
|
||||
protected:
|
||||
// Helper methods
|
||||
void VerifyFile(const ComPtr<IStream>& stream, const std::string& fileName, const ComPtr<IAppxBlockMapInternal>& blockMapInternal);
|
||||
|
||||
std::map<std::string, ComPtr<IStream>> m_streams;
|
||||
|
||||
MSIX_VALIDATION_OPTION m_validation = MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_FULL;
|
||||
|
@ -137,6 +139,9 @@ namespace MSIX {
|
|||
|
||||
std::vector<std::string> m_payloadFiles;
|
||||
std::vector<std::string> m_footprintFiles;
|
||||
std::vector<std::string> m_payloadPackages;
|
||||
|
||||
bool m_isBundle = false;
|
||||
};
|
||||
|
||||
class AppxFilesEnumerator final : public MSIX::ComClass<AppxFilesEnumerator, IAppxFilesEnumerator>
|
||||
|
|
|
@ -68,6 +68,7 @@ namespace MSIX {
|
|||
bool HasStream() override { return !!m_stream; }
|
||||
ComPtr<IStream> GetStream() override { return m_stream; }
|
||||
ComPtr<IStream> GetValidationStream(const std::string& part, const ComPtr<IStream>& stream) override;
|
||||
const std::string GetPackageFullName() override { NOTSUPPORTED; }
|
||||
|
||||
void ValidateDigestHeader(DigestHeader* header, std::size_t numberOfHashes, std::size_t modHashes);
|
||||
|
||||
|
|
|
@ -27,10 +27,8 @@ namespace MSIX {
|
|||
ComPtr<IStream> GetFile(const std::string& fileName) override;
|
||||
|
||||
ComPtr<IStream> OpenFile(const std::string& fileName, MSIX::FileStream::Mode mode) override;
|
||||
void CommitChanges() override;
|
||||
|
||||
protected:
|
||||
std::map<std::string, ComPtr<IStream>> m_streams;
|
||||
std::string m_root;
|
||||
|
||||
};//class DirectoryObject
|
||||
|
|
|
@ -11,6 +11,6 @@ namespace MSIX {
|
|||
class SHA256
|
||||
{
|
||||
public:
|
||||
static bool ComputeHash(/*in*/ std::uint8_t *buffer, /*in*/ std::uint32_t cbBuffer, /*inout*/ std::vector<uint8_t>& hash);
|
||||
static bool ComputeHash(std::uint8_t *buffer, std::uint32_t cbBuffer, std::vector<uint8_t>& hash);
|
||||
};
|
||||
}
|
|
@ -55,11 +55,6 @@ public:
|
|||
// or read + update, then nullptr is returned. If the file is opened with write and it does not exist,
|
||||
// then the file is created and an empty stream to the file is handed back to the caller.
|
||||
virtual MSIX::ComPtr<IStream> OpenFile(const std::string& fileName, MSIX::FileStream::Mode mode) = 0;
|
||||
|
||||
// Some storage objects may operate under cache semantics and therefore require an explicit commit.
|
||||
// Clients should explicitly call CommitChanges after all write operations into the object are complete.
|
||||
// An implementation of this interface MAY be a no-op.
|
||||
virtual void CommitChanges() = 0;
|
||||
};
|
||||
|
||||
SpecializeUuidOfImpl(IStorageObject);
|
|
@ -5,11 +5,34 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
namespace MSIX {
|
||||
namespace MSIX {
|
||||
/*
|
||||
from: https://connect.microsoft.com/VisualStudio/feedback/details/1403302/unresolved-external-when-using-codecvt-utf8
|
||||
Posted by Microsoft on 2/16/2016 at 11:49 AM
|
||||
<snip>
|
||||
A workaround is to replace 'char32_t' with 'unsigned int'. In VS2013, char32_t was a typedef of 'unsigned int'.
|
||||
In VS2015, char32_t is a distinct type of it's own. Switching your use of 'char32_t' to 'unsigned int' will get
|
||||
you the old behavior from earlier versions and won't trigger a missing export error.
|
||||
|
||||
There is also a similar error to this one with 'char16_t' that can be worked around using 'unsigned short'.
|
||||
<snip>
|
||||
*/
|
||||
#ifdef WIN32
|
||||
using StringType = std::basic_string<unsigned short>;
|
||||
using StringConvert = std::wstring_convert<std::codecvt_utf8_utf16<unsigned short>, unsigned short>;
|
||||
#else
|
||||
using StringType = std::u16string;
|
||||
using StringConvert = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>;
|
||||
#endif
|
||||
|
||||
StringType utf8_to_utf16(const std::string& utf8string);
|
||||
|
||||
// converts an input utf8 formatted string into a utf16 formatted string
|
||||
std::wstring utf8_to_utf16(const std::string& utf8string);
|
||||
std::wstring utf8_to_wstring(const std::string& utf8string);
|
||||
std::u16string utf8_to_u16string(const std::string& utf8string);
|
||||
|
||||
// converts an input utf16 formatted string into a utf8 formatted string
|
||||
std::string utf16_to_utf8(const std::wstring& utf16string);
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
virtual bool HasStream() = 0;
|
||||
virtual MSIX::ComPtr<IStream> GetStream() = 0;
|
||||
virtual MSIX::ComPtr<IStream> GetValidationStream(const std::string& part, const MSIX::ComPtr<IStream>& stream) = 0;
|
||||
virtual const std::string GetPackageFullName() = 0;
|
||||
};
|
||||
|
||||
SpecializeUuidOfImpl(IVerifierObject);
|
|
@ -34,6 +34,12 @@ struct State
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CreatePackageSubfolder()
|
||||
{
|
||||
unpackOptions = static_cast<MSIX_PACKUNPACK_OPTION>(unpackOptions | MSIX_PACKUNPACK_OPTION::MSIX_PACKUNPACK_OPTION_CREATEPACKAGESUBFOLDER);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkipManifestValidation()
|
||||
{
|
||||
validationOptions = static_cast<MSIX_VALIDATION_OPTION>(validationOptions | MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_SKIPAPPXMANIFEST);
|
||||
|
@ -73,7 +79,8 @@ struct State
|
|||
case UserSpecified::Unpack:
|
||||
if (packageName.empty() || directoryName.empty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -266,6 +273,8 @@ int main(int argc, char* argv[])
|
|||
[](State& state, const std::string& name) { return state.SetPackageName(name); }),
|
||||
Option("-d", true, "REQUIRED, specify output directory name.",
|
||||
[](State& state, const std::string& name) { return state.SetDirectoryName(name); }),
|
||||
Option("-pfn", false, "Unpacks all files to a subdirectory under the specified output path, named after the package full name.",
|
||||
[](State& state, const std::string&) {return state.CreatePackageSubfolder(); }),
|
||||
Option("-mv", false, "Skips manifest validation. By default manifest validation is enabled.",
|
||||
[](State& state, const std::string&) { return state.SkipManifestValidation(); }),
|
||||
Option("-sv", false, "Skips signature validation. By default signature validation is enabled.",
|
||||
|
|
|
@ -20,14 +20,16 @@
|
|||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
// names of footprint files.
|
||||
#define APPXBLOCKMAP_XML "AppxBlockMap.xml"
|
||||
#define APPXMANIFEST_XML "AppxManifest.xml"
|
||||
#define CODEINTEGRITY_CAT "AppxMetadata/CodeIntegrity.cat"
|
||||
#define APPXSIGNATURE_P7X "AppxSignature.p7x"
|
||||
#define CONTENT_TYPES_XML "[Content_Types].xml"
|
||||
#define APPXBLOCKMAP_XML "AppxBlockMap.xml"
|
||||
#define APPXMANIFEST_XML "AppxManifest.xml"
|
||||
#define CODEINTEGRITY_CAT "AppxMetadata/CodeIntegrity.cat"
|
||||
#define APPXSIGNATURE_P7X "AppxSignature.p7x"
|
||||
#define CONTENT_TYPES_XML "[Content_Types].xml"
|
||||
#define APPXBUNDLEMANIFEST_XML "AppxMetadata/AppxBundleManifest.xml"
|
||||
|
||||
static const std::array<const char*, 4> footprintFiles =
|
||||
{ APPXMANIFEST_XML,
|
||||
|
@ -52,6 +54,9 @@ namespace MSIX {
|
|||
nullptr, nullptr, nullptr, "%5B", nullptr, "%5D" // [ ]
|
||||
};
|
||||
|
||||
// Douglas Crockford's base 32 alphabet variant is 0-9, A-Z except for i, l, o, and u.
|
||||
static const char base32DigitList[] = "0123456789abcdefghjkmnpqrstvwxyz";
|
||||
|
||||
struct EncodingChar
|
||||
{
|
||||
const char* encode;
|
||||
|
@ -106,6 +111,66 @@ namespace MSIX {
|
|||
return result;
|
||||
}
|
||||
|
||||
static std::string Base32Encoding(const std::vector<uint8_t>& bytes)
|
||||
{
|
||||
static const size_t publisherIdSize = 13;
|
||||
static const size_t byteCount = 8;
|
||||
|
||||
// Consider groups of five bytes. This is the smallest number of bytes that has a number of bits
|
||||
// that's evenly divisible by five.
|
||||
// Every five bits starting with the most significant of the first byte are made into a base32 value.
|
||||
// Each value is used to index into the alphabet array to produce a base32 digit.
|
||||
// When out of bytes but the corresponding base32 value doesn't yet have five bits, 0 is used.
|
||||
// Normally in these cases a particular number of '=' characters are appended to the resulting base32
|
||||
// string to indicate how many bits didn't come from the actual byte value. For our purposes no
|
||||
// such padding characters are necessary.
|
||||
//
|
||||
// Bytes: aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee
|
||||
// Base32 Values: 000aaaaa 000aaabb 000bbbbb 000bcccc 000ccccd 000ddddd 000ddeee 000eeeee
|
||||
//
|
||||
// Combo of byte a & F8 a & 07 b & 3E b & 01 c & 0F d & 7C d & 03 e & 1F
|
||||
// values except b & C0 c & F0 d & 80 e & E0
|
||||
// for shifting
|
||||
|
||||
// Make sure the following math doesn't overflow.
|
||||
char output[publisherIdSize+1] = "";
|
||||
size_t outputIndex = 0;
|
||||
for(size_t byteIndex = 0; byteIndex < byteCount; byteIndex +=5)
|
||||
{
|
||||
uint8_t firstByte = bytes[byteIndex];
|
||||
uint8_t secondByte = (byteIndex + 1) < byteCount ? bytes[byteIndex + 1] : 0;
|
||||
output[outputIndex++] = base32DigitList[(firstByte & 0xF8) >> 3];
|
||||
output[outputIndex++] = base32DigitList[((firstByte & 0x07) << 2) | ((secondByte & 0xC0) >> 6)];
|
||||
|
||||
if(byteIndex + 1 < byteCount)
|
||||
{
|
||||
uint8_t thirdByte = (byteIndex + 2) < byteCount ? bytes[byteIndex + 2] : 0;
|
||||
output[outputIndex++] = base32DigitList[(secondByte & 0x3E) >> 1];
|
||||
output[outputIndex++] = base32DigitList[((secondByte & 0x01) << 4) | ((thirdByte & 0xF0) >> 4)];
|
||||
|
||||
if(byteIndex + 2 < byteCount)
|
||||
{
|
||||
uint8_t fourthByte = (byteIndex + 3) < byteCount ? bytes[byteIndex + 3] : 0;
|
||||
output[outputIndex++] = base32DigitList[((thirdByte & 0x0F) << 1) | ((fourthByte & 0x80) >> 7)];
|
||||
|
||||
if (byteIndex + 3 < byteCount)
|
||||
{
|
||||
uint8_t fifthByte = (byteIndex + 4) < byteCount ? bytes[byteIndex + 4] : 0;
|
||||
output[outputIndex++] = base32DigitList[(fourthByte & 0x7C) >> 2];
|
||||
output[outputIndex++] = base32DigitList[((fourthByte & 0x03) << 3) | ((fifthByte & 0xE0) >> 5)];
|
||||
|
||||
if (byteIndex + 4 < byteCount)
|
||||
{
|
||||
output[outputIndex++] = base32DigitList[fifthByte & 0x1F];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
output[publisherIdSize] = '\0';
|
||||
return std::string(output);
|
||||
}
|
||||
|
||||
AppxPackageId::AppxPackageId(
|
||||
const std::string& name,
|
||||
const std::string& version,
|
||||
|
@ -118,7 +183,15 @@ namespace MSIX {
|
|||
// Only name, publisher and version are required
|
||||
ThrowErrorIf(Error::AppxManifestSemanticError, (Name.empty() || Version.empty() || Publisher.empty()), "Invalid Identity element");
|
||||
|
||||
// TODO: calculate the publisher hash from the publisher value.
|
||||
// TODO: validate the name and resource id as package strings
|
||||
|
||||
auto wpublisher = utf8_to_u16string(publisher);
|
||||
std::vector<std::uint8_t> buffer(wpublisher.size() * sizeof(char16_t));
|
||||
memcpy(buffer.data(), &wpublisher[0], wpublisher.size() * sizeof(char16_t));
|
||||
|
||||
std::vector<std::uint8_t> hash;
|
||||
ThrowErrorIfNot(Error::Unexpected, SHA256::ComputeHash(buffer.data(), buffer.size(), hash), "Failed computing publisherId");
|
||||
PublisherId = Base32Encoding(hash);
|
||||
}
|
||||
|
||||
AppxManifestObject::AppxManifestObject(IXmlFactory* factory, const ComPtr<IStream>& stream) : m_stream(stream)
|
||||
|
@ -174,16 +247,32 @@ namespace MSIX {
|
|||
|
||||
// 4. Get manifest object using blockmap object for validation
|
||||
// TODO: pass validation flags and other necessary goodness through.
|
||||
file = m_container->GetFile(APPXMANIFEST_XML);
|
||||
ThrowErrorIfNot(Error::MissingAppxManifestXML, file, "AppxManifest.xml not in archive!");
|
||||
stream = m_appxBlockMap->GetValidationStream(APPXMANIFEST_XML, file);
|
||||
m_appxManifest = ComPtr<IVerifierObject>::Make<AppxManifestObject>(xmlFactory.Get(), stream);
|
||||
auto appxManifestInContainer = m_container->GetFile(APPXMANIFEST_XML);
|
||||
auto appxBundleManifestInContainer = m_container->GetFile(APPXBUNDLEMANIFEST_XML);
|
||||
|
||||
if ((validation & MSIX_VALIDATION_OPTION_SKIPSIGNATURE) == 0)
|
||||
ThrowErrorIfNot(Error::MissingAppxManifestXML, (appxManifestInContainer || appxBundleManifestInContainer) ,
|
||||
"AppxManifest.xml or AppxBundleManifest.xml not in archive!");
|
||||
ThrowErrorIf(Error::MissingAppxManifestXML, (appxManifestInContainer && appxBundleManifestInContainer) ,
|
||||
"AppxManifest.xml and AppxBundleManifest.xml in archive!");
|
||||
// We already validate that there's at least one and not both
|
||||
if(appxManifestInContainer)
|
||||
{
|
||||
std::string reason = "Publisher mismatch: '" + m_appxManifest->GetPublisher() + "' != '" + m_appxSignature->GetPublisher() + "'";
|
||||
ThrowErrorIfNot(Error::PublisherMismatch,
|
||||
(0 == m_appxManifest->GetPublisher().compare(m_appxSignature->GetPublisher())), reason.c_str());
|
||||
stream = m_appxBlockMap->GetValidationStream(APPXMANIFEST_XML, appxManifestInContainer);
|
||||
m_appxManifest = ComPtr<IVerifierObject>::Make<AppxManifestObject>(xmlFactory.Get(), stream);
|
||||
if ((m_validation & MSIX_VALIDATION_OPTION_SKIPSIGNATURE) == 0)
|
||||
{
|
||||
std::string reason = "Publisher mismatch: '" + m_appxManifest->GetPublisher() + "' != '" + m_appxSignature->GetPublisher() + "'";
|
||||
ThrowErrorIfNot(Error::PublisherMismatch,
|
||||
(0 == m_appxManifest->GetPublisher().compare(m_appxSignature->GetPublisher())), reason.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string pathInWindows(APPXBUNDLEMANIFEST_XML);
|
||||
std::replace(pathInWindows.begin(), pathInWindows.end(), '/', '\\');
|
||||
stream = m_appxBlockMap->GetValidationStream(pathInWindows, appxBundleManifestInContainer);
|
||||
// TODO: create appxBundleManifestObject and validate
|
||||
m_isBundle = true;
|
||||
}
|
||||
|
||||
struct Config
|
||||
|
@ -200,11 +289,12 @@ namespace MSIX {
|
|||
};
|
||||
|
||||
static const Config footPrintFileNames[] = {
|
||||
Config(APPXBLOCKMAP_XML, [](AppxPackageObject* self){ self->m_footprintFiles.push_back(APPXBLOCKMAP_XML); return self->m_appxBlockMap->GetStream();}),
|
||||
Config(APPXMANIFEST_XML, [](AppxPackageObject* self){ self->m_footprintFiles.push_back(APPXMANIFEST_XML); return self->m_appxManifest->GetStream();}),
|
||||
Config(APPXSIGNATURE_P7X, [](AppxPackageObject* self){ if (self->m_appxSignature->HasStream()){self->m_footprintFiles.push_back(APPXSIGNATURE_P7X);} return self->m_appxSignature->GetStream();}),
|
||||
Config(CODEINTEGRITY_CAT, [](AppxPackageObject* self){ self->m_footprintFiles.push_back(CODEINTEGRITY_CAT); auto file = self->m_container->GetFile(CODEINTEGRITY_CAT); return self->m_appxSignature->GetValidationStream(CODEINTEGRITY_CAT, file);}),
|
||||
Config(CONTENT_TYPES_XML, [](AppxPackageObject*)->ComPtr<IStream>{ return ComPtr<IStream>();}), // content types is never implicitly unpacked
|
||||
Config(APPXBLOCKMAP_XML, [](AppxPackageObject* self){ self->m_footprintFiles.push_back(APPXBLOCKMAP_XML); return self->m_appxBlockMap->GetStream();}),
|
||||
Config(APPXMANIFEST_XML, [](AppxPackageObject* self){ self->m_footprintFiles.push_back(APPXMANIFEST_XML); return self->m_appxManifest->GetStream();}),
|
||||
Config(APPXSIGNATURE_P7X, [](AppxPackageObject* self){ if (self->m_appxSignature->HasStream()){self->m_footprintFiles.push_back(APPXSIGNATURE_P7X);} return self->m_appxSignature->GetStream();}),
|
||||
Config(CODEINTEGRITY_CAT, [](AppxPackageObject* self){ self->m_footprintFiles.push_back(CODEINTEGRITY_CAT); auto file = self->m_container->GetFile(CODEINTEGRITY_CAT); return self->m_appxSignature->GetValidationStream(CODEINTEGRITY_CAT, file);}),
|
||||
Config(CONTENT_TYPES_XML, [](AppxPackageObject*)->ComPtr<IStream>{ return ComPtr<IStream>();}), // content types is never implicitly unpacked
|
||||
Config(APPXBUNDLEMANIFEST_XML, [](AppxPackageObject* self){ self->m_footprintFiles.push_back(APPXBUNDLEMANIFEST_XML); return /*self->m_appxBundleManifest->GetStream()*/ self->m_container->GetFile(APPXBUNDLEMANIFEST_XML);}),
|
||||
};
|
||||
|
||||
// 5. Ensure that the stream collection contains streams wired up for their appropriate validation
|
||||
|
@ -218,71 +308,99 @@ namespace MSIX {
|
|||
filesToProcess.erase(std::remove(filesToProcess.begin(), filesToProcess.end(), fileName), filesToProcess.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
auto blockMapInternal = m_appxBlockMap.As<IAppxBlockMapInternal>();
|
||||
for (const auto& fileName : blockMapInternal->GetFileNames())
|
||||
{ auto footPrintFile = std::find(std::begin(footPrintFileNames), std::end(footPrintFileNames), fileName);
|
||||
if (footPrintFile == std::end(footPrintFileNames))
|
||||
{ std::string containerFileName = EncodeFileName(fileName);
|
||||
m_payloadFiles.push_back(containerFileName);
|
||||
auto fileStream = m_container->GetFile(containerFileName);
|
||||
ThrowErrorIf(Error::FileNotFound, !fileStream, "File described in blockmap not contained in OPC container");
|
||||
|
||||
// Verify file in OPC and BlockMap
|
||||
ComPtr<IAppxFile> appxFile = fileStream.As<IAppxFile>();
|
||||
APPX_COMPRESSION_OPTION compressionOpt;
|
||||
ThrowHrIfFailed(appxFile->GetCompressionOption(&compressionOpt));
|
||||
bool isUncompressed = (compressionOpt == APPX_COMPRESSION_OPTION_NONE);
|
||||
|
||||
ComPtr<IAppxFileInternal> appxFileInternal = fileStream.As<IAppxFileInternal>();
|
||||
auto sizeOnZip = appxFileInternal->GetCompressedSize();
|
||||
|
||||
auto blocks = blockMapInternal->GetBlocks(fileName);
|
||||
std::uint64_t blocksSize = 0;
|
||||
for(auto& block : blocks)
|
||||
{ // For Block elements that don't have a Size attribute, we always set its size as BLOCKMAP_BLOCK_SIZE
|
||||
// (even for the last one). The Size attribute isn't specified if the file is not compressed.
|
||||
ThrowErrorIf(Error::BlockMapSemanticError, isUncompressed && (block.compressedSize != BLOCKMAP_BLOCK_SIZE),
|
||||
"An uncompressed file has a size attribute in its Block elements");
|
||||
blocksSize += block.compressedSize;
|
||||
auto blockMapFiles = blockMapInternal->GetFileNames();
|
||||
if(m_isBundle)
|
||||
{
|
||||
// There should only be one file in the blockmap for bundles. We validate that the block map contains
|
||||
// AppxMetadata/AppxBundleManifest.xml before, so just check the size.
|
||||
ThrowErrorIfNot(Error::BlockMapSemanticError, ((blockMapFiles.size() == 1)), "Block map contains invalid files.");
|
||||
|
||||
// TODO: change this to get the files in from the bundle manifest and compare with m_container when the parsing is done.
|
||||
for (const auto& fileName : m_container->GetFileNames(FileNameOptions::PayloadOnly))
|
||||
{ auto footPrintFile = std::find(std::begin(footPrintFileNames), std::end(footPrintFileNames), fileName);
|
||||
if (footPrintFile == std::end(footPrintFileNames))
|
||||
{
|
||||
m_payloadPackages.push_back(fileName);
|
||||
m_streams[fileName] = std::move(m_container->GetFile(fileName));
|
||||
filesToProcess.erase(std::remove(filesToProcess.begin(), filesToProcess.end(), fileName), filesToProcess.end());
|
||||
}
|
||||
|
||||
if(isUncompressed)
|
||||
{ UINT64 blockMapFileSize;
|
||||
auto blockMapFile = blockMapInternal->GetFile(fileName);
|
||||
ThrowHrIfFailed(blockMapFile->GetUncompressedSize(&blockMapFileSize));
|
||||
ThrowErrorIf(Error::BlockMapSemanticError, (blockMapFileSize != sizeOnZip ),
|
||||
"Uncompressed size of the file in the block map and the OPC container don't match");
|
||||
}
|
||||
else
|
||||
{ // From Windows code:
|
||||
// The file item is compressed. There are 2 cases here:
|
||||
// 1. The compressed size of the file is the same as the total size of all compressed blocks.
|
||||
// 2. The compressed size of the file is 2 bytes more than the total size of all compressed blocks.
|
||||
// It depends on how the block compression is done. MakeAppx block compression implementation will end up
|
||||
// with case 2. However, we shouldn't block the first case since it is totally valid and 3rd party
|
||||
// implementation may end up with it.
|
||||
// The reason we created compressed file item with 2 extra bytes (03 00) is because we use Z_FULL_FLUSH
|
||||
// flag to compress every block. If we use Z_FINISH flag to compress the last block, these 2 extra bytes will
|
||||
// not be generated. The AddBlock()-->... -->AddBlock()-->Close() pattern in OPC push stack prevents the
|
||||
// deflator from knowing whether the current block is the last block. So it cannot use Z_FINISH flag for
|
||||
// the last block of the file. Note that removing the 2 extra bytes from the compressed file data will make
|
||||
// it invalid when consumed by popular zip tools like WinZip and ShellZip. So they are required for the
|
||||
// packages we created.
|
||||
ThrowErrorIfNot(Error::BlockMapSemanticError,
|
||||
(blocksSize == sizeOnZip ) // case 1
|
||||
|| (blocksSize == sizeOnZip - 2), // case 2
|
||||
"Compressed size of the file in the block map and the OPC container don't match");
|
||||
}
|
||||
m_streams[containerFileName] = m_appxBlockMap->GetValidationStream(fileName, fileStream);
|
||||
filesToProcess.erase(std::remove(filesToProcess.begin(), filesToProcess.end(), containerFileName), filesToProcess.end());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& fileName : blockMapFiles)
|
||||
{ auto footPrintFile = std::find(std::begin(footPrintFileNames), std::end(footPrintFileNames), fileName);
|
||||
if (footPrintFile == std::end(footPrintFileNames))
|
||||
{ std::string containerFileName = EncodeFileName(fileName);
|
||||
m_payloadFiles.push_back(containerFileName);
|
||||
auto fileStream = m_container->GetFile(containerFileName);
|
||||
ThrowErrorIfNot(Error::FileNotFound, fileStream, "File described in blockmap not contained in OPC container");
|
||||
VerifyFile(fileStream, fileName, blockMapInternal);
|
||||
m_streams[containerFileName] = m_appxBlockMap->GetValidationStream(fileName, fileStream);
|
||||
filesToProcess.erase(std::remove(filesToProcess.begin(), filesToProcess.end(), containerFileName), filesToProcess.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the map is not empty, there's a file in the container that didn't go to the footprint or payload
|
||||
// files. (eg. payload file missing in the AppxBlockMap.xml)
|
||||
ThrowErrorIfNot(Error::BlockMapSemanticError, (filesToProcess.empty()), "Payload file not described in AppxBlockMap.xml");
|
||||
}
|
||||
|
||||
// Verify file in OPC and BlockMap
|
||||
void AppxPackageObject::VerifyFile(const ComPtr<IStream>& stream, const std::string& fileName, const ComPtr<IAppxBlockMapInternal>& blockMapInternal)
|
||||
{
|
||||
ComPtr<IAppxFile> appxFile = stream.As<IAppxFile>();;
|
||||
APPX_COMPRESSION_OPTION compressionOpt;
|
||||
ThrowHrIfFailed(appxFile->GetCompressionOption(&compressionOpt));
|
||||
bool isUncompressed = (compressionOpt == APPX_COMPRESSION_OPTION_NONE);
|
||||
|
||||
ComPtr<IAppxFileInternal> appxFileInternal = stream.As<IAppxFileInternal>();
|
||||
auto sizeOnZip = appxFileInternal->GetCompressedSize();
|
||||
|
||||
auto blocks = blockMapInternal->GetBlocks(fileName);
|
||||
std::uint64_t blocksSize = 0;
|
||||
for(auto& block : blocks)
|
||||
{ // For Block elements that don't have a Size attribute, we always set its size as BLOCKMAP_BLOCK_SIZE
|
||||
// (even for the last one). The Size attribute isn't specified if the file is not compressed.
|
||||
ThrowErrorIf(Error::BlockMapSemanticError, isUncompressed && (block.compressedSize != BLOCKMAP_BLOCK_SIZE),
|
||||
"An uncompressed file has a size attribute in its Block elements");
|
||||
blocksSize += block.compressedSize;
|
||||
}
|
||||
|
||||
if(isUncompressed)
|
||||
{
|
||||
UINT64 blockMapFileSize;
|
||||
auto blockMapFile = blockMapInternal->GetFile(fileName);
|
||||
ThrowHrIfFailed(blockMapFile->GetUncompressedSize(&blockMapFileSize));
|
||||
ThrowErrorIf(Error::BlockMapSemanticError, (blockMapFileSize != sizeOnZip ),
|
||||
"Uncompressed size of the file in the block map and the OPC container don't match");
|
||||
}
|
||||
else
|
||||
{
|
||||
// From Windows code:
|
||||
// The file item is compressed. There are 2 cases here:
|
||||
// 1. The compressed size of the file is the same as the total size of all compressed blocks.
|
||||
// 2. The compressed size of the file is 2 bytes more than the total size of all compressed blocks.
|
||||
// It depends on how the block compression is done. MakeAppx block compression implementation will end up
|
||||
// with case 2. However, we shouldn't block the first case since it is totally valid and 3rd party
|
||||
// implementation may end up with it.
|
||||
// The reason we created compressed file item with 2 extra bytes (03 00) is because we use Z_FULL_FLUSH
|
||||
// flag to compress every block. If we use Z_FINISH flag to compress the last block, these 2 extra bytes will
|
||||
// not be generated. The AddBlock()-->... -->AddBlock()-->Close() pattern in OPC push stack prevents the
|
||||
// deflator from knowing whether the current block is the last block. So it cannot use Z_FINISH flag for
|
||||
// the last block of the file. Note that removing the 2 extra bytes from the compressed file data will make
|
||||
// it invalid when consumed by popular zip tools like WinZip and ShellZip. So they are required for the
|
||||
// packages we created.
|
||||
ThrowErrorIfNot(Error::BlockMapSemanticError,
|
||||
(blocksSize == sizeOnZip ) // case 1
|
||||
|| (blocksSize == sizeOnZip - 2), // case 2
|
||||
"Compressed size of the file in the block map and the OPC container don't match");
|
||||
}
|
||||
}
|
||||
|
||||
void AppxPackageObject::Unpack(MSIX_PACKUNPACK_OPTION options, const ComPtr<IStorageObject>& to)
|
||||
{
|
||||
auto fileNames = GetFileNames(FileNameOptions::All);
|
||||
|
@ -290,8 +408,12 @@ namespace MSIX {
|
|||
{
|
||||
std::string targetName;
|
||||
if (options & MSIX_PACKUNPACK_OPTION_CREATEPACKAGESUBFOLDER)
|
||||
{ //targetName = GetAppxManifest()->GetPackageFullName() + to->GetPathSeparator() + fileName;
|
||||
NOTIMPLEMENTED;
|
||||
{ // Don't use to->GetPathSeparator(). DirectoryObject::OpenFile created directories
|
||||
// by looking at "/" in the string. If to->GetPathSeparator() is used the subfolder with
|
||||
// the package full name won't be created on Windows, but it will on other platforms.
|
||||
// This means that we have different behaviors in non-Win platforms.
|
||||
// TODO: have the same behavior on Windows and other platforms.
|
||||
targetName = m_appxManifest->GetPackageFullName() + "/" + fileName;
|
||||
}
|
||||
else
|
||||
{ targetName = DecodeFileName(fileName);
|
||||
|
@ -304,6 +426,18 @@ namespace MSIX {
|
|||
bytesCount.QuadPart = std::numeric_limits<std::uint64_t>::max();
|
||||
ThrowHrIfFailed(sourceFile->CopyTo(targetFile.Get(), bytesCount, nullptr, nullptr));
|
||||
}
|
||||
if(m_isBundle)
|
||||
{
|
||||
for(const auto& appx : m_payloadPackages)
|
||||
{
|
||||
auto appxStream = GetFile(appx);
|
||||
auto appxFactory = m_factory.As<IAppxFactory>();
|
||||
ComPtr<IAppxPackageReader> reader;
|
||||
ThrowHrIfFailed(appxFactory->CreatePackageReader(appxStream.Get(), &reader));
|
||||
reader.As<IPackage>()->Unpack(
|
||||
static_cast<MSIX_PACKUNPACK_OPTION>(options | MSIX_PACKUNPACK_OPTION_CREATEPACKAGESUBFOLDER), to.Get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* AppxPackageObject::GetPathSeparator() { return "/"; }
|
||||
|
@ -334,7 +468,6 @@ namespace MSIX {
|
|||
}
|
||||
|
||||
ComPtr<IStream> AppxPackageObject::OpenFile(const std::string& fileName, MSIX::FileStream::Mode mode) { NOTIMPLEMENTED; }
|
||||
void AppxPackageObject::CommitChanges() { NOTIMPLEMENTED; }
|
||||
|
||||
// IAppxPackageReader
|
||||
HRESULT STDMETHODCALLTYPE AppxPackageObject::GetBlockMap(IAppxBlockMapReader** blockMapReader) noexcept
|
||||
|
@ -405,6 +538,7 @@ namespace MSIX {
|
|||
|
||||
HRESULT STDMETHODCALLTYPE AppxPackageObject::GetPayloadPackage(LPCWSTR fileName, IAppxFile **payloadPackage) noexcept try
|
||||
{
|
||||
// TODO: Implement
|
||||
return static_cast<HRESULT>(Error::NotImplemented);
|
||||
} CATCH_RETURN();
|
||||
}
|
|
@ -52,13 +52,7 @@ namespace MSIX {
|
|||
std::string path = name.substr(0, lastSlash);
|
||||
mkdirp(path);
|
||||
auto result = ComPtr<IStream>::Make<FileStream>(std::move(name), mode);
|
||||
m_streams[fileName] = result.Get(); // now cache the result in m_streams.
|
||||
return result;
|
||||
}
|
||||
|
||||
void DirectoryObject::CommitChanges()
|
||||
{
|
||||
m_streams.clear();
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -41,7 +41,7 @@ namespace MSIX {
|
|||
static std::string dot(".");
|
||||
static std::string dotdot("..");
|
||||
|
||||
std::wstring utf16Name = utf8_to_utf16(root);
|
||||
std::wstring utf16Name = utf8_to_wstring(root);
|
||||
|
||||
WIN32_FIND_DATA findFileData = {};
|
||||
std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&::FindClose)> find(
|
||||
|
@ -159,7 +159,7 @@ namespace MSIX {
|
|||
|
||||
if (!found)
|
||||
{
|
||||
std::wstring utf16Name = utf8_to_utf16(path + GetPathSeparator() + directories.front());
|
||||
std::wstring utf16Name = utf8_to_wstring(path + GetPathSeparator() + directories.front());
|
||||
if (!CreateDirectory(utf16Name.c_str(), nullptr))
|
||||
{
|
||||
auto lastError = GetLastError();
|
||||
|
@ -171,14 +171,8 @@ namespace MSIX {
|
|||
}
|
||||
name = path + GetPathSeparator() + name;
|
||||
auto result = ComPtr<IStream>::Make<FileStream>(std::move(name), mode);
|
||||
m_streams[fileName] = result.Get(); // now cache the result in m_streams.
|
||||
return result;
|
||||
}
|
||||
|
||||
void DirectoryObject::CommitChanges()
|
||||
{
|
||||
m_streams.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Don't pollute other compilation units with any of our #defs...
|
||||
|
|
|
@ -8,10 +8,7 @@
|
|||
#include "openssl/sha.h"
|
||||
|
||||
namespace MSIX {
|
||||
bool SHA256::ComputeHash(
|
||||
/*in*/ std::uint8_t *buffer,
|
||||
/*in*/ std::uint32_t cbBuffer,
|
||||
/*inout*/ std::vector<uint8_t>& hash)
|
||||
bool SHA256::ComputeHash(std::uint8_t *buffer, std::uint32_t cbBuffer, std::vector<uint8_t>& hash)
|
||||
{
|
||||
hash.resize(SHA256_DIGEST_LENGTH);
|
||||
::SHA256(buffer, cbBuffer, hash.data());
|
||||
|
|
|
@ -65,7 +65,7 @@ namespace MSIX {
|
|||
ThrowStatusIfFailed(BCryptGetProperty(
|
||||
algHandle.get(), // Handle to a CNG object
|
||||
BCRYPT_HASH_LENGTH, // Property name (null terminated unicode string)
|
||||
(PBYTE)&hashLength, // Address of the output buffer which recieves the property value
|
||||
(PBYTE)&hashLength, // Address of the output buffer which receives the property value
|
||||
sizeof(hashLength), // Size of the buffer in bytes
|
||||
&resultLength, // Number of bytes that were copied into the buffer
|
||||
0), // Flags
|
||||
|
@ -79,7 +79,7 @@ namespace MSIX {
|
|||
ThrowStatusIfFailed(BCryptCreateHash(
|
||||
algHandle.get(), // Handle to an algorithm provider
|
||||
&hashHandleT, // A pointer to a hash handle - can be a hash or hmac object
|
||||
nullptr, // Pointer to the buffer that recieves the hash/hmac object
|
||||
nullptr, // Pointer to the buffer that receives the hash/hmac object
|
||||
0, // Size of the buffer in bytes
|
||||
nullptr, // A pointer to a key to use for the hash or MAC
|
||||
0, // Size of the key in bytes
|
||||
|
|
|
@ -245,7 +245,7 @@ public:
|
|||
{
|
||||
std::vector<std::uint8_t> result;
|
||||
|
||||
auto intermediate = GetAttributeValue(attribute);;
|
||||
auto intermediate = GetAttributeValue(attribute);
|
||||
ThrowErrorIfNot(Error::InvalidParameter, (0 == (intermediate.length() % 4)), "invalid base64 encoding");
|
||||
for(std::size_t index=0; index < intermediate.length(); index += 4)
|
||||
{
|
||||
|
|
|
@ -5,33 +5,29 @@
|
|||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include "UnicodeConversion.hpp"
|
||||
|
||||
namespace MSIX {
|
||||
std::wstring utf8_to_utf16(const std::string& utf8string)
|
||||
{
|
||||
/*
|
||||
from: https://connect.microsoft.com/VisualStudio/feedback/details/1403302/unresolved-external-when-using-codecvt-utf8
|
||||
Posted by Microsoft on 2/16/2016 at 11:49 AM
|
||||
<snip>
|
||||
A workaround is to replace 'char32_t' with 'unsigned int'. In VS2013, char32_t was a typedef of 'unsigned int'.
|
||||
In VS2015, char32_t is a distinct type of it's own. Switching your use of 'char32_t' to 'unsigned int' will get
|
||||
you the old behavior from earlier versions and won't trigger a missing export error.
|
||||
|
||||
There is also a similar error to this one with 'char16_t' that can be worked around using 'unsigned short'.
|
||||
<snip>
|
||||
*/
|
||||
#ifdef WIN32
|
||||
auto converted = std::wstring_convert<std::codecvt_utf8_utf16<unsigned short>, unsigned short>{}.from_bytes(utf8string.data());
|
||||
#else
|
||||
auto converted = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(utf8string.data());
|
||||
#endif
|
||||
StringType utf8_to_utf16(const std::string& utf8string)
|
||||
{
|
||||
return StringConvert{}.from_bytes(utf8string.data());
|
||||
}
|
||||
|
||||
std::wstring utf8_to_wstring(const std::string& utf8string)
|
||||
{
|
||||
auto converted = utf8_to_utf16(utf8string);
|
||||
std::wstring result(converted.begin(), converted.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::u16string utf8_to_u16string(const std::string& utf8string)
|
||||
{
|
||||
auto converted = utf8_to_utf16(utf8string);
|
||||
std::u16string result(converted.begin(), converted.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string utf16_to_utf8(const std::wstring& utf16string)
|
||||
{
|
||||
auto converted = std::wstring_convert<std::codecvt_utf8<wchar_t>>{}.to_bytes(utf16string.data());
|
||||
|
|
|
@ -12,7 +12,7 @@ function FindBinFolder {
|
|||
elif [ -e "../../build/bin/makemsix" ]
|
||||
then
|
||||
BINDIR="../../build/bin"
|
||||
else
|
||||
else
|
||||
echo "ERROR: Could not find build binaries"
|
||||
exit 2
|
||||
fi
|
||||
|
@ -40,7 +40,7 @@ function RunTest {
|
|||
echo "expect: "$SUCCESS", got: "$RESULT
|
||||
if [ $RESULT -eq $SUCCESS ]
|
||||
then
|
||||
echo "succeeded"
|
||||
echo "succeeded"
|
||||
else
|
||||
echo "FAILED"
|
||||
TESTFAILED=1
|
||||
|
@ -77,6 +77,35 @@ RunTest 51 ./../appx/BlockMap/No_blockmap.appx -ss
|
|||
RunTest 3 ./../appx/BlockMap/Bad_Namespace_Blockmap.appx -ss
|
||||
RunTest 81 ./../appx/BlockMap/Duplicate_file_in_blockmap.appx -ss
|
||||
|
||||
# Bundle tests
|
||||
RunTest 81 ./../appx/bundles/BlockMapContainsPayloadPackage.appxbundle -ss
|
||||
RunTest 51 ./../appx/bundles/BlockMapIsMissing.appxbundle -ss
|
||||
#RunTest 0 ./../appx/bundles/BlockMapViolatesSchema.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/ContainsNeutralAndX86AppPackages.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/ContainsNoPayload.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/ContainsOnlyResourcePackages.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/ContainsTwoNeutralAppPackages.appxbundle
|
||||
RunTest 0 ./../appx/bundles/MainBundle.appxbundle -ss
|
||||
#RunTest 0 ./../appx/bundles/ManifestDeclaresAppPackageForResourcePackage.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/ManifestDeclaresResourcePackageForAppPackage.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/ManifestHasExtraPackage.appxbundle
|
||||
RunTest 52 ./../appx/bundles/ManifestIsMissing.appxbundle -ss
|
||||
#RunTest 0 ./../appx/bundles/ManifestPackageHasIncorrectArchitecture.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/ManifestPackageHasIncorrectName.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/ManifestPackageHasIncorrectPublisher.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/ManifestPackageHasIncorrectSize.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/ManifestPackageHasIncorrectVersion.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/ManifestPackageHasInvalidOffset.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/ManifestPackageHasInvalidRange.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/ManifestViolatesSchema.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/PayloadPackageHasNonAppxExtension.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/PayloadPackageIsCompressed.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/PayloadPackageIsEmpty.appxbundle.zip
|
||||
#RunTest 0 ./../appx/bundles/PayloadPackageIsNotAppxPackage.appxbundle
|
||||
#RunTest 0 ./../appx/bundles/PayloadPackageNotListedInManifest.appxbundle
|
||||
RunTest 66 ./../appx/bundles/SignedUntrustedCert-CERT_E_CHAINING.appxbundle
|
||||
RunTest 0 ./../appx/bundles/StoreSigned_Desktop_x86_x64_MoviesTV.appxbundle
|
||||
|
||||
echo "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="
|
||||
if [ $TESTFAILED -ne 0 ]
|
||||
then
|
||||
|
|
|
@ -93,6 +93,35 @@ RunTest 0x8bad0033 .\..\appx\BlockMap\No_blockmap.appx "-ss"
|
|||
RunTest 0x8bad1003 .\..\appx\BlockMap\Bad_Namespace_Blockmap.appx "-ss"
|
||||
RunTest 0x8bad0051 .\..\appx\BlockMap\Duplicate_file_in_blockmap.appx "-ss"
|
||||
|
||||
# Bundle tests
|
||||
RunTest 0x8bad0051 .\..\appx\bundles\BlockMapContainsPayloadPackage.appxbundle "-ss"
|
||||
RunTest 0x8bad0033 .\..\appx\bundles\BlockMapIsMissing.appxbundle "-ss"
|
||||
#RunTest 0x00000000 .\..\appx\bundles\BlockMapViolatesSchema.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ContainsNeutralAndX86AppPackages.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ContainsNoPayload.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ContainsOnlyResourcePackages.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ContainsTwoNeutralAppPackages.appxbundle
|
||||
RunTest 0x00000000 .\..\appx\bundles\MainBundle.appxbundle "-ss"
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ManifestDeclaresAppPackageForResourcePackage.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ManifestDeclaresResourcePackageForAppPackage.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ManifestHasExtraPackage.appxbundle
|
||||
RunTest 0x8bad0034 .\..\appx\bundles\ManifestIsMissing.appxbundle "-ss"
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ManifestPackageHasIncorrectArchitecture.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ManifestPackageHasIncorrectName.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ManifestPackageHasIncorrectPublisher.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ManifestPackageHasIncorrectSize.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ManifestPackageHasIncorrectVersion.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ManifestPackageHasInvalidOffset.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ManifestPackageHasInvalidRange.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\ManifestViolatesSchema.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\PayloadPackageHasNonAppxExtension.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\PayloadPackageIsCompressed.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\PayloadPackageIsEmpty.appxbundle.zip
|
||||
#RunTest 0x00000000 .\..\appx\bundles\PayloadPackageIsNotAppxPackage.appxbundle
|
||||
#RunTest 0x00000000 .\..\appx\bundles\PayloadPackageNotListedInManifest.appxbundle
|
||||
RunTest 0x8bad0042 .\..\appx\bundles\SignedUntrustedCert-CERT_E_CHAINING.appxbundle
|
||||
RunTest 0x00000000 .\..\appx\bundles\StoreSigned_Desktop_x86_x64_MoviesTV.appxbundle
|
||||
|
||||
CleanupUnpackFolder
|
||||
|
||||
write-host "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="
|
||||
|
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичные данные
test/appx/bundles/ManifestDeclaresAppPackageForResourcePackage.appxbundle
Normal file
Двоичные данные
test/appx/bundles/ManifestDeclaresAppPackageForResourcePackage.appxbundle
Normal file
Двоичный файл не отображается.
Двоичные данные
test/appx/bundles/ManifestDeclaresResourcePackageForAppPackage.appxbundle
Normal file
Двоичные данные
test/appx/bundles/ManifestDeclaresResourcePackageForAppPackage.appxbundle
Normal file
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -78,6 +78,7 @@ Java_com_microsoft_androidbvt_MainActivity_RunTests(JNIEnv* env, jobject /* this
|
|||
std::string filePath = GetStringPathFromJString(env, jFilePath);
|
||||
CopyFilesFromAssets(env, assetManager, filePath, "");
|
||||
CopyFilesFromAssets(env, assetManager, filePath, "BlockMap");
|
||||
CopyFilesFromAssets(env, assetManager, filePath, "bundles");
|
||||
signed long hr = RunTests(const_cast<char*>(filePath.c_str()), const_cast<char*>(filePath.c_str()));
|
||||
if(hr == 0)
|
||||
{
|
||||
|
|
|
@ -381,6 +381,35 @@ static HRESULT RunTestsInternal(std::string source, std::string target)
|
|||
hr = RunTest(source + "BlockMap/Bad_Namespace_Blockmap.appx", unpackFolder, ss, 4099);
|
||||
hr = RunTest(source + "BlockMap/Duplicate_file_in_blockmap.appx", unpackFolder, ss, 81);
|
||||
|
||||
// Bundle tests
|
||||
hr = RunTest(source + "bundles/BlockMapContainsPayloadPackage.appxbundle", unpackFolder, ss, 81);
|
||||
hr = RunTest(source + "bundles/BlockMapIsMissing.appxbundle", unpackFolder, ss, 51);
|
||||
//hr = RunTest(source + "bundles/BlockMapViolatesSchema.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/ContainsNeutralAndX86AppPackages.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/ContainsNoPayload.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/ContainsOnlyResourcePackages.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/ContainsTwoNeutralAppPackages.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/MainBundle.appxbundle", unpackFolder, ss, 0 );
|
||||
//hr = RunTest(source + "bundles/ManifestDeclaresAppPackageForResourcePackage.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/ManifestDeclaresResourcePackageForAppPackage.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/ManifestHasExtraPackage.appxbundle", unpackFolder, full, 0);
|
||||
hr = RunTest(source + "bundles/ManifestIsMissing.appxbundle", unpackFolder, ss, 52);
|
||||
//hr = RunTest(source + "bundles/ManifestPackageHasIncorrectArchitecture.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/ManifestPackageHasIncorrectName.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/ManifestPackageHasIncorrectPublisher.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/ManifestPackageHasIncorrectSize.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/ManifestPackageHasIncorrectVersion.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/ManifestPackageHasInvalidOffset.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/ManifestPackageHasInvalidRange.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/ManifestViolatesSchema.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/PayloadPackageHasNonAppxExtension.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/PayloadPackageIsCompressed.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/PayloadPackageIsEmpty.appxbundle.zip", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/PayloadPackageIsNotAppxPackage.appxbundle", unpackFolder, full, 0);
|
||||
//hr = RunTest(source + "bundles/PayloadPackageNotListedInManifest.appxbundle", unpackFolder, full, 0);
|
||||
hr = RunTest(source + "bundles/SignedUntrustedCert-CERT_E_CHAINING.appxbundle", unpackFolder, full, 66);
|
||||
//hr = RunTest(source + "bundles/StoreSigned_Desktop_x86_x64_MoviesTV.appxbundle", unpackFolder, full, 0);
|
||||
|
||||
std::cout << "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=" << std::endl;
|
||||
if(g_TestFailed)
|
||||
{ std::cout << " FAILED " << std::endl;
|
||||
|
|
Загрузка…
Ссылка в новой задаче