Merged PR 1206790: Merge feature to master
Bunch of bug fixes, and enable Linux and x64 Windows builds. Related work items: #14874780, #14883294, #14942409, #14942463, #14986525, #14998095
This commit is contained in:
Коммит
009166a7cb
|
@ -397,4 +397,5 @@ __pycache__/
|
|||
lib/zlib
|
||||
# Files generated by CMake
|
||||
src/inc/AppxCerts.hpp
|
||||
src/inc/AppxBlockMapSchemas.hpp
|
||||
src/inc/AppxBlockMapSchemas.hpp
|
||||
src/inc/ContentTypesSchemas.hpp
|
|
@ -42,7 +42,7 @@
|
|||
|
||||
<xs:simpleType name="ST_ResourceReference">
|
||||
<xs:restriction base="ST_NonEmptyString">
|
||||
<xs:pattern value="\bms-resource:.{1,256}"/>
|
||||
<xs:pattern value="ms-resource:.{1,256}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
@ -127,7 +127,7 @@
|
|||
<xs:pattern value=".+\.([Dd][Ll][Ll])"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
||||
<xs:simpleType name="ST_ImageFile">
|
||||
<xs:restriction base="ST_FileName">
|
||||
<xs:pattern value=".+\.((jpg)|(png)|(jpeg))"/>
|
||||
|
@ -306,7 +306,7 @@
|
|||
<xs:enumeration value="x86a64"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
||||
<xs:simpleType name="ST_ArchitectureUnrestricted">
|
||||
<xs:restriction base="ST_AsciiIdentifier">
|
||||
<xs:maxLength value="7"/>
|
||||
|
@ -547,7 +547,7 @@
|
|||
<xs:enumeration value="windows.comInterface"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
||||
<xs:simpleType name="ST_PackageExtensionCategory_Foundation">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="windows.activatableClass.inProcessServer"/>
|
||||
|
|
|
@ -206,7 +206,7 @@
|
|||
<xs:element name="GameExplorer" type="CT_GameExplorer"/>
|
||||
<!--Certificates DEH-->
|
||||
<xs:element name="Certificates" type="CT_Certificates"/>
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:choice>
|
||||
|
@ -274,7 +274,7 @@
|
|||
<xs:selector xpath="m:Extensions/m:Extension/m:AutoPlayDevice/m:LaunchAction"/>
|
||||
<xs:field xpath="@Verb"/>
|
||||
</xs:unique>
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here (Uniqueness checks)]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:element>
|
||||
|
@ -286,7 +286,7 @@
|
|||
<xs:maxLength value="256"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
||||
<!-- VISUAL ELEMENTS SCHEMA -->
|
||||
<xs:complexType name="CT_VisualElements">
|
||||
<xs:all>
|
||||
|
@ -403,7 +403,7 @@
|
|||
<xs:element name="FileSavePicker" type="CT_FilePicker"/>
|
||||
<!--Background Tasks DEH-->
|
||||
<xs:element name="BackgroundTasks" type="CT_BackgroundTasks"/>
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:choice>
|
||||
|
@ -471,14 +471,14 @@
|
|||
|
||||
<xs:simpleType name="ST_DisplayName">
|
||||
<xs:restriction base="ST_NonEmptyString">
|
||||
<xs:pattern value="\bms-resource:.{1,256}"/>
|
||||
<xs:pattern value="ms-resource:.{1,256}"/>
|
||||
<xs:pattern value=".{1,256}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="ST_ShortDisplayName">
|
||||
<xs:restriction base="ST_NonEmptyString">
|
||||
<xs:pattern value="\bms-resource:.{1,256}"/>
|
||||
<xs:pattern value="ms-resource:.{1,256}"/>
|
||||
<xs:pattern value=".{1,40}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
@ -606,7 +606,7 @@
|
|||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here]
|
||||
@@END_SDKSPLIT -->
|
||||
<!--DEPLOYMENT EXTENSION HANDLERS (DEH) SCHEMAS-->
|
||||
|
@ -894,7 +894,7 @@
|
|||
<xs:attribute name="GameDefinitionContainer" type="ST_FileName" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH schema here]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:schema>
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
Please contact 'manifest' alias if you need to request an addition
|
||||
or edit to the schema.
|
||||
|
||||
When modifying this file, please keep it consistent with the
|
||||
|
||||
When modifying this file, please keep it consistent with the
|
||||
Schema\WindowsSDK\AppxManifestSchema2010_v2.xsd file
|
||||
|
||||
@@END_SDKSPLIT -->
|
||||
|
@ -235,7 +235,7 @@
|
|||
<xs:element name="ProxyStub" type="CT_ProxyStub"/>
|
||||
<!--Certificates DEH-->
|
||||
<xs:element name="Certificates" type="CT_Certificates"/>
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:choice>
|
||||
|
@ -302,7 +302,7 @@
|
|||
<xs:selector xpath="m:Extensions/m:Extension/m:AutoPlayDevice/m:LaunchAction"/>
|
||||
<xs:field xpath="@Verb"/>
|
||||
</xs:unique>
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here (Uniqueness checks)]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:element>
|
||||
|
@ -314,7 +314,7 @@
|
|||
<xs:maxLength value="256"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
||||
<!-- VISUAL ELEMENTS SCHEMA -->
|
||||
<xs:attributeGroup name="VisualElementsBaseAttributes">
|
||||
<xs:attribute name="DisplayName" type="ST_DisplayName" use="required"/>
|
||||
|
@ -441,7 +441,7 @@
|
|||
<xs:element name="FileSavePicker" type="CT_FilePicker"/>
|
||||
<!--Background Tasks DEH-->
|
||||
<xs:element name="BackgroundTasks" type="CT_BackgroundTasks"/>
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:choice>
|
||||
|
@ -514,14 +514,14 @@
|
|||
|
||||
<xs:simpleType name="ST_DisplayName">
|
||||
<xs:restriction base="ST_NonEmptyString">
|
||||
<xs:pattern value="\bms-resource:.{1,256}"/>
|
||||
<xs:pattern value="ms-resource:.{1,256}"/>
|
||||
<xs:pattern value=".{1,256}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="ST_ShortDisplayName">
|
||||
<xs:restriction base="ST_NonEmptyString">
|
||||
<xs:pattern value="\bms-resource:.{1,256}"/>
|
||||
<xs:pattern value="ms-resource:.{1,256}"/>
|
||||
<xs:pattern value=".{1,40}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
@ -649,7 +649,7 @@
|
|||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here]
|
||||
@@END_SDKSPLIT -->
|
||||
<!--DEPLOYMENT EXTENSION HANDLERS (DEH) SCHEMAS-->
|
||||
|
@ -938,7 +938,7 @@
|
|||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH schema here]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:schema>
|
||||
|
|
|
@ -242,7 +242,7 @@
|
|||
<xs:element name="ProxyStub" type="CT_ProxyStub"/>
|
||||
<!--Certificates DEH-->
|
||||
<xs:element name="Certificates" type="CT_Certificates"/>
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:choice>
|
||||
|
@ -313,7 +313,7 @@
|
|||
<xs:selector xpath="m:Extensions/m:Extension/m:AutoPlayDevice/m:LaunchAction"/>
|
||||
<xs:field xpath="@Verb"/>
|
||||
</xs:unique>
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here (Uniqueness checks)]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:element>
|
||||
|
@ -325,7 +325,7 @@
|
|||
<xs:maxLength value="256"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
||||
<!-- VISUAL ELEMENTS SCHEMA -->
|
||||
<xs:attributeGroup name="VisualElementsBaseAttributes">
|
||||
<xs:attribute name="DisplayName" type="ST_DisplayName" use="required"/>
|
||||
|
@ -452,7 +452,7 @@
|
|||
<xs:element name="FileSavePicker" type="CT_FilePicker"/>
|
||||
<!--Background Tasks DEH-->
|
||||
<xs:element name="BackgroundTasks" type="CT_BackgroundTasks"/>
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:choice>
|
||||
|
@ -525,14 +525,14 @@
|
|||
|
||||
<xs:simpleType name="ST_DisplayName">
|
||||
<xs:restriction base="ST_NonEmptyString">
|
||||
<xs:pattern value="\bms-resource:.{1,256}"/>
|
||||
<xs:pattern value="ms-resource:.{1,256}"/>
|
||||
<xs:pattern value=".{1,256}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="ST_ShortDisplayName">
|
||||
<xs:restriction base="ST_NonEmptyString">
|
||||
<xs:pattern value="\bms-resource:.{1,256}"/>
|
||||
<xs:pattern value="ms-resource:.{1,256}"/>
|
||||
<xs:pattern value=".{1,40}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
@ -660,7 +660,7 @@
|
|||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here]
|
||||
@@END_SDKSPLIT -->
|
||||
<!--DEPLOYMENT EXTENSION HANDLERS (DEH) SCHEMAS-->
|
||||
|
@ -949,7 +949,7 @@
|
|||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH schema here]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:schema>
|
||||
|
|
|
@ -236,7 +236,7 @@
|
|||
<xs:element name="ProxyStub" type="CT_ProxyStub"/>
|
||||
<!--Certificates DEH-->
|
||||
<xs:element name="Certificates" type="CT_Certificates"/>
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:choice>
|
||||
|
@ -303,7 +303,7 @@
|
|||
<xs:selector xpath="m:Extensions/m:Extension/m:AutoPlayDevice/m:LaunchAction"/>
|
||||
<xs:field xpath="@Verb"/>
|
||||
</xs:unique>
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here (Uniqueness checks)]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:element>
|
||||
|
@ -315,7 +315,7 @@
|
|||
<xs:maxLength value="256"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
||||
<!-- VISUAL ELEMENTS SCHEMA -->
|
||||
<xs:attributeGroup name="VisualElementsBaseAttributes">
|
||||
<xs:attribute name="DisplayName" type="ST_DisplayName" use="required"/>
|
||||
|
@ -442,7 +442,7 @@
|
|||
<xs:element name="FileSavePicker" type="CT_FilePicker"/>
|
||||
<!--Background Tasks DEH-->
|
||||
<xs:element name="BackgroundTasks" type="CT_BackgroundTasks"/>
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:choice>
|
||||
|
@ -515,14 +515,14 @@
|
|||
|
||||
<xs:simpleType name="ST_DisplayName">
|
||||
<xs:restriction base="ST_NonEmptyString">
|
||||
<xs:pattern value="\bms-resource:.{1,256}"/>
|
||||
<xs:pattern value="ms-resource:.{1,256}"/>
|
||||
<xs:pattern value=".{1,256}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="ST_ShortDisplayName">
|
||||
<xs:restriction base="ST_NonEmptyString">
|
||||
<xs:pattern value="\bms-resource:.{1,256}"/>
|
||||
<xs:pattern value="ms-resource:.{1,256}"/>
|
||||
<xs:pattern value=".{1,40}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
@ -650,7 +650,7 @@
|
|||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH here]
|
||||
@@END_SDKSPLIT -->
|
||||
<!--DEPLOYMENT EXTENSION HANDLERS (DEH) SCHEMAS-->
|
||||
|
@ -939,7 +939,7 @@
|
|||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
<!-- @@BEGIN_SDKSPLIT
|
||||
[Add your DEH schema here]
|
||||
@@END_SDKSPLIT -->
|
||||
</xs:schema>
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<xs:schema xmlns="http://schemas.openxmlformats.org/package/2006/content-types" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://schemas.openxmlformats.org/package/2006/content-types" elementFormDefault="qualified" attributeFormDefault="unqualified" blockDefault="#all">
|
||||
<!--
|
||||
Individual patterns for content type grammar
|
||||
|
||||
For better readability, each pattern using numbers is also described in a comment using
|
||||
one of the following pattern designators.
|
||||
-->
|
||||
<!--DEFINE [media-type] "([type]/[subtype]([LWS]*;[LWS]*[parameter])*)" -->
|
||||
<!--DEFINE [parameter] "([attribute]=[value])" -->
|
||||
<!--DEFINE [attribute] "([token])" -->
|
||||
<!--DEFINE [value] "([token]|[quoted-string])" -->
|
||||
<!--DEFINE [subtype] "([token])" -->
|
||||
<!--DEFINE [type] "([token])" -->
|
||||
<!--DEFINE [quoted-string] "("([qdtext]|[quoted-pair])*")" -->
|
||||
<!--DEFINE [qdtext] "([\p{IsLatin-1Supplement}\p{IsBasicLatin}-[\p{Cc}[DEL]"\n\r]]|[LWS])" -->
|
||||
<!--DEFINE [quoted-pair] "(\\[CHAR])" -->
|
||||
<!--DEFINE [LWS] "([SP]+)" -->
|
||||
<!--DEFINE [CHAR] "[\p{IsBasicLatin}]" -->
|
||||
<!--DEFINE [separators] "[\(\)<>@,;:\\"/\[\]\?=\{\} \t]" -->
|
||||
<!--DEFINE [token] "(([\p{IsBasicLatin}-[\p{Cc}[DEL]\(\)<>@,;:\\"/\[\]\?=\{\}\s\t]])+)" -->
|
||||
<!--DEFINE [SP] "\s" -->
|
||||
<!--DEFINE [DEL] "" -->
|
||||
|
||||
<xs:element name="Types" type="CT_Types" />
|
||||
<xs:element name="Default" type="CT_Default" />
|
||||
<xs:element name="Override" type="CT_Override" />
|
||||
|
||||
<xs:complexType name="CT_Types">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="Default" />
|
||||
<xs:element ref="Override" />
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="CT_Default">
|
||||
<xs:attribute name="Extension" type="ST_Extension" use="required" />
|
||||
<xs:attribute name="ContentType" type="ST_ContentType" use="required" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="CT_Override">
|
||||
<xs:attribute name="ContentType" type="ST_ContentType" use="required" />
|
||||
<xs:attribute name="PartName" type="xs:anyURI" use="required" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="ST_ContentType">
|
||||
<xs:restriction base="xs:string">
|
||||
<!--
|
||||
<xs:pattern value="[media-type]"/>
|
||||
-->
|
||||
<xs:pattern value="(((([\p{IsBasicLatin}-[\p{Cc}\(\)<>@,;:\\"/\[\]\?=\{\}\s\t]])+))/((([\p{IsBasicLatin}-[\p{Cc}\(\)<>@,;:\\"/\[\]\?=\{\}\s\t]])+))((\s+)*;(\s+)*(((([\p{IsBasicLatin}-[\p{Cc}\(\)<>@,;:\\"/\[\]\?=\{\}\s\t]])+))=((([\p{IsBasicLatin}-[\p{Cc}\(\)<>@,;:\\"/\[\]\?=\{\}\s\t]])+)|("(([\p{IsLatin-1Supplement}\p{IsBasicLatin}-[\p{Cc}"\n\r]]|(\s+))|(\\[\p{IsBasicLatin}]))*"))))*)" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="ST_Extension">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="([!$&'\(\)\*\+,:=]|(%[0-9a-fA-F][0-9a-fA-F])|[:@]|[a-zA-Z0-9\-_~])+" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,8 @@
|
|||
REM HUZZA FOR: https://dmerej.info/blog/post/cmake-visual-studio-and-the-command-line/
|
||||
if not exist .vs md .vs
|
||||
cd .vs
|
||||
if exist CMakeFiles rd /s /q CMakeFiles
|
||||
if exist CMakeCache.txt del CMakeCache.txt
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
cmake -DWIN32=on -DCMAKE_BUILD_TYPE=Release -G"NMake Makefiles" ..
|
||||
nmake
|
|
@ -3,6 +3,10 @@
|
|||
#include <string>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
#ifdef WIN32
|
||||
#define UNICODE
|
||||
|
@ -68,6 +72,98 @@ std::wstring utf8_to_utf16(const std::string& utf8string)
|
|||
return result;
|
||||
}
|
||||
|
||||
// describes an option to a command that the user may specify
|
||||
struct Option
|
||||
{
|
||||
using CBF = std::function<bool(const std::string& value)>;
|
||||
|
||||
Option(bool param, const std::string& help, CBF callback): Help(help), Callback(callback), TakesParameter(param)
|
||||
{}
|
||||
|
||||
bool TakesParameter;
|
||||
std::string Name;
|
||||
std::string Help;
|
||||
CBF Callback;
|
||||
};
|
||||
|
||||
// Tracks the state of the current parse operation as well as implements input validation
|
||||
struct State
|
||||
{
|
||||
bool CreatePackageSubfolder()
|
||||
{
|
||||
unpackOptions = static_cast<APPX_PACKUNPACK_OPTION>(unpackOptions | APPX_PACKUNPACK_OPTION::APPX_PACKUNPACK_OPTION_CREATEPACKAGESUBFOLDER);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkipManifestValidation()
|
||||
{
|
||||
validationOptions = static_cast<APPX_VALIDATION_OPTION>(validationOptions | APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_SKIPAPPXMANIFEST);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkipSignature()
|
||||
{
|
||||
validationOptions = static_cast<APPX_VALIDATION_OPTION>(validationOptions | APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_SKIPSIGNATURE);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AllowSignatureOriginUnknown()
|
||||
{
|
||||
validationOptions = static_cast<APPX_VALIDATION_OPTION>(validationOptions | APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_ALLOWSIGNATUREORIGINUNKNOWN);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetPackageName(const std::string& name)
|
||||
{
|
||||
if (!packageName.empty() || name.empty()) { return false; }
|
||||
packageName = utf8_to_utf16(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetDirectoryName(const std::string& name)
|
||||
{
|
||||
if (!directoryName.empty() || name.empty()) { return false; }
|
||||
directoryName = utf8_to_utf16(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring packageName;
|
||||
std::wstring directoryName;
|
||||
APPX_VALIDATION_OPTION validationOptions = APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_FULL;
|
||||
APPX_PACKUNPACK_OPTION unpackOptions = APPX_PACKUNPACK_OPTION::APPX_PACKUNPACK_OPTION_NONE;
|
||||
};
|
||||
|
||||
// Displays contextual formatted help to the user.
|
||||
int Help(char* toolName, std::map<std::string, Option>& options)
|
||||
{
|
||||
std::cout << std::endl;
|
||||
std::cout << "Usage:" << std::endl;
|
||||
std::cout << "------" << std::endl;
|
||||
std::cout << "\t" << toolName << " -p <package> -d <directory> [options] " << std::endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << "Description:" << std::endl;
|
||||
std::cout << "------------" << std::endl;
|
||||
std::cout << "\tExtracts all files within an app package at the input <package> name to the" << std::endl;
|
||||
std::cout << "\tspecified output <directory>. The output has the same directory structure " << std::endl;
|
||||
std::cout << "\tas the package." << std::endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << "Options:" << std::endl;
|
||||
std::cout << "--------" << std::endl;
|
||||
|
||||
for (const auto& option : options)
|
||||
{
|
||||
std::cout << "\t" << std::left << std::setfill(' ') << std::setw(5) <<
|
||||
option.first << ": " << option.second.Help << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// error text if the user provided underspecified input
|
||||
void Error(char* toolName)
|
||||
{
|
||||
std::cout << toolName << ": error : Missing required options. Use '-?' for more details." << std::endl;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
// TODO: paths coming in SHOULD have platform-appropriate path separators
|
||||
void replace(std::wstring& input, const wchar_t oldchar, const wchar_t newchar)
|
||||
|
@ -147,7 +243,7 @@ struct FootprintFilesType
|
|||
|
||||
// Types of footprint files in an app package
|
||||
const int FootprintFilesCount = 4;
|
||||
const FootprintFilesType footprintFilesType[FootprintFilesCount] = {
|
||||
FootprintFilesType footprintFilesType[FootprintFilesCount] = {
|
||||
{APPX_FOOTPRINT_FILE_TYPE_MANIFEST, "manifest", true },
|
||||
{APPX_FOOTPRINT_FILE_TYPE_BLOCKMAP, "block map", true },
|
||||
{APPX_FOOTPRINT_FILE_TYPE_SIGNATURE, "digital signature", true },
|
||||
|
@ -198,13 +294,13 @@ void STDMETHODCALLTYPE MyFree(LPVOID pv) { std::free(pv); }
|
|||
// reader
|
||||
// On success, receives the created instance of IAppxPackageReader.
|
||||
//
|
||||
HRESULT GetPackageReader(LPCWSTR inputFileName, IAppxPackageReader** package)
|
||||
HRESULT GetPackageReader(State& state, IAppxPackageReader** package)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
ComPtr<IAppxFactory> appxFactory;
|
||||
ComPtr<IStream> inputStream;
|
||||
|
||||
hr = CreateStreamOnFileUTF16(inputFileName, true, &inputStream);
|
||||
hr = CreateStreamOnFileUTF16(state.packageName.c_str(), true, &inputStream);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// On Win32 platforms CoCreateAppxFactory defaults to CoTaskMemAlloc/CoTaskMemFree
|
||||
|
@ -213,7 +309,7 @@ HRESULT GetPackageReader(LPCWSTR inputFileName, IAppxPackageReader** package)
|
|||
hr = CoCreateAppxFactoryWithHeap(
|
||||
MyAllocate,
|
||||
MyFree,
|
||||
APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_SKIPAPPXMANIFEST,
|
||||
state.validationOptions,
|
||||
&appxFactory);
|
||||
|
||||
// Create a new package reader using the factory. For
|
||||
|
@ -361,33 +457,88 @@ HRESULT ExtractPayloadFiles(IAppxPackageReader* package, LPCWSTR outputPath)
|
|||
return hr;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
// Parses argc/argv input via commands into state, and extract the package.
|
||||
int ParseAndRun(std::map<std::string, Option>& options, State& state, int argc, char* argv[])
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
auto ParseInput = [&]()->bool {
|
||||
int index = 1;
|
||||
while (index < argc)
|
||||
{
|
||||
auto option = options.find(argv[index]);
|
||||
if (option == options.end()) { return false; }
|
||||
char const *parameter = "";
|
||||
if (option->second.TakesParameter)
|
||||
{
|
||||
if (++index == argc) { break; }
|
||||
parameter = argv[index];
|
||||
}
|
||||
if (!option->second.Callback(parameter)) { return false; }
|
||||
++index;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
std::wstring fileName = utf8_to_utf16(argv[1]);
|
||||
std::wstring pathName = utf8_to_utf16(argv[2]);
|
||||
if (!ParseInput()) { return Help(argv[0], options); }
|
||||
if (state.packageName.empty() || state.directoryName.empty())
|
||||
{ Error(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
// Create a package using the file name in argv[1]
|
||||
ComPtr<IAppxPackageReader> package;
|
||||
hr = GetPackageReader(fileName.c_str(), &package);
|
||||
hr = GetPackageReader(state, &package);
|
||||
|
||||
// Print information about all footprint files, and extract them to disk
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ExtractFootprintFiles(package.Get(), pathName.c_str());
|
||||
hr = ExtractFootprintFiles(package.Get(), state.directoryName.c_str());
|
||||
}
|
||||
|
||||
// Print information about all payload files, and extract them to disk
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ExtractPayloadFiles(package.Get(), pathName.c_str());
|
||||
hr = ExtractPayloadFiles(package.Get(), state.directoryName.c_str());
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// TODO: Tell a more specific reason why the faiulre occurred.
|
||||
std::printf("\nError %X occurred while extracting the appx package\n", static_cast<int>(hr));
|
||||
}
|
||||
|
||||
return static_cast<int>(hr);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
State state;
|
||||
std::map<std::string, Option> options = {
|
||||
{ "-p", Option(true, "REQUIRED, specify input package name.",
|
||||
[&](const std::string& name) { return state.SetPackageName(name); })
|
||||
},
|
||||
{ "-d", Option(true, "REQUIRED, specify output directory name.",
|
||||
[&](const std::string& name) { return state.SetDirectoryName(name); })
|
||||
},
|
||||
{ "-pfn", Option(false, "Unpacks all files to a subdirectory under the specified output path, named after the package full name.",
|
||||
[&](const std::string&) { return state.CreatePackageSubfolder(); })
|
||||
},
|
||||
{ "-mv", Option(false, "Skips manifest validation. By default manifest validation is enabled.",
|
||||
[&](const std::string&) { return state.SkipManifestValidation(); })
|
||||
},
|
||||
{ "-sv", Option(false, "Skips signature validation. By default signature validation is enabled.",
|
||||
[&](const std::string&) { return state.AllowSignatureOriginUnknown(); })
|
||||
},
|
||||
{ "-ss", Option(false, "Skips enforcement of signed packages. By default packages must be signed.",
|
||||
[&](const std::string&)
|
||||
{ footprintFilesType[2].isRequired = false;
|
||||
return state.SkipSignature();
|
||||
})
|
||||
},
|
||||
{ "-?", Option(false, "Displays this help text.",
|
||||
[&](const std::string&) { return false; })
|
||||
}
|
||||
};
|
||||
|
||||
auto result = ParseAndRun(options, state, argc, argv);
|
||||
if (result != 0)
|
||||
{
|
||||
std::cout << "Error: " << std::hex << result << " while extracting the appx package" <<std::endl;
|
||||
}
|
||||
return result;
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
#include "HashStream.hpp"
|
||||
#include "ComHelper.hpp"
|
||||
#include "SHA256.hpp"
|
||||
#include "AppxFactory.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
@ -35,7 +36,8 @@ namespace xPlat {
|
|||
class BlockMapStream : public StreamBase
|
||||
{
|
||||
public:
|
||||
BlockMapStream(IStream* stream, std::vector<Block>& blocks) : m_stream(stream)
|
||||
BlockMapStream(IxPlatFactory* factory, std::string decodedName, IStream* stream, std::vector<Block>& blocks)
|
||||
: m_factory(factory), m_decodedName(decodedName), m_stream(stream)
|
||||
{
|
||||
// Determine overall stream size
|
||||
ULARGE_INTEGER uli;
|
||||
|
@ -141,7 +143,7 @@ namespace xPlat {
|
|||
|
||||
HRESULT STDMETHODCALLTYPE GetName(LPWSTR* fileName) override
|
||||
{
|
||||
return ResultOf([&]{ return m_stream.As<IAppxFile>()->GetName(fileName); });
|
||||
return m_factory->MarshalOutString(m_decodedName, fileName);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetContentType(LPWSTR* contentType) override
|
||||
|
@ -159,6 +161,8 @@ namespace xPlat {
|
|||
std::vector<BlockPlusStream> m_blockStreams;
|
||||
std::uint64_t m_relativePosition;
|
||||
std::uint64_t m_streamSize;
|
||||
std::string m_decodedName;
|
||||
ComPtr<IStream> m_stream;
|
||||
IxPlatFactory* m_factory;
|
||||
};
|
||||
}
|
|
@ -9,12 +9,12 @@
|
|||
|
||||
|
||||
namespace xPlat {
|
||||
|
||||
|
||||
// This represents a subset of a Stream
|
||||
class RangeStream : public StreamBase
|
||||
{
|
||||
public:
|
||||
RangeStream(std::uint64_t offset, std::uint64_t size, IStream* stream) :
|
||||
RangeStream(std::uint64_t offset, std::uint64_t size, IStream* stream) :
|
||||
m_offset(offset),
|
||||
m_size(size),
|
||||
m_stream(stream)
|
||||
|
@ -40,7 +40,7 @@ namespace xPlat {
|
|||
//TODO: We need to constrain newPos so that it can't exceed the end of the stream
|
||||
ULARGE_INTEGER pos = { 0 };
|
||||
m_stream->Seek(newPos, Reference::START, &pos);
|
||||
m_relativePosition = std::min((pos.QuadPart - m_offset), m_size);
|
||||
m_relativePosition = std::min(static_cast<std::uint64_t>(pos.QuadPart - m_offset), m_size);
|
||||
if (newPosition) { newPosition->QuadPart = m_relativePosition; }
|
||||
});
|
||||
}
|
||||
|
|
|
@ -27,7 +27,8 @@ namespace xPlat {
|
|||
/*in*/ APPX_VALIDATION_OPTION option,
|
||||
/*in*/ IStream *stream,
|
||||
/*inout*/ std::map<xPlat::AppxSignatureObject::DigestName, xPlat::AppxSignatureObject::Digest>& digests,
|
||||
/*inout*/ SignatureOrigin& origin);
|
||||
/*inout*/ SignatureOrigin& origin,
|
||||
/*inout*/ std::string& publisher);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace xPlat {
|
|||
bool hasValue = !attributeValue.empty();
|
||||
std::uint32_t value = 0;
|
||||
if (hasValue) { value = static_cast<std::uint32_t>(std::stoul(attributeValue)); }
|
||||
return value;
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::string GetName(XERCES_CPP_NAMESPACE::DOMElement* element)
|
||||
|
@ -67,7 +67,7 @@ namespace xPlat {
|
|||
XercesXMLChPtr nameAttr(XMLString::transcode("Hash"));
|
||||
XMLSize_t len = 0;
|
||||
XercesXMLBytePtr decodedData(XERCES_CPP_NAMESPACE::Base64::decodeToXMLByte(
|
||||
element->getAttribute(nameAttr.Get()),
|
||||
element->getAttribute(nameAttr.Get()),
|
||||
&len));
|
||||
std::vector<std::uint8_t> result(len);
|
||||
for(XMLSize_t index=0; index < len; index++)
|
||||
|
@ -83,7 +83,7 @@ namespace xPlat {
|
|||
result.hash = GetDigestData(element);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
AppxBlockMapObject::AppxBlockMapObject(IxPlatFactory* factory, ComPtr<IStream>& stream) : m_factory(factory), m_stream(stream)
|
||||
{
|
||||
auto dom = ComPtr<IXmlObject>::Make<XmlObject>(stream, &blockMapSchema);
|
||||
|
@ -103,8 +103,13 @@ namespace xPlat {
|
|||
fileResult->snapshotItem(i);
|
||||
auto fileNode = static_cast<DOMElement*>(fileResult->getNodeValue());
|
||||
|
||||
auto name = GetName(fileNode);
|
||||
ThrowErrorIf(Error::BlockMapSemanticError, (name == "[Content_Types].xml"), "[Content_Types].xml cannot be in the AppxBlockMap.xml file");
|
||||
auto existing = m_blockMap.find(name);
|
||||
ThrowErrorIf(Error::BlockMapSemanticError, (existing != m_blockMap.end()), "duplicate file name specified.");
|
||||
|
||||
// Get blocks elements
|
||||
XercesXMLChPtr blockXPath(XMLString::transcode("./Block"));
|
||||
XercesXMLChPtr blockXPath(XMLString::transcode("./Block"));
|
||||
XercesPtr<DOMXPathResult> blockResult = dom->Document()->evaluate(
|
||||
blockXPath.Get(),
|
||||
fileNode,
|
||||
|
@ -113,19 +118,15 @@ namespace xPlat {
|
|||
nullptr);
|
||||
|
||||
// get all the blocks for the file.
|
||||
std::vector<Block> blocks(blockResult->getSnapshotLength());
|
||||
std::vector<Block> blocks(blockResult->getSnapshotLength());
|
||||
for (XMLSize_t j = 0; j < blockResult->getSnapshotLength(); j++)
|
||||
{
|
||||
blockResult->snapshotItem(j);
|
||||
auto blockNode = static_cast<DOMElement*>(blockResult->getNodeValue());
|
||||
blocks[j] = GetBlock(blockNode);
|
||||
}
|
||||
}
|
||||
|
||||
auto name = GetName(fileNode);
|
||||
auto existing = m_blockMap.find(name);
|
||||
ThrowErrorIf(Error::BlockMapSemanticError, (existing != m_blockMap.end()), "duplicate file name specified.");
|
||||
m_blockMap.insert(std::make_pair(name, std::move(blocks)));
|
||||
|
||||
m_blockMapfiles.insert(std::make_pair(name,
|
||||
ComPtr<IAppxBlockMapFile>::Make<AppxBlockMapFile>(
|
||||
factory,
|
||||
|
@ -133,7 +134,7 @@ namespace xPlat {
|
|||
GetLocalFileHeaderSize(fileNode),
|
||||
name,
|
||||
GetSize(fileNode))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xPlat::ComPtr<IStream> AppxBlockMapObject::GetValidationStream(const std::string& part, IStream* stream)
|
||||
|
@ -141,7 +142,7 @@ namespace xPlat {
|
|||
ThrowErrorIf(Error::InvalidParameter, (part.empty() || stream == nullptr), "bad input");
|
||||
auto item = m_blockMap.find(part);
|
||||
ThrowErrorIf(Error::BlockMapSemanticError, item == m_blockMap.end(), "file not tracked by blockmap");
|
||||
return ComPtr<IStream>::Make<BlockMapStream>(stream, item->second);
|
||||
return ComPtr<IStream>::Make<BlockMapStream>(m_factory, part, stream, item->second);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AppxBlockMapObject::GetFile(LPCWSTR filename, IAppxBlockMapFile **file)
|
||||
|
@ -162,7 +163,7 @@ namespace xPlat {
|
|||
ComPtr<IAppxBlockMapReader> self;
|
||||
ThrowHrIfFailed(QueryInterface(UuidOfImpl<IAppxBlockMapReader>::iid, reinterpret_cast<void**>(&self)));
|
||||
*enumerator = ComPtr<IAppxBlockMapFilesEnumerator>::Make<AppxBlockMapFilesEnumerator>(
|
||||
self.Get(),
|
||||
self.Get(),
|
||||
std::move(GetFileNames(FileNameOptions::All))).Detach();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "StorageObject.hpp"
|
||||
#include "AppxPackageObject.hpp"
|
||||
#include "UnicodeConversion.hpp"
|
||||
#include "ContentTypesSchemas.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -31,8 +32,8 @@ namespace xPlat {
|
|||
};
|
||||
|
||||
static const std::uint8_t PercentangeEncodingTableSize = 0x5E;
|
||||
static const std::vector<std::string> PercentangeEncoding = {
|
||||
"", "", "", "", "", "", "", "",
|
||||
static const std::vector<std::string> PercentangeEncoding =
|
||||
{ "", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
|
@ -47,8 +48,7 @@ namespace xPlat {
|
|||
};
|
||||
|
||||
static const std::map<std::string, char> EncodingToChar =
|
||||
{
|
||||
{"20", ' '}, {"21", '!'}, {"23", '#'}, {"24", '$'},
|
||||
{ {"20", ' '}, {"21", '!'}, {"23", '#'}, {"24", '$'},
|
||||
{"25", '%'}, {"26", '&'}, {"27", '\''}, {"28", '('},
|
||||
{"29", ')'}, {"25", '+'}, {"2B", '%'}, {"2C", ','},
|
||||
{"3B", ';'}, {"3D", '='}, {"40", '@'}, {"5B", '['},
|
||||
|
@ -59,19 +59,15 @@ namespace xPlat {
|
|||
{
|
||||
std::string result;
|
||||
for (std::uint32_t position = 0; position < fileName.length(); ++position)
|
||||
{
|
||||
std::uint8_t index = static_cast<std::uint8_t>(fileName[position]);
|
||||
{ std::uint8_t index = static_cast<std::uint8_t>(fileName[position]);
|
||||
if(fileName[position] < PercentangeEncodingTableSize && index < PercentangeEncoding.size() && !PercentangeEncoding[index].empty())
|
||||
{
|
||||
result += PercentangeEncoding[index];
|
||||
{ result += PercentangeEncoding[index];
|
||||
}
|
||||
else if (fileName[position] == '\\') // Remove Windows file name
|
||||
{
|
||||
result += '/';
|
||||
else if (fileName[position] == '\\') // Remove Windows file separator.
|
||||
{ result += '/';
|
||||
}
|
||||
else
|
||||
{
|
||||
result += fileName[position];
|
||||
{ result += fileName[position];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -81,27 +77,18 @@ namespace xPlat {
|
|||
{
|
||||
std::string result;
|
||||
for (std::uint32_t i = 0; i < fileName.length(); ++i)
|
||||
{
|
||||
if(fileName[i] == '%')
|
||||
{
|
||||
auto found = EncodingToChar.find(fileName.substr(i+1, 2));
|
||||
{ if(fileName[i] == '%')
|
||||
{ auto found = EncodingToChar.find(fileName.substr(i+1, 2));
|
||||
if (found != EncodingToChar.end())
|
||||
{
|
||||
result += found->second;
|
||||
{ result += found->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(Error::AppxUnknownFileNameEncoding, fileName);
|
||||
{ throw Exception(Error::AppxUnknownFileNameEncoding, fileName);
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
else if (fileName[i] == '/') // Windows file name
|
||||
{
|
||||
result += '\\';
|
||||
}
|
||||
else
|
||||
{
|
||||
result += fileName[i];
|
||||
{ result += fileName[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -141,7 +128,7 @@ namespace xPlat {
|
|||
// 2. Get content type using signature object for validation
|
||||
// TODO: switch underlying type of m_contentType to something more specific.
|
||||
auto temp = m_appxSignature->GetValidationStream(CONTENT_TYPES_XML, m_container->GetFile(CONTENT_TYPES_XML));
|
||||
m_contentType = ComPtr<IVerifierObject>::Make<XmlObject>(temp);
|
||||
m_contentType = ComPtr<IVerifierObject>::Make<XmlObject>(temp, &contentTypesSchema);
|
||||
ThrowErrorIfNot(Error::AppxMissingContentTypesXML, (m_contentType->HasStream()), "[Content_Types].xml not in archive!");
|
||||
|
||||
// 3. Get blockmap object using signature object for validation
|
||||
|
@ -210,13 +197,11 @@ namespace xPlat {
|
|||
{
|
||||
std::string targetName;
|
||||
if (options & APPX_PACKUNPACK_OPTION_CREATEPACKAGESUBFOLDER)
|
||||
{
|
||||
throw Exception(Error::NotImplemented);
|
||||
{ throw Exception(Error::NotImplemented);
|
||||
//targetName = GetAppxManifest()->GetPackageFullName() + to->GetPathSeparator() + fileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetName = fileName;
|
||||
{ targetName = DecodeFileName(fileName);
|
||||
}
|
||||
|
||||
auto targetFile = to->OpenFile(targetName, xPlat::FileStream::Mode::WRITE_UPDATE);
|
||||
|
|
|
@ -21,7 +21,8 @@ AppxSignatureObject::AppxSignatureObject(APPX_VALIDATION_OPTION validationOption
|
|||
m_stream(stream),
|
||||
m_validationOptions(validationOptions)
|
||||
{
|
||||
m_hasDigests = SignatureValidator::Validate(validationOptions, stream, m_digests, m_signatureOrigin);
|
||||
std::string publisher;
|
||||
m_hasDigests = SignatureValidator::Validate(validationOptions, stream, m_digests, m_signatureOrigin, publisher);
|
||||
|
||||
if (0 == (validationOptions & APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_SKIPSIGNATURE))
|
||||
{ // reset the source stream back to the beginning after validating it.
|
||||
|
|
|
@ -52,6 +52,17 @@ std::map<std::string, std::string> blockMapSchema = {
|
|||
")
|
||||
file(WRITE "../inc/AppxBlockMapSchemas.hpp" "${BLOCKMAP_HEADER}")
|
||||
|
||||
# Create header for [Content_Types] schema
|
||||
file(READ "${CMAKE_PROJECT_ROOT}/AppxPackaging/[Content_Types]/opc-contentTypes.xsd" CONTENT_TYPES_SCHEMA)
|
||||
set(CONTENT_TYPES_HEADER "// This file is generated by CMake and contains XSDs for parsing [Content_Types].xml. Do not edit!!
|
||||
#include <string>
|
||||
#include <map>
|
||||
std::map<std::string, std::string> contentTypesSchema = {
|
||||
{\"contentTypesSchemaRaw\", R\"###(${CONTENT_TYPES_SCHEMA})###\" }
|
||||
};
|
||||
")
|
||||
file(WRITE "../inc/ContentTypesSchemas.hpp" "${CONTENT_TYPES_HEADER}")
|
||||
|
||||
set(LIB_PUBLIC_HEADERS
|
||||
../inc/AppxPackaging.hpp
|
||||
../inc/AppxWindows.hpp
|
||||
|
|
|
@ -40,8 +40,8 @@ namespace xPlat
|
|||
void operator()(X509_STORE_CTX *xsc) const { if (xsc) {X509_STORE_CTX_cleanup(xsc); X509_STORE_CTX_free(xsc);} };
|
||||
};
|
||||
|
||||
struct unique_X509_NAME_deleter {
|
||||
void operator()(X509_NAME *xn) const { if (xn) OPENSSL_free(xn); };
|
||||
struct unique_OPENSSL_string_deleter {
|
||||
void operator()(char *os) const { if (os) OPENSSL_free(os); };
|
||||
};
|
||||
|
||||
struct unique_STACK_X509_deleter {
|
||||
|
@ -57,9 +57,9 @@ namespace xPlat
|
|||
typedef std::unique_ptr<X509, unique_X509_deleter> unique_X509;
|
||||
typedef std::unique_ptr<X509_STORE, unique_X509_STORE_deleter> unique_X509_STORE;
|
||||
typedef std::unique_ptr<X509_STORE_CTX, unique_X509_STORE_CTX_deleter> unique_X509_STORE_CTX;
|
||||
typedef std::unique_ptr<X509_NAME, unique_X509_NAME_deleter> unique_X509_NAME;
|
||||
typedef std::unique_ptr<char, unique_OPENSSL_string_deleter> unique_OPENSSL_string;
|
||||
typedef std::unique_ptr<STACK_OF(X509), unique_STACK_X509_deleter> unique_STACK_X509;
|
||||
|
||||
|
||||
typedef struct Asn1Sequence
|
||||
{
|
||||
std::uint8_t tag;
|
||||
|
@ -239,11 +239,76 @@ namespace xPlat
|
|||
return ok;
|
||||
}
|
||||
|
||||
bool GetPublisherName(/*in*/ unique_PKCS7& p7, /*inout*/ std::string& publisher)
|
||||
{
|
||||
X509* cert = nullptr;
|
||||
STACK_OF(X509) *untrustedCerts = p7.get()->d.sign->cert;
|
||||
// If there's only one cert, it's a self-signed package; just return its subject
|
||||
if (sk_X509_num(untrustedCerts) == 1)
|
||||
{
|
||||
cert = sk_X509_value(untrustedCerts, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::map<std::string/*issuer*/, int/*refcount*/> issuers;
|
||||
for (int i = 0; i < sk_X509_num(untrustedCerts); i++)
|
||||
{
|
||||
X509* certT = sk_X509_value(untrustedCerts, i);
|
||||
unique_OPENSSL_string issuer(X509_NAME_oneline(X509_get_issuer_name(certT), NULL, 0));
|
||||
auto search = issuers.find(issuer.get());
|
||||
// If the issuer is not in the map, add it and set refcount to 1
|
||||
if (search == issuers.end())
|
||||
{
|
||||
issuers[issuer.get()] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This issuer is in the map, increment its refcount
|
||||
int count = search->second;
|
||||
issuers[issuer.get()] = count + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Now, loop through the certs and find out which one isn't an issuer
|
||||
for (int i = 0; i < sk_X509_num(untrustedCerts); i++)
|
||||
{
|
||||
X509* certT = sk_X509_value(untrustedCerts, i);
|
||||
unique_OPENSSL_string subject(X509_NAME_oneline(X509_get_subject_name(certT), NULL, 0));
|
||||
auto search = issuers.find(subject.get());
|
||||
// If the subject is not in the map, we have found our cert
|
||||
if (search == issuers.end())
|
||||
{
|
||||
cert = certT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We should have found a certificate
|
||||
if (cert)
|
||||
{
|
||||
// Create a BIO memory buffer, and print the subject name to it.
|
||||
unique_BIO bio(BIO_new(BIO_s_mem()));
|
||||
X509_NAME_print_ex(bio.get(),
|
||||
X509_get_subject_name(cert),
|
||||
0,
|
||||
XN_FLAG_SEP_CPLUS_SPC | XN_FLAG_DN_REV);
|
||||
|
||||
// Now extract the publisher from the BIO print buffer
|
||||
char *memBuffer = nullptr;
|
||||
BIO_get_mem_data(bio.get(), &memBuffer);
|
||||
publisher = std::string(memBuffer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SignatureValidator::Validate(
|
||||
/*in*/ APPX_VALIDATION_OPTION option,
|
||||
/*in*/ IStream *stream,
|
||||
/*inout*/ std::map<xPlat::AppxSignatureObject::DigestName, xPlat::AppxSignatureObject::Digest>& digests,
|
||||
/*inout*/ SignatureOrigin& origin)
|
||||
/*inout*/ SignatureOrigin& origin,
|
||||
/*inout*/ std::string& publisher)
|
||||
{
|
||||
// If the caller wants to skip signature validation altogether, just bug out early. We will not read the digests
|
||||
if (option & APPX_VALIDATION_OPTION_SKIPSIGNATURE) { return false; }
|
||||
|
@ -337,6 +402,11 @@ namespace xPlat
|
|||
xPlat::SignatureOrigin::LOB == origin ||
|
||||
(option & APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_ALLOWSIGNATUREORIGINUNKNOWN)
|
||||
), "Signature origin check failed");
|
||||
|
||||
ThrowErrorIfNot(Error::AppxSignatureInvalid, (
|
||||
GetPublisherName(p7, publisher) == true
|
||||
), "Signature origin check failed");
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace xPlat
|
|
@ -351,11 +351,40 @@ namespace xPlat
|
|||
return IsAuthenticodeTrustedChain(certChainContext.get());
|
||||
}
|
||||
|
||||
static bool GetPublisherName(/*in*/byte* signatureBuffer, /*in*/ ULONG cbSignatureBuffer, /*inout*/ std::string& publisher)
|
||||
{
|
||||
unique_cert_context certificateContext(GetCertContext(signatureBuffer, cbSignatureBuffer));
|
||||
|
||||
int requiredLength = CertNameToStrA(
|
||||
X509_ASN_ENCODING,
|
||||
&certificateContext.get()->pCertInfo->Subject,
|
||||
CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
|
||||
nullptr,
|
||||
0);
|
||||
|
||||
std::vector<char> publisherT;
|
||||
publisherT.reserve(requiredLength + 1);
|
||||
|
||||
if (CertNameToStrA(
|
||||
X509_ASN_ENCODING,
|
||||
&certificateContext.get()->pCertInfo->Subject,
|
||||
CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
|
||||
publisherT.data(),
|
||||
requiredLength) > 0)
|
||||
{
|
||||
publisher = std::string(publisherT.data());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SignatureValidator::Validate(
|
||||
/*in*/ APPX_VALIDATION_OPTION option,
|
||||
/*in*/ IStream *stream,
|
||||
/*inout*/ std::map<xPlat::AppxSignatureObject::DigestName, xPlat::AppxSignatureObject::Digest>& digests,
|
||||
/*inout*/ SignatureOrigin& origin)
|
||||
/*inout*/ SignatureOrigin& origin,
|
||||
/*inout*/ std::string& publisher)
|
||||
{
|
||||
// If the caller wants to skip signature validation altogether, just bug out early. We will not read the digests
|
||||
if (option & APPX_VALIDATION_OPTION_SKIPSIGNATURE) { return false; }
|
||||
|
@ -518,6 +547,10 @@ namespace xPlat
|
|||
((xPlat::SignatureOrigin::Unknown == origin) && !SignatureOriginUnknownAllowed),
|
||||
"Unknown signature origin");
|
||||
|
||||
ThrowErrorIfNot(Error::AppxSignatureInvalid,
|
||||
GetPublisherName(p7s, p7sSize, publisher) == true,
|
||||
"Could not retrieve publisher name");
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace xPlat
|
|
@ -0,0 +1,77 @@
|
|||
#!/bin/bash
|
||||
TESTFAILED=0
|
||||
function FindBinFolder {
|
||||
#look in .vs/bin first
|
||||
if [ -e "../.vs/bin/MakeXplat" ]
|
||||
then
|
||||
BINDIR="../.vs/bin"
|
||||
elif [ -e "../.vscode/bin/MakeXplat" ]
|
||||
then
|
||||
BINDIR="../.vscode/bin"
|
||||
elif [ -e "../build/bin/MakeXplat" ]
|
||||
then
|
||||
BINDIR="../build/bin"
|
||||
else
|
||||
echo "ERROR: Could not find build binaries"
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
function CleanupUnpackFolder {
|
||||
rm -f -r ./unpack/*
|
||||
if [ -e "./unpack/*" ]
|
||||
then
|
||||
echo "ERROR: Could not cleanup ./unpack directory"
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
function RunTest {
|
||||
CleanupUnpackFolder
|
||||
local SUCCESS="$1"
|
||||
local UNPACKFOLDER="$2"
|
||||
local ARGS="$3"
|
||||
echo "------------------------------------------------------"
|
||||
echo $BINDIR/MakeXplat unpack -d ./unpack -p $UNPACKFOLDER $ARGS
|
||||
echo "------------------------------------------------------"
|
||||
$BINDIR/MakeXplat unpack -d ./unpack -p $UNPACKFOLDER $ARGS
|
||||
local RESULT=$?
|
||||
echo "expect: "$SUCCESS", got: "$RESULT
|
||||
if [ $RESULT -eq $SUCCESS ]
|
||||
then
|
||||
echo "succeeded"
|
||||
else
|
||||
echo "FAILED"
|
||||
TESTFAILED=1
|
||||
fi
|
||||
}
|
||||
|
||||
FindBinFolder
|
||||
# return code is last two digits, but in decimal, not hex. e.g. 0x8bad0002 == 2, 0x8bad0041 == 65, etc...
|
||||
# common codes:
|
||||
# AppxSignatureInvalid = ERROR_FACILITY + 0x0041 == 65
|
||||
|
||||
RunTest 2 ./appx/Empty.appx -sv
|
||||
RunTest 0 ./appx/HelloWorld.appx -ss
|
||||
RunTest 65 ./appx/SignatureNotLastPart-ERROR_BAD_FORMAT.appx
|
||||
#RunTest 0x134 ./appx/SignedMismatchedPublisherName-ERROR_BAD_FORMAT.appx
|
||||
RunTest 65 ./appx/SignedTamperedBlockMap-TRUST_E_BAD_DIGEST.appx
|
||||
RunTest 65 ./appx/SignedTamperedBlockMap-TRUST_E_BAD_DIGEST.appx -sv
|
||||
RunTest 65 ./appx/SignedTamperedCD-TRUST_E_BAD_DIGEST.appx
|
||||
RunTest 65 ./appx/SignedTamperedCodeIntegrity-TRUST_E_BAD_DIGEST.appx
|
||||
RunTest 65 ./appx/SignedTamperedContentTypes-TRUST_E_BAD_DIGEST.appx
|
||||
RunTest 65 ./appx/SignedUntrustedCert-CERT_E_CHAINING.appx
|
||||
RunTest 0 ./appx/StoreSigned_Desktop_x64_MoviesTV.appx
|
||||
RunTest 65 ./appx/TestAppxPackage_Win32.appx
|
||||
RunTest 65 ./appx/TestAppxPackage_x64.appx
|
||||
RunTest 18 ./appx/UnsignedZip64WithCI-APPX_E_MISSING_REQUIRED_FILE.appx
|
||||
|
||||
echo "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="
|
||||
if [ $TESTFAILED -ne 0 ]
|
||||
then
|
||||
echo " FAILED "
|
||||
exit $TESTFAILED
|
||||
else
|
||||
echo " passed "
|
||||
exit 0
|
||||
fi
|
|
@ -0,0 +1,94 @@
|
|||
|
||||
$global:TESTFAILED=0
|
||||
$global:BINDIR=""
|
||||
|
||||
function FindBinFolder {
|
||||
if (Test-Path "..\.vs\bin\MakeXplat.exe" )
|
||||
{
|
||||
$global:BINDIR="..\.vs\bin"
|
||||
}
|
||||
elseif (Test-Path "..\.vscode\bin\MakeXplat.exe" )
|
||||
{
|
||||
$global:BINDIR="..\.vscode\bin"
|
||||
}
|
||||
elseif (Test-Path "..\build\bin\MakeXplat.exe")
|
||||
{
|
||||
$global:BINDIR="..\build\bin"
|
||||
}
|
||||
else
|
||||
{
|
||||
write-host "ERROR: Could not find build binaries"
|
||||
exit
|
||||
}
|
||||
}
|
||||
|
||||
function CleanupUnpackFolder {
|
||||
Remove-Item ".\unpack\*" -recurse
|
||||
if (Test-Path ".\unpack\*" )
|
||||
{
|
||||
write-host "ERROR: Could not cleanup .\unpack directory"
|
||||
exit
|
||||
}
|
||||
}
|
||||
|
||||
function RunTest([string] $UNPACKFOLDER, [int] $SUCCESSCODE) {
|
||||
CleanupUnpackFolder
|
||||
write-host "------------------------------------------------------"
|
||||
write-host $UNPACKFOLDER >> Win32.log
|
||||
#$BINDIR\MakeXplat unpack -d .\unpack -p $UNPACKFOLDER
|
||||
Start-Process "$BINDIR\MakeXplat.exe" -ArgumentList ("unpack", "-d", ".\unpack", "-p", $UNPACKFOLDER) -Wait
|
||||
$ERRORCODE=$?
|
||||
if ( $ERRORCODE -eq $SUCCESSCODE )
|
||||
{
|
||||
write-host "Succeeded: $SUCCESSCODE"
|
||||
}
|
||||
else
|
||||
{
|
||||
write-host "Expected: $SUCCESSCODE"
|
||||
write-host "Failed: $ERRORCODE"
|
||||
$global:TESTFAILED=1
|
||||
}
|
||||
}
|
||||
|
||||
FindBinFolder
|
||||
|
||||
RunTest .\appx\CentennialCoffee.appx 134
|
||||
RunTest .\appx\Empty.appx 134
|
||||
RunTest .\appx\HelloWorld.appx 134
|
||||
RunTest .\appx\SignatureNotLastPart-ERROR_BAD_FORMAT.appx 134
|
||||
RunTest .\appx\SignedMismatchedPublisherName-ERROR_BAD_FORMAT.appx 134
|
||||
RunTest .\appx\SignedTamperedBlockMap-TRUST_E_BAD_DIGEST.appx 134
|
||||
RunTest .\appx\SignedTamperedCD-TRUST_E_BAD_DIGEST.appx 134
|
||||
RunTest .\appx\SignedTamperedCodeIntegrity-TRUST_E_BAD_DIGEST.appx 134
|
||||
RunTest .\appx\SignedTamperedContentTypes-TRUST_E_BAD_DIGEST.appx 134
|
||||
RunTest .\appx\SignedUntrustedCert-CERT_E_CHAINING.appx 134
|
||||
RunTest .\appx\StoreSigned_Desktop_x64_MoviesTV.appx 0
|
||||
RunTest .\appx\TestAppxPackage_Win32.appx 134
|
||||
RunTest .\appx\TestAppxPackage_x64.appx 134
|
||||
RunTest .\appx\UnsignedZip64WithCI-APPX_E_MISSING_REQUIRED_FILE.appx 134
|
||||
RunTest .\appx\BlockMap\TODAVIANO\Signature_in_BlockMap.appx 134
|
||||
RunTest .\appx\BlockMap\TODAVIANO\SignedTamperedBlockMap-TRUST_E_BAD_DIGEST.appx 134
|
||||
RunTest .\appx\BlockMap\Missing_Manifest_in_blockmap.appx 134
|
||||
RunTest .\appx\BlockMap\ContentTypes_in_blockmap.appx 134
|
||||
RunTest .\appx\BlockMap\Invalid_Bad_Block.appx 134
|
||||
RunTest .\appx\BlockMap\Size_wrong_uncompressed.appx 134
|
||||
RunTest .\appx\BlockMap\HelloWorld.appx 134
|
||||
RunTest .\appx\BlockMap\Extra_file_in_blockmap.appx 134
|
||||
RunTest .\appx\BlockMap\File_missing_from_blockmap.appx 134
|
||||
RunTest .\appx\BlockMap\No_blockmap.appx 134
|
||||
RunTest .\appx\BlockMap\Bad_Namespace_Blockmap.appx 134
|
||||
RunTest .\appx\BlockMap\Duplicate_file_in_blockmap.appx 134
|
||||
|
||||
CleanupUnpackFolder
|
||||
|
||||
if ( $global:TESTFAILED -eq 1 )
|
||||
{
|
||||
exit 134
|
||||
}
|
||||
else
|
||||
{
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
|
Двоичные данные
test/appx/BlockMap/TODAVIANO/Signature_in_BlockMap.appx
Двоичные данные
test/appx/BlockMap/TODAVIANO/Signature_in_BlockMap.appx
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Загрузка…
Ссылка в новой задаче