mirror of https://github.com/BOINC/boinc.git
- WINSETUP: Don't create the flag file until after the migration phase
has been completed. - WINSETUP: Use real quotes around the delete file command instead of tick marks, the Windows shell throws an error - WINSETUP: Instead of calling the Windows API to determinethe various special folders, use the MSI properties which match the 'choose folder' dialog. We can skip the step if doing a case insensitive compare. Plus it appears that MSI normalizes the paths for us. win_build/installerv2/redist/Windows/src/boinccas/ boinccas.cpp boinccas95.def boinccas95.vcproj CAMigrateBOINCData.cpp CAValidateRebootRequest.cpp, .h (Added) CAVerifyInstallDirectories.cpp win_build/installerv2/redist/Windows/Win32/ boinccas.dll boinccas95.dll win_build/installerv2/redist/Windows/x64/ boinccas.dll boinccas95.dll svn path=/trunk/boinc/; revision=15606
This commit is contained in:
parent
ba6526f8c9
commit
cd07d91c0f
|
@ -5757,3 +5757,28 @@ David 14 July 2008
|
|||
sched/
|
||||
sched_send.C
|
||||
server_types.h
|
||||
|
||||
Rom 15 July 2008
|
||||
- WINSETUP: Don't create the flag file until after the migration phase
|
||||
has been completed.
|
||||
- WINSETUP: Use real quotes around the delete file command instead of
|
||||
tick marks, the Windows shell throws an error
|
||||
- WINSETUP: Instead of calling the Windows API to determinethe various
|
||||
special folders, use the MSI properties which match the 'choose folder'
|
||||
dialog. We can skip the step if doing a case insensitive compare.
|
||||
Plus it appears that MSI normalizes the paths for us.
|
||||
|
||||
win_build/installerv2/redist/Windows/src/boinccas/
|
||||
boinccas.cpp
|
||||
boinccas95.def
|
||||
boinccas95.vcproj
|
||||
CAMigrateBOINCData.cpp
|
||||
CAValidateRebootRequest.cpp, .h (Added)
|
||||
CAVerifyInstallDirectories.cpp
|
||||
win_build/installerv2/redist/Windows/Win32/
|
||||
boinccas.dll
|
||||
boinccas95.dll
|
||||
win_build/installerv2/redist/Windows/x64/
|
||||
boinccas.dll
|
||||
boinccas95.dll
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -336,12 +336,11 @@ UINT CAMigrateBOINCData::OnExecution()
|
|||
tstring strWindowsSystemDirectory;
|
||||
tstring strProgramFilesDirectory;
|
||||
tstring strSystemDrive;
|
||||
tstring strMessage;
|
||||
tstring strVersionWindows64;
|
||||
struct _stat buf;
|
||||
ULONGLONG ullFileSize = 0;
|
||||
ULONGLONG ullDirectorySize = 0;
|
||||
ULONGLONG ullFreeDiskSpace = 0;
|
||||
TCHAR szBuffer[2048];
|
||||
UINT uiReturnValue = -1;
|
||||
|
||||
LogMessage(
|
||||
|
@ -372,6 +371,32 @@ UINT CAMigrateBOINCData::OnExecution()
|
|||
uiReturnValue = GetProperty( _T("ProductVersion"), strProductVersion );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
uiReturnValue = GetProperty( _T("VersionNT64"), strVersionWindows64 );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
uiReturnValue = GetProperty( _T("WindowsFolder"), strWindowsDirectory );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
uiReturnValue = GetProperty( _T("WindowsVolume"), strSystemDrive );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
if (strVersionWindows64.length() > 0)
|
||||
{
|
||||
uiReturnValue = GetProperty( _T("System64Folder"), strWindowsSystemDirectory );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
uiReturnValue = GetProperty( _T("ProgramFiles64Folder"), strProgramFilesDirectory );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
uiReturnValue = GetProperty( _T("SystemFolder"), strWindowsSystemDirectory );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
uiReturnValue = GetProperty( _T("ProgramFilesFolder"), strProgramFilesDirectory );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
}
|
||||
|
||||
|
||||
// If the REMOVE property is specified, then we are uninstalling BOINC, and
|
||||
// need to move things back to their orginal location.
|
||||
|
@ -397,90 +422,6 @@ UINT CAMigrateBOINCData::OnExecution()
|
|||
{
|
||||
strDestinationClientStateFile = strFutureDataDirectory + _T("\\client_state.xml");
|
||||
|
||||
// Determine the Windows directory
|
||||
if (GetWindowsDirectory(szBuffer, sizeof(szBuffer)/sizeof(TCHAR)))
|
||||
{
|
||||
strWindowsDirectory = tstring(szBuffer) + _T("\\");
|
||||
|
||||
strMessage = _T("strWindowsDirectory: '");
|
||||
strMessage += strWindowsDirectory;
|
||||
strMessage += _T("'");
|
||||
|
||||
LogMessage(
|
||||
INSTALLMESSAGE_INFO,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
strMessage.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
// Determine the system drive
|
||||
if (!strWindowsDirectory.empty())
|
||||
{
|
||||
TCHAR drive[_MAX_DRIVE];
|
||||
TCHAR dir[_MAX_DIR];
|
||||
TCHAR fname[_MAX_FNAME];
|
||||
TCHAR ext[_MAX_EXT];
|
||||
|
||||
_tsplitpath(strWindowsDirectory.c_str(), drive, dir, fname, ext);
|
||||
|
||||
strSystemDrive = tstring(drive) + _T("\\");
|
||||
|
||||
strMessage = _T("strSystemDrive: '");
|
||||
strMessage += strSystemDrive;
|
||||
strMessage += _T("'");
|
||||
|
||||
LogMessage(
|
||||
INSTALLMESSAGE_INFO,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
strMessage.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
// Determine the Windows System directory.
|
||||
if (GetSystemDirectory(szBuffer, sizeof(szBuffer)/sizeof(TCHAR)))
|
||||
{
|
||||
strWindowsSystemDirectory = tstring(szBuffer) + _T("\\");
|
||||
|
||||
strMessage = _T("strWindowsSystemDirectory: '");
|
||||
strMessage += strWindowsSystemDirectory;
|
||||
strMessage += _T("'");
|
||||
|
||||
LogMessage(
|
||||
INSTALLMESSAGE_INFO,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
strMessage.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
// Determine the Program Files directory.
|
||||
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, szBuffer)))
|
||||
{
|
||||
strProgramFilesDirectory = tstring(szBuffer) + _T("\\");
|
||||
|
||||
strMessage = _T("strProgramFilesDirectory: '");
|
||||
strMessage += strProgramFilesDirectory;
|
||||
strMessage += _T("'");
|
||||
|
||||
LogMessage(
|
||||
INSTALLMESSAGE_INFO,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
strMessage.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Perform some basic sanity tests to see if we need to migrate
|
||||
// anything.
|
||||
BOOL bClientStateExists =
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
// Berkeley Open Infrastructure for Network Computing
|
||||
// http://boinc.berkeley.edu
|
||||
// Copyright (C) 2005 University of California
|
||||
//
|
||||
// This is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation;
|
||||
// either version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This software is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Lesser General Public License for more details.
|
||||
//
|
||||
// To view the GNU Lesser General Public License visit
|
||||
// http://www.gnu.org/copyleft/lesser.html
|
||||
// or write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "boinccas.h"
|
||||
#include "CAValidateRebootRequest.h"
|
||||
|
||||
#define CUSTOMACTION_NAME _T("CAValidateRebootRequest")
|
||||
#define CUSTOMACTION_PROGRESSTITLE _T("Validating any reboot requests.")
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Function:
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
CAValidateRebootRequest::CAValidateRebootRequest(MSIHANDLE hMSIHandle) :
|
||||
BOINCCABase(hMSIHandle, CUSTOMACTION_NAME, CUSTOMACTION_PROGRESSTITLE)
|
||||
{}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Function:
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
CAValidateRebootRequest::~CAValidateRebootRequest()
|
||||
{
|
||||
BOINCCABase::~BOINCCABase();
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Function:
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
UINT CAValidateRebootRequest::OnExecution()
|
||||
{
|
||||
tstring strInstallDirectory;
|
||||
tstring strRebootRequested;
|
||||
tstring strRebootPendingFilename;
|
||||
FILE* fRebootPending;
|
||||
UINT uiReturnValue = -1;
|
||||
|
||||
|
||||
uiReturnValue = GetProperty( _T("INSTALLDIR"), strInstallDirectory );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
uiReturnValue = GetProperty( _T("RETURN_REBOOTREQUESTED"), strRebootRequested );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
|
||||
// Create reboot pending file
|
||||
//
|
||||
strRebootPendingFilename = strInstallDirectory + _T("\\RebootPending.txt");
|
||||
|
||||
fRebootPending = _tfopen(strRebootPendingFilename.c_str(), _T("wb"));
|
||||
if (fRebootPending) fclose(fRebootPending);
|
||||
|
||||
|
||||
// Create a registry key to delete the RebootInProgress.txt flag
|
||||
// file on a reboot.
|
||||
//
|
||||
if ( _T("1") == strRebootRequested )
|
||||
{
|
||||
LONG lReturnValue;
|
||||
HKEY hkSetupHive;
|
||||
|
||||
lReturnValue = RegCreateKeyEx(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"),
|
||||
0,
|
||||
NULL,
|
||||
REG_OPTION_NON_VOLATILE,
|
||||
KEY_READ | KEY_WRITE,
|
||||
NULL,
|
||||
&hkSetupHive,
|
||||
NULL
|
||||
);
|
||||
if (lReturnValue == ERROR_SUCCESS)
|
||||
{
|
||||
tstring strCommand;
|
||||
|
||||
strCommand = _T("cmd.exe /c \"del \"") + strRebootPendingFilename + _T("\"\"");
|
||||
|
||||
RegSetValueEx(
|
||||
hkSetupHive,
|
||||
_T("*BOINCRebootPendingCleanup"),
|
||||
0,
|
||||
REG_SZ,
|
||||
(CONST BYTE *)strCommand.c_str(),
|
||||
(DWORD)(strCommand.size()*sizeof(TCHAR))
|
||||
);
|
||||
|
||||
RegCloseKey(hkSetupHive);
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Function: ValidateRebootRequest
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
UINT __stdcall ValidateRebootRequest(MSIHANDLE hInstall)
|
||||
{
|
||||
UINT uiReturnValue = 0;
|
||||
|
||||
CAValidateRebootRequest* pCA = new CAValidateRebootRequest(hInstall);
|
||||
uiReturnValue = pCA->Execute();
|
||||
delete pCA;
|
||||
|
||||
return uiReturnValue;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// Berkeley Open Infrastructure for Network Computing
|
||||
// http://boinc.berkeley.edu
|
||||
// Copyright (C) 2005 University of California
|
||||
//
|
||||
// This is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation;
|
||||
// either version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This software is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Lesser General Public License for more details.
|
||||
//
|
||||
// To view the GNU Lesser General Public License visit
|
||||
// http://www.gnu.org/copyleft/lesser.html
|
||||
// or write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
//
|
||||
|
||||
#ifndef _CAVALIDATEREBOOTREQUEST_H_
|
||||
#define _CAVALIDATEREBOOTREQUEST_H_
|
||||
|
||||
|
||||
class CAValidateRebootRequest : public BOINCCABase
|
||||
{
|
||||
public:
|
||||
|
||||
CAValidateRebootRequest(MSIHANDLE hMSIHandle);
|
||||
~CAValidateRebootRequest();
|
||||
virtual UINT OnExecution();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -66,8 +66,7 @@ UINT CAVerifyInstallDirectories::OnExecution()
|
|||
tstring strWindowsSystemDirectory;
|
||||
tstring strProgramFilesDirectory;
|
||||
tstring strSystemDrive;
|
||||
tstring strMessage;
|
||||
TCHAR szBuffer[2048];
|
||||
tstring strVersionWindows64;
|
||||
UINT uiReturnValue = 0;
|
||||
|
||||
|
||||
|
@ -77,89 +76,31 @@ UINT CAVerifyInstallDirectories::OnExecution()
|
|||
uiReturnValue = GetProperty( _T("DATADIR"), strDataDirectory );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
|
||||
// Determine the Windows directory
|
||||
if (GetWindowsDirectory(szBuffer, sizeof(szBuffer)/sizeof(TCHAR)))
|
||||
{
|
||||
strWindowsDirectory = tstring(szBuffer) + _T("\\");
|
||||
|
||||
strMessage = _T("strWindowsDirectory: '");
|
||||
strMessage += strWindowsDirectory;
|
||||
strMessage += _T("'");
|
||||
|
||||
LogMessage(
|
||||
INSTALLMESSAGE_INFO,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
strMessage.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
// Determine the system drive
|
||||
if (!strWindowsDirectory.empty())
|
||||
{
|
||||
TCHAR drive[_MAX_DRIVE];
|
||||
TCHAR dir[_MAX_DIR];
|
||||
TCHAR fname[_MAX_FNAME];
|
||||
TCHAR ext[_MAX_EXT];
|
||||
|
||||
_tsplitpath(strWindowsDirectory.c_str(), drive, dir, fname, ext);
|
||||
|
||||
strSystemDrive = tstring(drive) + _T("\\");
|
||||
|
||||
strMessage = _T("strSystemDrive: '");
|
||||
strMessage += strSystemDrive;
|
||||
strMessage += _T("'");
|
||||
|
||||
LogMessage(
|
||||
INSTALLMESSAGE_INFO,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
strMessage.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
// Determine the Windows System directory.
|
||||
if (GetSystemDirectory(szBuffer, sizeof(szBuffer)/sizeof(TCHAR)))
|
||||
{
|
||||
strWindowsSystemDirectory = tstring(szBuffer) + _T("\\");
|
||||
|
||||
strMessage = _T("strWindowsSystemDirectory: '");
|
||||
strMessage += strWindowsSystemDirectory;
|
||||
strMessage += _T("'");
|
||||
|
||||
LogMessage(
|
||||
INSTALLMESSAGE_INFO,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
strMessage.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
// Determine the Program Files directory.
|
||||
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, szBuffer)))
|
||||
{
|
||||
strProgramFilesDirectory = tstring(szBuffer) + _T("\\");
|
||||
|
||||
strMessage = _T("strProgramFilesDirectory: '");
|
||||
strMessage += strProgramFilesDirectory;
|
||||
strMessage += _T("'");
|
||||
|
||||
LogMessage(
|
||||
INSTALLMESSAGE_INFO,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
strMessage.c_str()
|
||||
);
|
||||
}
|
||||
uiReturnValue = GetProperty( _T("VersionNT64"), strVersionWindows64 );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
uiReturnValue = GetProperty( _T("WindowsFolder"), strWindowsDirectory );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
uiReturnValue = GetProperty( _T("WindowsVolume"), strSystemDrive );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
if (strVersionWindows64.length() > 0)
|
||||
{
|
||||
uiReturnValue = GetProperty( _T("System64Folder"), strWindowsSystemDirectory );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
uiReturnValue = GetProperty( _T("ProgramFiles64Folder"), strProgramFilesDirectory );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
uiReturnValue = GetProperty( _T("SystemFolder"), strWindowsSystemDirectory );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
|
||||
uiReturnValue = GetProperty( _T("ProgramFilesFolder"), strProgramFilesDirectory );
|
||||
if ( uiReturnValue ) return uiReturnValue;
|
||||
}
|
||||
|
||||
|
||||
// Perform some basic sanity tests to see if we need to migrate
|
||||
|
|
|
@ -669,56 +669,6 @@ UINT BOINCCABase::LogMessage(
|
|||
/////////////////////////////////////////////////////////////////////
|
||||
UINT BOINCCABase::RebootWhenFinished()
|
||||
{
|
||||
tstring strInstallDirectory;
|
||||
tstring strRebootPending;
|
||||
FILE* fRebootPending;
|
||||
|
||||
GetProperty( _T("INSTALLDIR"), strInstallDirectory );
|
||||
|
||||
|
||||
// Create reboot pending file
|
||||
//
|
||||
strRebootPending = strInstallDirectory + _T("\\RebootPending.txt");
|
||||
|
||||
fRebootPending = _tfopen(strRebootPending.c_str(), _T("wb"));
|
||||
if (fRebootPending) fclose(fRebootPending);
|
||||
|
||||
|
||||
// Create a registry key to delete the RebootInProgress.txt flag
|
||||
// file on a reboot.
|
||||
//
|
||||
LONG lReturnValue;
|
||||
HKEY hkSetupHive;
|
||||
|
||||
lReturnValue = RegCreateKeyEx(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"),
|
||||
0,
|
||||
NULL,
|
||||
REG_OPTION_NON_VOLATILE,
|
||||
KEY_READ | KEY_WRITE,
|
||||
NULL,
|
||||
&hkSetupHive,
|
||||
NULL
|
||||
);
|
||||
if (lReturnValue == ERROR_SUCCESS)
|
||||
{
|
||||
tstring strCommand;
|
||||
|
||||
strCommand = _T("!cmd /c \"del '") + strRebootPending + _T("'\"");
|
||||
|
||||
RegSetValueEx(
|
||||
hkSetupHive,
|
||||
_T("BOINCRebootPendingCleanup"),
|
||||
0,
|
||||
REG_SZ,
|
||||
(CONST BYTE *)strCommand.c_str(),
|
||||
(DWORD)(strCommand.size()*sizeof(TCHAR))
|
||||
);
|
||||
|
||||
RegCloseKey(hkSetupHive);
|
||||
}
|
||||
|
||||
SetProperty(_T("RETURN_REBOOTREQUESTED"), _T("1"));
|
||||
return MsiSetMode(m_hMSIHandle, MSIRUNMODE_REBOOTATEND, TRUE);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ EXPORTS
|
|||
CleanupOldBinaries
|
||||
VerifyInstallDirectories
|
||||
ValidateInstall
|
||||
ValidateRebootRequest
|
||||
SaveExecutionState
|
||||
RestoreExecutionState
|
||||
|
|
@ -421,6 +421,10 @@
|
|||
RelativePath=".\CAValidateInstall.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CAValidateRebootRequest.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CAValidateSetupType.cpp"
|
||||
>
|
||||
|
@ -526,6 +530,10 @@
|
|||
RelativePath=".\CAValidateInstall.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CAValidateRebootRequest.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CAValidateSetupType.h"
|
||||
>
|
||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue