Bug 1436475 Part 5 - Add a readme and a patch file with our changes to the 7-zip directory. r?agashlin draft
authorMatt Howell <mhowell@mozilla.com>
Tue, 13 Feb 2018 09:17:07 -0800
changeset 754615 c19dd1b23a8dba13d796390d93fd40d6e1070677
parent 754614 9955c19c3c72ac5673740d4c2159ef288470d510
push id98946
push usermhowell@mozilla.com
push dateTue, 13 Feb 2018 22:25:21 +0000
reviewersagashlin
bugs1436475
milestone60.0a1
Bug 1436475 Part 5 - Add a readme and a patch file with our changes to the 7-zip directory. r?agashlin MozReview-Commit-ID: G8NyEVL0qUx
other-licenses/7zstub/README.mozilla
other-licenses/7zstub/mozilla_customizations.diff
new file mode 100644
--- /dev/null
+++ b/other-licenses/7zstub/README.mozilla
@@ -0,0 +1,7 @@
+This directory contains the 7-zip code used in building the self-extracting Windows installer.
+
+The src/ directory contains an almost-unmodified copy of the 7-zip source code version 18.01, as downloaded from http://7-zip.org/a/7z1801-src.7z on February 12, 2018. A few modifications have been made to this copy of the source code. Those modifications are contained in mozilla_customizations.diff, which should be found in the same directory as this file.
+
+The firefox/ directory contains 7zSD.sfx, a 32-bit Windows executable built from the Visual C++ 6 project found in the src/CPP/7zip/Bundles/SFXSetup/ directory. The "ReleaseD" configuration was used. Including a compiled binary in the source tree is certainly not preferred, but is necessary in this case because the code does not build cleanly under more recent compilers. Using an older toolchain also allows us to keep this executable running on older Windows versions which are no longer supported by the build system used for the main application; that means we can show an error message specifically informing users that their OS is too old, instead of the generic "not a valid Win32 application" error that Windows would generate if we used the newer toolchain and that isn't very helpful to most users.
+
+The firefox/ directory also contains an icon and a manifest which were edited into 7zSD.sfx's resources after build time. The icon had to be added this way as opposed to just replacing the included 7-zip icon because the resource compiler included with VC6 does not support the 128x128 PNG image included in our icon file. Similarly, the manifest had to be edited in because the older resource compiler does not support manifests (and the 7-zip build files do not try to use any).
new file mode 100644
--- /dev/null
+++ b/other-licenses/7zstub/mozilla_customizations.diff
@@ -0,0 +1,459 @@
+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
+@@ -49,17 +49,17 @@ RSC=rc.exe
+ # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+ # ADD BASE RSC /l 0x419 /d "NDEBUG"
+ # ADD RSC /l 0x419 /d "NDEBUG"
+ BSC32=bscmake.exe
+ # ADD BASE BSC32 /nologo
+ # 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"
+ 
+ # PROP BASE Use_MFC 0
+ # PROP BASE Use_Debug_Libraries 1
+ # PROP BASE Output_Dir "Debug"
+ # PROP BASE Intermediate_Dir "Debug"
+@@ -76,17 +76,17 @@ LINK32=link.exe
+ # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+ # ADD BASE RSC /l 0x419 /d "_DEBUG"
+ # ADD RSC /l 0x419 /d "_DEBUG"
+ BSC32=bscmake.exe
+ # ADD BASE BSC32 /nologo
+ # 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"
+ 
+ # PROP BASE Use_MFC 0
+ # PROP BASE Use_Debug_Libraries 0
+ # PROP BASE Output_Dir "ReleaseD"
+ # PROP BASE Intermediate_Dir "ReleaseD"
+ # PROP BASE Ignore_Export_Lib 0
+@@ -102,19 +102,19 @@ LINK32=link.exe
+ # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+ # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+ # ADD BASE RSC /l 0x419 /d "NDEBUG"
+ # ADD RSC /l 0x419 /d "NDEBUG"
+ 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 
+ 
+ # Begin Target
+ 
+ # Name "SFXSetup - Win32 Release"
+ # Name "SFXSetup - Win32 Debug"
+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
+@@ -120,31 +120,226 @@ static void ShowErrorMessageSpec(const U
+   if (pos >= 0)
+   {
+     message.Delete(pos, 2);
+     message.Insert(pos, name);
+   }
+   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;
++}
++
++// 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
+     #else
+     LPSTR
+     #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();
+ 
+   UString archiveName, switches;
+   #ifdef _SHELL_EXECUTE
+   UString executeFile, executeParameters;
+   #endif
+   NCommandLineParser::SplitCommandLine(GetCommandLineW(), archiveName, switches);
+@@ -167,16 +362,30 @@ int APIENTRY WinMain(HINSTANCE hInstance
+     if (!assumeYes)
+       ShowErrorMessage(L"Can't load config info");
+     return 1;
+   }
+ 
+   UString dirPrefix ("." STRING_PATH_SEPARATOR);
+   UString appLaunched;
+   bool showProgress = true;
++
++  /* BEGIN Mozilla customizations */
++  bool extractOnly = false;
++  if (switches.IsPrefixedBy_NoCase(L"-ms") ||
++      switches.IsPrefixedBy_NoCase(L"/ini") ||
++      switches.IsPrefixedBy_NoCase(L"/s")) {
++    showProgress = false;
++  } else if (switches.IsPrefixedBy_NoCase(L"/extractdir=")) {
++    assumeYes = true;
++    showProgress = false;
++    extractOnly = true;
++  }
++  /* END Mozilla customizations */
++
+   if (!config.IsEmpty())
+   {
+     CObjectVector<CTextConfigPair> pairs;
+     if (!GetTextConfig(config, pairs))
+     {
+       if (!assumeYes)
+         ShowErrorMessage(L"Config failed");
+       return 1;
+@@ -199,17 +408,18 @@ int APIENTRY WinMain(HINSTANCE hInstance
+     
+     #ifdef _SHELL_EXECUTE
+     executeFile = GetTextConfigValue(pairs, "ExecuteFile");
+     executeParameters = GetTextConfigValue(pairs, "ExecuteParameters");
+     #endif
+   }
+ 
+   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");
+     return 1;
+   }
+ 
+   CCodecs *codecs = new CCodecs;
+   CMyComPtr<IUnknown> compressCodecsInfo = codecs;
+@@ -217,17 +427,19 @@ int APIENTRY WinMain(HINSTANCE hInstance
+     HRESULT result = codecs->Load();
+     if (result != S_OK)
+     {
+       ShowErrorMessage(L"Can not load codecs");
+       return 1;
+     }
+   }
+ 
+-  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;
+     UString errorMessage;
+     HRESULT result = ExtractArchive(codecs, fullPath, tempDirPath, showProgress,
+       isCorrupt, errorMessage);
+     
+     if (result != S_OK)
+@@ -245,16 +457,38 @@ int APIENTRY WinMain(HINSTANCE hInstance
+             errorMessage = NError::MyFormatMessage(result);
+           ::MessageBoxW(0, errorMessage, NWindows::MyLoadString(IDS_EXTRACTION_ERROR_TITLE), MB_ICONERROR);
+         }
+       }
+       return 1;
+     }
+   }
+ 
++  /* 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);
++    }
++  }
++
++  if (extractOnly) {
++    return 0;
++  }
++  /* END Mozilla customizations */
++
+   #ifndef UNDER_CE
+   CCurrentDirRestorer currentDirRestorer;
+   if (!SetCurrentDir(tempDirPath))
+     return 1;
+   #endif
+   
+   HANDLE hProcess = 0;
+ #ifdef _SHELL_EXECUTE
+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
+@@ -1,6 +1,8 @@
+ #define IDI_ICON  1
+ 
+ #define IDS_EXTRACTION_ERROR_TITLE       7
+ #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
+@@ -6,11 +6,13 @@ MY_VERSION_INFO_APP("7z Setup SFX", "7zS
+ IDI_ICON  ICON "setup.ico"
+ 
+ STRINGTABLE
+ BEGIN
+   IDS_EXTRACTION_ERROR_TITLE  "Extraction Failed"
+   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
+@@ -160,17 +160,18 @@ bool CProgressDialog::OnButtonClicked(in
+ {
+   switch (buttonID)
+   {
+     case IDCANCEL:
+     {
+       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)
+       {
+         if (_externalCloseMessageWasReceived)
+           OnExternalCloseMessage();
+         return true;
+       }