diff --git a/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/SFXSetup.dsp b/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/SFXSetup.dsp --- a/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/SFXSetup.dsp +++ b/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/SFXSetup.dsp @@ -54,7 +54,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"C:\Util\7zS.sfx" /opt:NOWIN98 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib delayimp.lib /nologo /subsystem:windows /machine:I386 /out:"Release\7zS.sfx" /opt:NOWIN98 /delayload:user32.dll /delayload:shell32.dll /delayload:oleaut32.dll # SUBTRACT LINK32 /pdb:none !ELSEIF "$(CFG)" == "SFXSetup - Win32 Debug" @@ -81,7 +81,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"C:\UTIL\7zSfxS.exe" /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib delayimp.lib /nologo /subsystem:windows /debug /machine:I386 /out:"Debug\7zSfxS.exe" /pdbtype:sept /delayload:user32.dll /delayload:shell32.dll /delayload:oleaut32.dll !ELSEIF "$(CFG)" == "SFXSetup - Win32 ReleaseD" @@ -107,9 +107,9 @@ BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"C:\UTIL\7zWinSR.exe" +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"C:\UTIL\7zWinSR.exe" # SUBTRACT BASE LINK32 /debug /nodefaultlib -# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"C:\Util\7zSD.sfx" /opt:NOWIN98 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib delayimp.lib /nologo /subsystem:windows /machine:I386 /out:"ReleaseD\7zSD.sfx" /opt:NOWIN98 /delayload:user32.dll /delayload:shell32.dll /delayload:oleaut32.dll # SUBTRACT LINK32 /pdb:none !ENDIF diff --git a/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp b/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp --- a/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp +++ b/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp @@ -27,6 +27,10 @@ #include "resource.h" +/* BEGIN Mozilla customizations */ +#include "../../../Common/IntToString.h" +/* END Mozilla customizations */ + using namespace NWindows; using namespace NFile; using namespace NDir; @@ -125,6 +129,398 @@ static void ShowErrorMessageSpec(const U ShowErrorMessage(NULL, message); } +/* BEGIN Mozilla customizations */ + +static char const * +FindStrInBuf(char const * buf, size_t bufLen, char const * str) +{ + size_t index = 0; + while (index < bufLen) { + char const * result = strstr(buf + index, str); + if (result) { + return result; + } + while ((buf[index] != '\0') && (index < bufLen)) { + index++; + } + index++; + } + return NULL; +} + +static bool +ReadPostSigningDataFromView(char const * view, DWORD size, AString& data) +{ + // Find the offset and length of the certificate table, + // so we know the valid range to look for the token. + if (size < (0x3c + sizeof(UInt32))) { + return false; + } + UInt32 PEHeaderOffset = *(UInt32*)(view + 0x3c); + UInt32 optionalHeaderOffset = PEHeaderOffset + 24; + UInt32 certDirEntryOffset = 0; + if (size < (optionalHeaderOffset + sizeof(UInt16))) { + return false; + } + UInt16 magic = *(UInt16*)(view + optionalHeaderOffset); + if (magic == 0x010b) { + // 32-bit executable + certDirEntryOffset = optionalHeaderOffset + 128; + } else if (magic == 0x020b) { + // 64-bit executable; certain header fields are wider + certDirEntryOffset = optionalHeaderOffset + 144; + } else { + // Unknown executable + return false; + } + if (size < certDirEntryOffset + 8) { + return false; + } + UInt32 certTableOffset = *(UInt32*)(view + certDirEntryOffset); + UInt32 certTableLen = *(UInt32*)(view + certDirEntryOffset + sizeof(UInt32)); + if (certTableOffset == 0 || certTableLen == 0 || + size < (certTableOffset + certTableLen)) { + return false; + } + + char const token[] = "__MOZCUSTOM__:"; + // We're searching for a string inside a binary blob, + // so a normal strstr that bails on the first NUL won't work. + char const * tokenPos = FindStrInBuf(view + certTableOffset, + certTableLen, token); + if (tokenPos) { + size_t tokenLen = (sizeof(token) / sizeof(token[0])) - 1; + data = AString(tokenPos + tokenLen); + return true; + } + return false; +} + +static bool +ReadPostSigningData(UString exePath, AString& data) +{ + bool retval = false; + HANDLE exeFile = CreateFileW(exePath, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (exeFile != INVALID_HANDLE_VALUE) { + HANDLE mapping = CreateFileMapping(exeFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (mapping != INVALID_HANDLE_VALUE) { + // MSDN claims the return value on failure is NULL, + // but I've also seen it returned on success, so double-check. + if (mapping || GetLastError() == ERROR_SUCCESS) { + char * view = (char*)MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); + if (view) { + DWORD fileSize = GetFileSize(exeFile, NULL); + retval = ReadPostSigningDataFromView(view, fileSize, data); + } + CloseHandle(mapping); + } + } + CloseHandle(exeFile); + } + return retval; +} + +// Simple class for allocating a character buffer and automatically freeing it +// when it goes out of scope. +class AutoCharBuffer { +private: + char *buffer; + + void Alloc(UInt32 size) { + buffer = new char[size]; + length = 0; + } +public: + // Other than being reset when the buffer is deallocated/reallocated, this + // isn't updated by this class. + UInt32 length; + + AutoCharBuffer() + : length(0) + , buffer(NULL) {} + + AutoCharBuffer(UInt32 size) { + Alloc(size); + } + + void Realloc(UInt32 size) { + delete [] buffer; + Alloc(size); + } + + void Dealloc() { + delete [] buffer; + buffer = NULL; + length = 0; + } + + virtual ~AutoCharBuffer() { + Dealloc(); + } + + char *Buffer() { + return buffer; + } +}; + +static void +AppendStringValueToIni(AString& iniData, const char* key, const char* value) { + iniData += key; + iniData += '='; + iniData += value; + iniData += '\n'; +} + +static void +AppendDwordValueToIni(AString& iniData, const char* key, DWORD intValue) { + AString stringValue; + stringValue.Add_UInt32(intValue); + AppendStringValueToIni(iniData, key, stringValue.Ptr()); +} + +static void +AppendQwordValueToIni(AString& iniData, const char* key, LONGLONG intValue) { + // The implementations for `ConvertToString` are a little wonky and + // expect the output buffer to just be the correct size. To make sure we are + // using it right here, this int conversion implementation was copied from + // `CStdOutStream::operator<<(Int64 number)` in `StdOutStream.cpp`. + char stringValue[32]; + ConvertInt64ToString(intValue, stringValue); + AppendStringValueToIni(iniData, key, stringValue); +} + +static void +ReadExeFileSystemIntoIniData(const UString &exePath, AString& iniData) { + const char* fsKey = "fileSystem"; + const char* readFsErrorTypeKey = "readFsError"; + const char* readFsErrorCodeKey = "readFsErrorCode"; + + HANDLE exeFile = CreateFileW(exePath, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (exeFile == INVALID_HANDLE_VALUE) { + DWORD errorCode = GetLastError(); + AppendStringValueToIni(iniData, readFsErrorTypeKey, "openFile"); + AppendDwordValueToIni(iniData, readFsErrorCodeKey, errorCode); + return; + } + + const size_t bufferSize = MAX_PATH + 1; + wchar_t buffer[bufferSize]; + BOOL success = GetVolumeInformationByHandleW(exeFile, NULL, 0, NULL, NULL, + NULL, buffer, bufferSize); + if (!success) { + DWORD errorCode = GetLastError(); + AppendStringValueToIni(iniData, readFsErrorTypeKey, "getVolInfo"); + AppendDwordValueToIni(iniData, readFsErrorCodeKey, errorCode); + CloseHandle(exeFile); + return; + } + CloseHandle(exeFile); + + size_t fsLen = wcsnlen(buffer, bufferSize); + if (fsLen == bufferSize) { + AppendStringValueToIni(iniData, readFsErrorTypeKey, "fsUnterminated"); + return; + } + + const int narrowBufferSize = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, NULL, + 0, NULL, NULL); + if (narrowBufferSize <= 0) { + DWORD errorCode = GetLastError(); + AppendStringValueToIni(iniData, readFsErrorTypeKey, "getBufferSize"); + AppendDwordValueToIni(iniData, readFsErrorCodeKey, errorCode); + return; + } + AutoCharBuffer fs(narrowBufferSize); + int written = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, fs.Buffer(), + narrowBufferSize, NULL, NULL); + if (written <= 0) { + DWORD errorCode = GetLastError(); + AppendStringValueToIni(iniData, readFsErrorTypeKey, "convertString"); + AppendDwordValueToIni(iniData, readFsErrorCodeKey, errorCode); + return; + } + + // Like "fileSystem=FAT32" or "fileSystem=NTFS". + AppendStringValueToIni(iniData, fsKey, fs.Buffer()); +} + +// Read Zone Identifier information from alternate data stream. +// Always either returns the data that was read, or data indicating what sort of +// error was encountered when obtaining the data. +// When this function returns, `metadata` is guaranteed to contain relevant +// metadata that should be written out. But `data.Buffer()` may be null +// depending on whether we successfully read data. +static void +ReadZoneIdentifierData(const UString &exePath, AString& metadata, + AutoCharBuffer& data) +{ + metadata.Empty(); + data.Dealloc(); + + // We don't want to allow this function to just read an unlimited amount into + // `data`, so this value will control at what point we consider the file too + // big to be valid. + // 1 MB should be way more than enough. The Zone Identifier file generally + // consists of no more than 4 short lines of text. + const size_t maxReadSize = 1 * 1000 * 1000; + const char* readZoneIdErrorTypeKey = "readZoneIdError"; + const char* readZoneIdErrorCodeKey = "readZoneIdErrorCode"; + // It looks like the Zone Identifier will be INI data. But since there is no + // real guarantee of this, we are going to put an INI-compatible sentinel + // before we start appending the Zone Identifier file. This should help us + // better parse the file contents if we discover, say, that there is another + // possible format for Zone Identifier data. + const char* zoneIdStartSentinel = "\n[MozillaZoneIdentifierStartSentinel]\n"; + + metadata += "[Mozilla]\n"; + ReadExeFileSystemIntoIniData(exePath, metadata); + + UString adsPath(exePath); + // A colon (`:`) is not a valid path constituent (see + // https://learn.microsoft.com/en-ca/windows/win32/fileio/naming-a-file), so + // file systems that don't support ADS will fail to open rather than open an + // unrelated file. + adsPath += L":Zone.Identifier"; + HANDLE adsFile = CreateFileW(adsPath, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (adsFile == INVALID_HANDLE_VALUE) { + DWORD errorCode = GetLastError(); + AppendStringValueToIni(metadata, readZoneIdErrorTypeKey, "openFile"); + AppendDwordValueToIni(metadata, readZoneIdErrorCodeKey, errorCode); + return; + } + + LARGE_INTEGER fileSize; + BOOL success = GetFileSizeEx(adsFile, &fileSize); + UInt32 bufferSize = maxReadSize; + if (!success) { + AppendStringValueToIni(metadata, "zoneIdFileSize", "unknown"); + AppendStringValueToIni(metadata, "zoneIdBufferLargeEnough", "unknown"); + } else { + AppendQwordValueToIni(metadata, "zoneIdFileSize", fileSize.QuadPart); + if (fileSize.QuadPart < (LONGLONG)bufferSize) { + AppendStringValueToIni(metadata, "zoneIdBufferLargeEnough", "true"); + bufferSize = (UInt32)fileSize.QuadPart; + } else { + AppendStringValueToIni(metadata, "zoneIdBufferLargeEnough", "false"); + } + } + data.Realloc(bufferSize); + + DWORD readCount; + success = ReadFile(adsFile, data.Buffer(), bufferSize, &readCount, NULL); + if (!success) { + DWORD errorCode = GetLastError(); + AppendStringValueToIni(metadata, readZoneIdErrorTypeKey, "readFile"); + AppendDwordValueToIni(metadata, readZoneIdErrorCodeKey, errorCode); + + data.Dealloc(); + CloseHandle(adsFile); + return; + } + data.length = readCount; + + char dummyBuffer; + + success = ReadFile(adsFile, &dummyBuffer, 1, &readCount, NULL); + CloseHandle(adsFile); + if (success) { + if (readCount == 0) { + // We are at the end of the file + AppendStringValueToIni(metadata, "zoneIdTruncated", "false"); + } else { + AppendStringValueToIni(metadata, "zoneIdTruncated", "true"); + } + } else { + AppendStringValueToIni(metadata, "zoneIdTruncated", "unknown"); + } + + metadata += zoneIdStartSentinel; +} + +// Delayed load libraries are loaded when the first symbol is used. +// The following ensures that we load the delayed loaded libraries from the +// system directory. +struct AutoLoadSystemDependencies +{ + AutoLoadSystemDependencies() + { + HMODULE module = ::GetModuleHandleW(L"kernel32.dll"); + if (module) { + // SetDefaultDllDirectories is always available on Windows 8 and above. It + // is also available on Windows Vista, Windows Server 2008, and + // Windows 7 when MS KB2533623 has been applied. + typedef BOOL (WINAPI *SetDefaultDllDirectoriesType)(DWORD); + SetDefaultDllDirectoriesType setDefaultDllDirectories = + (SetDefaultDllDirectoriesType) GetProcAddress(module, "SetDefaultDllDirectories"); + if (setDefaultDllDirectories) { + setDefaultDllDirectories(0x0800 /* LOAD_LIBRARY_SEARCH_SYSTEM32 */ ); + return; + } + } + + static LPCWSTR delayDLLs[] = { L"uxtheme.dll", L"userenv.dll", + L"setupapi.dll", L"apphelp.dll", + L"propsys.dll", L"dwmapi.dll", + L"cryptbase.dll", L"oleacc.dll", + L"clbcatq.dll" }; + WCHAR systemDirectory[MAX_PATH + 1] = { L'\0' }; + // If GetSystemDirectory fails we accept that we'll load the DLLs from the + // normal search path. + GetSystemDirectoryW(systemDirectory, MAX_PATH + 1); + size_t systemDirLen = wcslen(systemDirectory); + + // Make the system directory path terminate with a slash + if (systemDirectory[systemDirLen - 1] != L'\\' && systemDirLen) { + systemDirectory[systemDirLen] = L'\\'; + ++systemDirLen; + // No need to re-NULL terminate + } + + // For each known DLL ensure it is loaded from the system32 directory + for (size_t i = 0; i < sizeof(delayDLLs) / sizeof(delayDLLs[0]); ++i) { + size_t fileLen = wcslen(delayDLLs[i]); + wcsncpy(systemDirectory + systemDirLen, delayDLLs[i], + MAX_PATH - systemDirLen); + if (systemDirLen + fileLen <= MAX_PATH) { + systemDirectory[systemDirLen + fileLen] = L'\0'; + } else { + systemDirectory[MAX_PATH] = L'\0'; + } + LPCWSTR fullModulePath = systemDirectory; // just for code readability + LoadLibraryW(fullModulePath); + } + } +} loadDLLs; + +BOOL +RemoveCurrentDirFromSearchPath() +{ + // kernel32.dll is in the knownDLL list so it is safe to load without a full path + HMODULE kernel32 = LoadLibraryW(L"kernel32.dll"); + if (!kernel32) { + return FALSE; + } + + typedef BOOL (WINAPI *SetDllDirectoryType)(LPCWSTR); + SetDllDirectoryType SetDllDirectoryFn = + (SetDllDirectoryType)GetProcAddress(kernel32, "SetDllDirectoryW"); + if (!SetDllDirectoryFn) { + FreeLibrary(kernel32); + return FALSE; + } + + // If this call fails we can't do much about it, so ignore it. + // It is unlikely to fail and this is just a precaution anyway. + SetDllDirectoryFn(L""); + FreeLibrary(kernel32); + return TRUE; +} + +/* END Mozilla customizations */ + int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */, #ifdef UNDER_CE LPWSTR @@ -133,13 +529,35 @@ int APIENTRY WinMain(HINSTANCE hInstance #endif /* lpCmdLine */,int /* nCmdShow */) { + /* BEGIN Mozilla customizations */ + // Disable current directory from being in the search path. + // This call does not help with implicitly loaded DLLs. + if (!RemoveCurrentDirFromSearchPath()) { + WCHAR minOSTitle[512] = { '\0' }; + WCHAR minOSText[512] = { '\0' }; + LoadStringW(NULL, IDS_MIN_OS_TITLE, minOSTitle, + sizeof(minOSTitle) / sizeof(minOSTitle[0])); + LoadStringW(NULL, IDS_MIN_OS_TEXT, minOSText, + sizeof(minOSText) / sizeof(minOSText[0])); + MessageBoxW(NULL, minOSText, minOSTitle, MB_OK | MB_ICONERROR); + return 1; + } + /* END Mozilla customizations */ + g_hInstance = (HINSTANCE)hInstance; NT_CHECK - #ifdef _WIN32 - LoadSecurityDlls(); - #endif + // BEGIN Mozilla customizations + // Our AutoLoadSystemDependencies (see above) does the same job as the + // LoadSecurityDlls function, but slightly better because it runs as a static + // initializer, and it doesn't include LOAD_LIBRARY_SEARCH_USER_DIRS in + // the search path, which partially defeats the purpose of calling + // SetDefaultDllDirectories at all. + //#ifdef _WIN32 + //LoadSecurityDlls(); + //#endif + // END Mozilla customizations // InitCommonControls(); @@ -172,6 +590,18 @@ int APIENTRY WinMain(HINSTANCE hInstance UString dirPrefix ("." STRING_PATH_SEPARATOR); UString appLaunched; bool showProgress = true; + + /* BEGIN Mozilla customizations */ + bool extractOnly = false; + if (switches.IsPrefixedBy_NoCase(L"/extractdir=")) { + assumeYes = true; + showProgress = false; + extractOnly = true; + } else if (!switches.IsEmpty()) { + showProgress = false; + } + /* END Mozilla customizations */ + if (!config.IsEmpty()) { CObjectVector pairs; @@ -204,7 +634,8 @@ int APIENTRY WinMain(HINSTANCE hInstance } CTempDir tempDir; - if (!tempDir.Create(kTempDirPrefix)) + /* Mozilla customizations - Added !extractOnly */ + if (!extractOnly && !tempDir.Create(kTempDirPrefix)) { if (!assumeYes) ShowErrorMessage(L"Can not create temp folder archive"); @@ -222,7 +653,9 @@ int APIENTRY WinMain(HINSTANCE hInstance } } - const FString tempDirPath = tempDir.GetPath(); + /* BEGIN Mozilla customizations - added extractOnly parameter support */ + const FString tempDirPath = extractOnly ? switches.Ptr(12) : GetUnicodeString(tempDir.GetPath()); + /* END Mozilla customizations */ // tempDirPath = L"M:\\1\\"; // to test low disk space { bool isCorrupt = false; @@ -250,6 +683,84 @@ int APIENTRY WinMain(HINSTANCE hInstance } } + /* BEGIN Mozilla customizations */ + // Retrieve and store any data added to this file after signing. + { + AString postSigningData; + if (ReadPostSigningData(fullPath, postSigningData)) { + FString postSigningDataFilePath(tempDirPath); + NFile::NName::NormalizeDirPathPrefix(postSigningDataFilePath); + postSigningDataFilePath += L"postSigningData"; + + NFile::NIO::COutFile postSigningDataFile; + postSigningDataFile.Create(postSigningDataFilePath, true); + + UInt32 written = 0; + postSigningDataFile.Write(postSigningData, postSigningData.Len(), written); + } + } + + // Read Zone Identifier information + // This will consist of two types of data that we will write to the same file. + // First we have the metadata, which will be INI data with these possible + // keys: + // - `fileSystem`: What file system the executable is on + // - `readFsError`: A string describing why we couldn't get the file system. + // Either this key will be present or the `fileSystem` key + // will be. + // - `readFsErrorCode`: An integer returned by `GetLastError()` indicating, + // in more detail, why we failed to obtain the file + // system. This key may exist if `readFsError` exists. + // - `readZoneIdError`: A string describing why we couldn't get the + // provenance data. + // - `readZoneIdErrorCode`: An integer returned by `GetLastError()` + // indicating, in more detail, why we failed to get the + // provenance data. This key may exist if + // `readZoneIdError` exists. + // - `zoneIdFileSize`: Either `"unknown"`, or an integer indicating the + // number of bytes in the zone identifier ADS. + // - `zoneIdBufferLargeEnough`: Either `"unknown"`, `"true"`, or `"false"`, + // indicating whether the file size was bigger + // than the maximum size that we will read from + // that file. + // - `zoneIdTruncated`: Either `"unknown"`, `"true"`, or `"false"`. Indicates + // whether or not we saw the end of the ADS file when we + // read from it. + // The above keys will be in the `"[Mozilla]"` section of the metadata. + // The other type of data that will go into the file is the directly copied + // data from the Zone Identifier ADS. This _should_ also be INI data, making + // the entirety of the file valid INI data. + // In the "good" case, this makes things very easy for us since INI reading + // functionality is already available. If we see an unexpected amount of + // telemetry data reporting that the INI is invalid, we will probably need to + // determine what other data formats are possible in that ADS. + // To make it easier to separate out the Zone Identifier data from the + // metadata, in that case, the metadata will always end with this sentinel, + // as long as `zoneIdData` contains valid data: + // `"\n[MozillaZoneIdentifierStartSentinel]\n"` + { + AString metadata; + AutoCharBuffer zoneIdData; + ReadZoneIdentifierData(fullPath, metadata, zoneIdData); + FString zoneIdDataFilePath(tempDirPath); + NFile::NName::NormalizeDirPathPrefix(zoneIdDataFilePath); + zoneIdDataFilePath += L"zoneIdProvenanceData"; + + NFile::NIO::COutFile zoneIdDataFile; + zoneIdDataFile.Create(zoneIdDataFilePath, true); + + UInt32 written = 0; + zoneIdDataFile.Write(metadata, metadata.Len(), written); + if (zoneIdData.length > 0 && zoneIdData.Buffer()) { + zoneIdDataFile.Write(zoneIdData.Buffer(), zoneIdData.length, written); + } + } + + if (extractOnly) { + return 0; + } + /* END Mozilla customizations */ + #ifndef UNDER_CE CCurrentDirRestorer currentDirRestorer; if (!SetCurrentDir(tempDirPath)) diff --git a/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/resource.h b/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/resource.h --- a/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/resource.h +++ b/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/resource.h @@ -4,3 +4,5 @@ #define IDS_EXTRACTION_ERROR_MESSAGE 8 #define IDS_CANNOT_CREATE_FOLDER 3003 #define IDS_PROGRESS_EXTRACTING 3300 +#define IDS_MIN_OS_TITLE 70 +#define IDS_MIN_OS_TEXT 71 diff --git a/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/resource.rc b/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/resource.rc --- a/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/resource.rc +++ b/other-licenses/7zstub/src/CPP/7zip/Bundles/SFXSetup/resource.rc @@ -11,6 +11,8 @@ BEGIN IDS_EXTRACTION_ERROR_MESSAGE "File is corrupt" IDS_CANNOT_CREATE_FOLDER "Cannot create folder '{0}'" IDS_PROGRESS_EXTRACTING "Extracting" + IDS_MIN_OS_TITLE "Setup Error" + IDS_MIN_OS_TEXT "Microsoft Windows 7 or newer is required." END #include "../../UI/FileManager/ProgressDialog.rc" diff --git a/other-licenses/7zstub/src/CPP/7zip/UI/FileManager/ProgressDialog.cpp b/other-licenses/7zstub/src/CPP/7zip/UI/FileManager/ProgressDialog.cpp --- a/other-licenses/7zstub/src/CPP/7zip/UI/FileManager/ProgressDialog.cpp +++ b/other-licenses/7zstub/src/CPP/7zip/UI/FileManager/ProgressDialog.cpp @@ -165,7 +165,8 @@ bool CProgressDialog::OnButtonClicked(in bool paused = Sync.GetPaused(); Sync.SetPaused(true); _inCancelMessageBox = true; - int res = ::MessageBoxW(*this, L"Are you sure you want to cancel?", _title, MB_YESNOCANCEL); + // Mozilla Customization - Removed redundant cancel button from dialog. + int res = ::MessageBoxW(*this, L"Are you sure you want to cancel?", _title, MB_YESNO); _inCancelMessageBox = false; Sync.SetPaused(paused); if (res == IDCANCEL || res == IDNO)