diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9aa33434944..2b07492b46a 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -123,13 +123,18 @@ jobs: - name: Build if: success() - run: msbuild win_build\boinc.sln -p:Configuration=${{matrix.configuration}} -p:Platform=${{matrix.platform}} -p:VcpkgTripletConfig=ci -p:DLIB=${{ env.DLIB }} -p:DMDF=${{ env.DMDF }} -m + run: msbuild win_build\boinc.sln -p:Configuration=${{matrix.configuration}} -p:Platform=${{matrix.platform}} -p:VcpkgTripletConfig=ci -p:DLIB=${{ env.DLIB }} -p:DMDF=${{ env.DMDF }} -p:BOINCCAS_TEST=true -m - name: Run unit tests if: success() && matrix.platform == 'x64' && matrix.configuration == 'Release' working-directory: win_build\Build\${{matrix.platform}}\${{matrix.configuration}} run: | ${{github.workspace}}\temp\OpenCppCoverage\OpenCppCoverage.exe --cover_children --optimized_build --sources ${{github.workspace}} --export_type=binary:gtest.cov -- unittests.exe --gtest_output=xml:gtest.xml + + - name: Run crypt_prog tests + if: success() && matrix.platform == 'x64' && matrix.configuration == 'Release' + working-directory: win_build\Build\${{matrix.platform}}\${{matrix.configuration}} + run: | ${{github.workspace}}\temp\OpenCppCoverage\OpenCppCoverage.exe --cover_children --optimized_build --modules ${{github.workspace}} --sources ${{github.workspace}} --input_coverage=gtest.cov --export_type=cobertura:cobertura.xml -- python ${{github.workspace}}\tests\crypt_prog_tests.py ${{github.workspace}}\win_build\Build\x64\${{matrix.configuration}}\crypt_prog.exe - name: Verify MSI file diff --git a/3rdParty/vcpkg_ports/configs/msbuild/vcpkg.json b/3rdParty/vcpkg_ports/configs/msbuild/vcpkg.json index ebe622e0b61..e4e21247569 100644 --- a/3rdParty/vcpkg_ports/configs/msbuild/vcpkg.json +++ b/3rdParty/vcpkg_ports/configs/msbuild/vcpkg.json @@ -21,6 +21,8 @@ { "name": "libzip", "default-features": false - } + }, + "wil", + "tinyxml2" ] } diff --git a/clientsetup/win/CAAnnounceUpgrade.cpp b/clientsetup/win/CAAnnounceUpgrade.cpp index 41b2df35df0..318cd7538c5 100644 --- a/clientsetup/win/CAAnnounceUpgrade.cpp +++ b/clientsetup/win/CAAnnounceUpgrade.cpp @@ -1,84 +1,32 @@ -// Berkeley Open Infrastructure for Network Computing -// http://boinc.berkeley.edu -// Copyright (C) 2005 University of California +// This file is part of BOINC. +// https://boinc.berkeley.edu +// Copyright (C) 2025 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. +// BOINC 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 3 of the License, or (at your option) any later version. // -// This software is distributed in the hope that it will be useful, +// BOINC 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 -// +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . #include "stdafx.h" #include "boinccas.h" #include "CAAnnounceUpgrade.h" -#define CUSTOMACTION_NAME _T("CAAnnounceUpgrade") -#define CUSTOMACTION_PROGRESSTITLE _T("Announce the new BOINC version to all components.") - - -///////////////////////////////////////////////////////////////////// -// -// Function: -// -// Description: -// -///////////////////////////////////////////////////////////////////// CAAnnounceUpgrade::CAAnnounceUpgrade(MSIHANDLE hMSIHandle) : - BOINCCABase(hMSIHandle, CUSTOMACTION_NAME, CUSTOMACTION_PROGRESSTITLE) -{} - - -///////////////////////////////////////////////////////////////////// -// -// Function: -// -// Description: -// -///////////////////////////////////////////////////////////////////// -CAAnnounceUpgrade::~CAAnnounceUpgrade() -{ - BOINCCABase::~BOINCCABase(); -} + BOINCCABase(hMSIHandle, _T("CAAnnounceUpgrade"), + _T("Announce the new BOINC version to all components.")) {} - -///////////////////////////////////////////////////////////////////// -// -// Function: -// -// Description: -// -///////////////////////////////////////////////////////////////////// -UINT CAAnnounceUpgrade::OnExecution() -{ +UINT CAAnnounceUpgrade::OnExecution() { return SetUpgradeParameters(); } - -///////////////////////////////////////////////////////////////////// -// -// Function: AnnounceUpgrade -// -// Description: -// -///////////////////////////////////////////////////////////////////// -UINT __stdcall AnnounceUpgrade(MSIHANDLE hInstall) -{ - UINT uiReturnValue = 0; - - CAAnnounceUpgrade* pCA = new CAAnnounceUpgrade(hInstall); - uiReturnValue = pCA->Execute(); - delete pCA; - - return uiReturnValue; +UINT __stdcall AnnounceUpgrade(MSIHANDLE hInstall) { + return CAAnnounceUpgrade(hInstall).Execute(); } - diff --git a/clientsetup/win/CAAnnounceUpgrade.h b/clientsetup/win/CAAnnounceUpgrade.h index 6eac233add3..846e8d45ed2 100644 --- a/clientsetup/win/CAAnnounceUpgrade.h +++ b/clientsetup/win/CAAnnounceUpgrade.h @@ -1,36 +1,25 @@ -// Berkeley Open Infrastructure for Network Computing -// http://boinc.berkeley.edu -// Copyright (C) 2005 University of California +// This file is part of BOINC. +// https://boinc.berkeley.edu +// Copyright (C) 2025 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. +// BOINC 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 3 of the License, or (at your option) any later version. // -// This software is distributed in the hope that it will be useful, +// BOINC 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 _CAANNOUNCEUPGRADE_H_ -#define _CAANNOUNCEUPGRADE_H_ +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . +#pragma once class CAAnnounceUpgrade : public BOINCCABase { public: - CAAnnounceUpgrade(MSIHANDLE hMSIHandle); - ~CAAnnounceUpgrade(); - virtual UINT OnExecution(); + UINT OnExecution() override final; }; - - -#endif - diff --git a/clientsetup/win/CACleanupOldBinaries.cpp b/clientsetup/win/CACleanupOldBinaries.cpp index 97a7b09ef4d..5c1567259c8 100644 --- a/clientsetup/win/CACleanupOldBinaries.cpp +++ b/clientsetup/win/CACleanupOldBinaries.cpp @@ -1,71 +1,40 @@ -// Berkeley Open Infrastructure for Network Computing -// http://boinc.berkeley.edu -// Copyright (C) 2005 University of California +// This file is part of BOINC. +// https://boinc.berkeley.edu +// Copyright (C) 2025 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. +// BOINC 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 3 of the License, or (at your option) any later version. // -// This software is distributed in the hope that it will be useful, +// BOINC 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 -// +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . #include "stdafx.h" #include "boinccas.h" #include "CACleanupOldBinaries.h" -#define CUSTOMACTION_NAME _T("CACleanupOldBinaries") -#define CUSTOMACTION_PROGRESSTITLE _T("Cleanup any old binaries that were left lying around from some other install.") - - -///////////////////////////////////////////////////////////////////// -// -// Function: -// -// Description: -// -///////////////////////////////////////////////////////////////////// CACleanupOldBinaries::CACleanupOldBinaries(MSIHANDLE hMSIHandle) : - BOINCCABase(hMSIHandle, CUSTOMACTION_NAME, CUSTOMACTION_PROGRESSTITLE) -{} - - -///////////////////////////////////////////////////////////////////// -// -// Function: -// -// Description: -// -///////////////////////////////////////////////////////////////////// -CACleanupOldBinaries::~CACleanupOldBinaries() -{ - BOINCCABase::~BOINCCABase(); -} - - -///////////////////////////////////////////////////////////////////// -// -// Function: -// -// Description: -// -///////////////////////////////////////////////////////////////////// -UINT CACleanupOldBinaries::OnExecution() -{ - tstring strInstallDirectory; - UINT uiReturnValue; - - - uiReturnValue = GetProperty( _T("INSTALLDIR"), strInstallDirectory ); - if ( uiReturnValue ) return uiReturnValue; + BOINCCABase(hMSIHandle, _T("CACleanupOldBinaries"), + _T("Cleanup any old binaries that were left lying around from some " + "other install.")) {} + +UINT CACleanupOldBinaries::OnExecution() { + tstring strInstallDirectory; + + const auto uiReturnValue = + GetProperty(_T("INSTALLDIR"), strInstallDirectory); + if (uiReturnValue != ERROR_SUCCESS) { + return uiReturnValue; + } + if (strInstallDirectory.empty()) { + return ERROR_INSTALL_FAILURE; + } DeleteFile(tstring(strInstallDirectory + _T("\\boinc.exe")).c_str()); DeleteFile(tstring(strInstallDirectory + _T("\\boincmgr.exe")).c_str()); @@ -83,21 +52,6 @@ UINT CACleanupOldBinaries::OnExecution() return ERROR_SUCCESS; } - -///////////////////////////////////////////////////////////////////// -// -// Function: CleanupOldBinaries -// -// Description: -// -///////////////////////////////////////////////////////////////////// -UINT __stdcall CleanupOldBinaries(MSIHANDLE hInstall) -{ - UINT uiReturnValue = 0; - - CACleanupOldBinaries* pCA = new CACleanupOldBinaries(hInstall); - uiReturnValue = pCA->Execute(); - delete pCA; - - return uiReturnValue; +UINT __stdcall CleanupOldBinaries(MSIHANDLE hInstall) { + return CACleanupOldBinaries(hInstall).Execute(); } diff --git a/clientsetup/win/CACleanupOldBinaries.h b/clientsetup/win/CACleanupOldBinaries.h index 13acf672304..f9a1e4ffe8c 100644 --- a/clientsetup/win/CACleanupOldBinaries.h +++ b/clientsetup/win/CACleanupOldBinaries.h @@ -1,37 +1,25 @@ -// Berkeley Open Infrastructure for Network Computing -// http://boinc.berkeley.edu -// Copyright (C) 2005 University of California +// This file is part of BOINC. +// https://boinc.berkeley.edu +// Copyright (C) 2025 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. +// BOINC 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 3 of the License, or (at your option) any later version. // -// This software is distributed in the hope that it will be useful, +// BOINC 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 _CACLEANUPOLDBINARIES_H_ -#define _CACLEANUPOLDBINARIES_H_ +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . +#pragma once class CACleanupOldBinaries : public BOINCCABase { public: - CACleanupOldBinaries(MSIHANDLE hMSIHandle); - ~CACleanupOldBinaries(); - virtual UINT OnExecution(); - + UINT OnExecution() override final; }; - - -#endif - diff --git a/clientsetup/win/CACreateAcctMgrLoginFile.cpp b/clientsetup/win/CACreateAcctMgrLoginFile.cpp index 4e1ef412654..d61859da0a9 100644 --- a/clientsetup/win/CACreateAcctMgrLoginFile.cpp +++ b/clientsetup/win/CACreateAcctMgrLoginFile.cpp @@ -1,124 +1,78 @@ -// Berkeley Open Infrastructure for Network Computing -// http://boinc.berkeley.edu -// Copyright (C) 2005 University of California +// This file is part of BOINC. +// https://boinc.berkeley.edu +// Copyright (C) 2025 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. +// BOINC 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 3 of the License, or (at your option) any later version. // -// This software is distributed in the hope that it will be useful, +// BOINC 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., -// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . #include "stdafx.h" #include "boinccas.h" #include "CACreateAcctMgrLoginFile.h" - -#define CUSTOMACTION_NAME _T("CACreateAcctMgrLoginFile") -#define CUSTOMACTION_PROGRESSTITLE _T("Store account manager initialization data") - - -///////////////////////////////////////////////////////////////////// -// -// Function: -// -// Description: -// -///////////////////////////////////////////////////////////////////// CACreateAcctMgrLoginFile::CACreateAcctMgrLoginFile(MSIHANDLE hMSIHandle) : - BOINCCABase(hMSIHandle, CUSTOMACTION_NAME, CUSTOMACTION_PROGRESSTITLE) -{} - - -///////////////////////////////////////////////////////////////////// -// -// Function: -// -// Description: -// -///////////////////////////////////////////////////////////////////// -CACreateAcctMgrLoginFile::~CACreateAcctMgrLoginFile() -{ - BOINCCABase::~BOINCCABase(); + BOINCCABase(hMSIHandle, _T("CACreateAcctMgrLoginFile"), + _T("Store account manager initialization data")) { } +UINT CACreateAcctMgrLoginFile::OnExecution() { + tstring strDataDirectory; + auto uiReturnValue = GetProperty(_T("DATADIR"), strDataDirectory); + if (uiReturnValue != ERROR_SUCCESS) { + return uiReturnValue; + } + if (strDataDirectory.empty()) { + return ERROR_INSTALL_FAILURE; + } + if (!std::filesystem::exists(strDataDirectory)) { + return ERROR_INSTALL_FAILURE; + } -///////////////////////////////////////////////////////////////////// -// -// Function: -// -// Description: -// -///////////////////////////////////////////////////////////////////// -UINT CACreateAcctMgrLoginFile::OnExecution() -{ - tstring strDataDirectory; - tstring strAcctMgrLogin; - tstring strAcctMgrPasswordHash; - tstring strAcctMgrLoginFile; - UINT uiReturnValue; - - - uiReturnValue = GetProperty( _T("DATADIR"), strDataDirectory ); - if ( uiReturnValue ) return uiReturnValue; - - uiReturnValue = GetProperty( _T("ACCTMGR_LOGIN"), strAcctMgrLogin ); - if ( uiReturnValue ) return uiReturnValue; - - uiReturnValue = GetProperty( _T("ACCTMGR_PASSWORDHASH"), strAcctMgrPasswordHash ); - if ( uiReturnValue ) return uiReturnValue; - - - if (!strAcctMgrLogin.empty()) { - - // The project_init.xml file is stored in the data directory. - // - strAcctMgrLoginFile = strDataDirectory + _T("\\acct_mgr_login.xml"); - - FILE* fAcctMgrLoginFile = _tfopen(strAcctMgrLoginFile.c_str(), _T("w")); + tstring strAcctMgrLogin; + uiReturnValue = GetProperty(_T("ACCTMGR_LOGIN"), strAcctMgrLogin); + if (uiReturnValue != ERROR_SUCCESS) { + return uiReturnValue; + } - _ftprintf( - fAcctMgrLoginFile, - _T("\n") - _T(" %s\n") - _T(" %s\n") - _T("\n"), - strAcctMgrLogin.c_str(), - !strAcctMgrPasswordHash.empty() ? strAcctMgrPasswordHash.c_str() : _T("") - ); + if (strAcctMgrLogin.empty()) { + return ERROR_SUCCESS; + } - fclose(fAcctMgrLoginFile); + tstring strAcctMgrPasswordHash; + uiReturnValue = GetProperty(_T("ACCTMGR_PASSWORDHASH"), + strAcctMgrPasswordHash); + if (uiReturnValue != ERROR_SUCCESS) { + return uiReturnValue; } + // The project_init.xml file is stored in the data directory. + // + const auto strAcctMgrLoginFile = + strDataDirectory + _T("\\acct_mgr_login.xml"); + auto fAcctMgrLoginFile = _tfopen(strAcctMgrLoginFile.c_str(), _T("w")); + _ftprintf( + fAcctMgrLoginFile, + _T("\n") + _T(" %s\n") + _T(" %s\n") + _T("\n"), + strAcctMgrLogin.c_str(), + strAcctMgrPasswordHash.c_str() + ); + fclose(fAcctMgrLoginFile); + return ERROR_SUCCESS; } - -///////////////////////////////////////////////////////////////////// -// -// Function: CreateAcctMgrLoginFile -// -// Description: This custom action stores the account manager login data -// specified on the commandline in a file in the data -// directory. -// -///////////////////////////////////////////////////////////////////// -UINT __stdcall CreateAcctMgrLoginFile(MSIHANDLE hInstall) -{ - UINT uiReturnValue = 0; - - CACreateAcctMgrLoginFile* pCA = new CACreateAcctMgrLoginFile(hInstall); - uiReturnValue = pCA->Execute(); - delete pCA; - - return uiReturnValue; +UINT __stdcall CreateAcctMgrLoginFile(MSIHANDLE hInstall) { + return CACreateAcctMgrLoginFile(hInstall).Execute(); } diff --git a/clientsetup/win/CACreateAcctMgrLoginFile.h b/clientsetup/win/CACreateAcctMgrLoginFile.h index f12b20e666c..c1057015d13 100644 --- a/clientsetup/win/CACreateAcctMgrLoginFile.h +++ b/clientsetup/win/CACreateAcctMgrLoginFile.h @@ -1,37 +1,25 @@ -// Berkeley Open Infrastructure for Network Computing -// http://boinc.berkeley.edu -// Copyright (C) 2005 University of California +// This file is part of BOINC. +// https://boinc.berkeley.edu +// Copyright (C) 2025 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. +// BOINC 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 3 of the License, or (at your option) any later version. // -// This software is distributed in the hope that it will be useful, +// BOINC 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., -// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// - -#ifndef _CACREATEPACCTMGRLOGINFILE_H_ -#define _CACREATEPACCTMGRLOGINFILE_H_ +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . +#pragma once class CACreateAcctMgrLoginFile : public BOINCCABase { public: - CACreateAcctMgrLoginFile(MSIHANDLE hMSIHandle); - ~CACreateAcctMgrLoginFile(); - virtual UINT OnExecution(); - + UINT OnExecution() override final; }; - - -#endif - diff --git a/clientsetup/win/boinccas.cpp b/clientsetup/win/boinccas.cpp index d6044582ea6..a4da8ef56c9 100644 --- a/clientsetup/win/boinccas.cpp +++ b/clientsetup/win/boinccas.cpp @@ -141,24 +141,23 @@ static BOOL IsVersionNewer(const tstring v1, const tstring v2) { return FALSE; } - -///////////////////////////////////////////////////////////////////// -// -// Function: SetUpgradeParameters -// -// Description: -// -///////////////////////////////////////////////////////////////////// -UINT BOINCCABase::SetUpgradeParameters() -{ +UINT BOINCCABase::SetUpgradeParameters() { tstring strCurrentProductVersion; - UINT uiReturnValue = 0; - uiReturnValue = GetProperty( _T("ProductVersion"), strCurrentProductVersion ); - if ( uiReturnValue ) return uiReturnValue; + auto uiReturnValue = GetProperty(_T("ProductVersion"), + strCurrentProductVersion); + if (uiReturnValue) { + return uiReturnValue; + } + if (strCurrentProductVersion.empty()) { + return ERROR_INSTALL_FAILURE; + } - uiReturnValue = SetRegistryValue( _T("UpgradingTo"), strCurrentProductVersion ); - if ( uiReturnValue ) return uiReturnValue; + uiReturnValue = SetRegistryValue(_T("UpgradingTo"), + strCurrentProductVersion); + if (uiReturnValue) { + return uiReturnValue; + } return ERROR_SUCCESS; } diff --git a/clientsetup/win/stdafx.h b/clientsetup/win/stdafx.h index c6d0ba0b17a..f080450b0af 100644 --- a/clientsetup/win/stdafx.h +++ b/clientsetup/win/stdafx.h @@ -101,6 +101,7 @@ #include #include #include +#include // Misc Includes #include diff --git a/tests/unit-tests/boinccas/boinccas_helper.cpp b/tests/unit-tests/boinccas/boinccas_helper.cpp new file mode 100644 index 00000000000..14baf67e089 --- /dev/null +++ b/tests/unit-tests/boinccas/boinccas_helper.cpp @@ -0,0 +1,240 @@ +// This file is part of BOINC. +// https://boinc.berkeley.edu +// Copyright (C) 2025 University of California +// +// BOINC 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 3 of the License, or (at your option) any later version. +// +// BOINC 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. +// +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . + +#include +#include "boinccas_helper.h" +#include +#include +#include + +MsiHelper::MsiHelper() { + cleanup(); + init(); +} + +MsiHelper::~MsiHelper() { + cleanup(); +} + +void MsiHelper::init() { + auto result = MsiOpenDatabase(msiName, MSIDBOPEN_CREATE, &hMsi); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("MsiOpenDatabase failed: " + + std::to_string(result)); + } + fillSummaryInformationTable(); + createPropertiesTable(); + insertProperties({ + {"ProductCode", "{3F18E95A-7D04-4807-839E-23A535627A86}"}, + {"Manufacturer", "Test Manufacturer"}, + {"ProductLanguage", "1033"}, + {"ProductName", "Test"} + }); + result = MsiDatabaseCommit(hMsi); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("MsiDatabaseCommit failed: " + + std::to_string(result)); + } +} + +void MsiHelper::cleanup() { + if (hMsi != 0) { + MsiCloseHandle(hMsi); + hMsi = 0; + } + try { + std::filesystem::remove(std::filesystem::current_path() / msiName); + } + catch (const std::exception& ex) { + throw std::runtime_error( "Failed to remove existing MSI file: " + + std::string(ex.what())); + } +} + +void MsiHelper::createTable(const std::string_view& sql_create) { + PMSIHANDLE hView; + auto result = MsiDatabaseOpenView(hMsi, sql_create.data(), &hView); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("Error creating view: " + + std::to_string(result)); + } + result = MsiViewExecute(hView, 0); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("Error executing view: " + + std::to_string(result)); + } + result = MsiViewClose(hView); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("Error closing view: " + + std::to_string(result)); + } + result = MsiDatabaseCommit(hMsi); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("MsiDatabaseCommit failed: " + + std::to_string(result)); + } +} + +void MsiHelper::createPropertiesTable() { + constexpr std::string_view sql_create = "CREATE TABLE `Property` " + "(`Property` CHAR(72) NOT NULL, `Value` LONGCHAR NOT NULL LOCALIZABLE " + "PRIMARY KEY `Property`)"; + createTable(sql_create); +} + +void MsiHelper::insertProperties( + const std::vector>& properties) { + constexpr std::string_view sql_insert = + "INSERT INTO `Property` (`Property`, `Value`) " + "VALUES (?, ?)"; + PMSIHANDLE hView; + auto result = MsiDatabaseOpenView(hMsi, sql_insert.data(), &hView); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("Error creating view: " + + std::to_string(result)); + } + for (const auto& record : properties) { + const auto hRecord = MsiCreateRecord(2); + if (hRecord == 0) { + throw std::runtime_error("Failed to create record"); + } + result = MsiRecordSetString(hRecord, 1, record.first.c_str()); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("Failed to set record, errorcode: " + + std::to_string(result)); + } + result = MsiRecordSetString(hRecord, 2, record.second.c_str()); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("Failed to set record, errorcode: " + + std::to_string(result)); + } + + result = MsiViewExecute(hView, hRecord); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("Error inserting record: " + + std::to_string(result)); + } + result = MsiCloseHandle(hRecord); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("Error closing record: " + + std::to_string(result)); + } + } + + result = MsiViewClose(hView); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("Error closing view: " + + std::to_string(result)); + } + + result = MsiDatabaseCommit(hMsi); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("MsiDatabaseCommit failed: " + + std::to_string(result)); + } +} + +void MsiHelper::fillSummaryInformationTable() { + PMSIHANDLE hSummaryInfo; + constexpr auto updateCount = 4; + + auto result = MsiGetSummaryInformation(hMsi, nullptr, updateCount, + &hSummaryInfo); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("MsiGetSummaryInformation failed: " + + std::to_string(result)); + } + result = MsiSummaryInfoSetProperty(hSummaryInfo, 2, VT_LPSTR, 0, nullptr, + "Test"); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("MsiSummaryInfoSetProperty 2 failed: " + + std::to_string(result)); + } + result = MsiSummaryInfoSetProperty(hSummaryInfo, 7, VT_LPSTR, 0, nullptr, + "Intel;1033"); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("MsiSummaryInfoSetProperty 7 failed: " + + std::to_string(result)); + } + result = MsiSummaryInfoSetProperty(hSummaryInfo, 9, VT_LPSTR, 0, nullptr, + "{2C4296B7-9E88-4CD8-9FC6-26CE7B053ED1}"); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("MsiSummaryInfoSetProperty 9 failed: " + + std::to_string(result)); + } + result = MsiSummaryInfoSetProperty(hSummaryInfo, 14, VT_I4, 200, nullptr, + nullptr); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("MsiSummaryInfoSetProperty 14 failed: " + + std::to_string(result)); + } + result = MsiSummaryInfoPersist(hSummaryInfo); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("MsiSummaryInfoPersist failed:" + + std::to_string(result)); + } +} + +constexpr auto registryKey = +"SOFTWARE\\Space Sciences Laboratory, U.C. Berkeley\\BOINC Setup"; + +std::string getRegistryValue(const std::string& valueName) { + HKEY hKey = nullptr; + const auto openResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registryKey, 0, + KEY_READ, &hKey); + if (openResult != ERROR_SUCCESS) { + return {}; + } + + DWORD type = 0; + DWORD size = 0; + auto queryResult = RegQueryValueEx(hKey, valueName.c_str(), nullptr, + &type, nullptr, &size); + if (queryResult != ERROR_SUCCESS) { + RegCloseKey(hKey); + return {}; + } + + std::string value; + value.resize(size); + queryResult = RegQueryValueEx(hKey, valueName.c_str(), nullptr, &type, + reinterpret_cast(value.data()), &size); + RegCloseKey(hKey); + + if (queryResult != ERROR_SUCCESS) { + return {}; + } + + // Only REG_SZ and REG_EXPAND_SZ are expected; trim at first NUL. + const auto nulPos = value.find('\0'); + if (nulPos != std::string::npos) { + value.resize(nulPos); + } + + return value; +} + +void cleanRegistryKey() { + HKEY hKey = nullptr; + const auto openResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registryKey, 0, + KEY_WRITE, &hKey); + if (openResult != ERROR_SUCCESS) { + return; + } + RegDeleteKey(HKEY_LOCAL_MACHINE, registryKey); + RegCloseKey(hKey); +} diff --git a/tests/unit-tests/boinccas/boinccas_helper.h b/tests/unit-tests/boinccas/boinccas_helper.h new file mode 100644 index 00000000000..b45099db97a --- /dev/null +++ b/tests/unit-tests/boinccas/boinccas_helper.h @@ -0,0 +1,61 @@ +// This file is part of BOINC. +// https://boinc.berkeley.edu +// Copyright (C) 2025 University of California +// +// BOINC 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 3 of the License, or (at your option) any later version. +// +// BOINC 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. +// +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . + +#pragma once + +#include +#include "wil/resource.h" +#include + +template +std::pair load_function_from_boinccas( + const std::string& function_name) { + wil::unique_hmodule dll(LoadLibrary("boinccas.dll")); + if (!dll) { + throw std::runtime_error("Failed to load boinccas.dll"); + } + auto func = reinterpret_cast(GetProcAddress(dll.get(), + function_name.c_str())); + if (!func) { + throw std::runtime_error("Failed to load function: " + function_name); + } + return { std::move(dll), func }; +} + +std::string getRegistryValue(const std::string& valueName); +void cleanRegistryKey(); + +constexpr auto msiName = "test.msi"; + +class MsiHelper { +public: + MsiHelper(); + ~MsiHelper(); + void insertProperties( + const std::vector>& properties); + std::string getMsiHandle() const { + return "#" + std::to_string(hMsi); + } + +private: + void init(); + void cleanup(); + void fillSummaryInformationTable(); + void createPropertiesTable(); + void createTable(const std::string_view& sql_create); + MSIHANDLE hMsi = 0; +}; diff --git a/tests/unit-tests/boinccas/test_boinccas_CAAnnounceUpgrade.cpp b/tests/unit-tests/boinccas/test_boinccas_CAAnnounceUpgrade.cpp new file mode 100644 index 00000000000..4bf2ad6d23b --- /dev/null +++ b/tests/unit-tests/boinccas/test_boinccas_CAAnnounceUpgrade.cpp @@ -0,0 +1,72 @@ +// This file is part of BOINC. +// https://boinc.berkeley.edu +// Copyright (C) 2025 University of California +// +// BOINC 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 3 of the License, or (at your option) any later version. +// +// BOINC 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. +// +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . + +#include "gtest/gtest.h" + +#include "boinccas_helper.h" + +using namespace std; + +namespace test_boinccas_CAAnnounceUpgrade { + using AnnounceUpgradeFn = UINT(WINAPI*)(MSIHANDLE); + + class test_boinccas_CAAnnounceUpgrade : public ::testing::Test { + protected: + test_boinccas_CAAnnounceUpgrade() { + std::tie(hDll, hFunc) = + load_function_from_boinccas( + "AnnounceUpgrade"); + cleanRegistryKey(); + } + ~test_boinccas_CAAnnounceUpgrade() override { + cleanRegistryKey(); + } + + AnnounceUpgradeFn hFunc = nullptr; + MsiHelper msiHelper; + private: + wil::unique_hmodule hDll = nullptr; + }; + + constexpr auto expectedVersion = "1.2.3.4"; +#ifdef BOINCCAS_TEST + TEST_F(test_boinccas_CAAnnounceUpgrade, + Empty_ProductVersion) { + PMSIHANDLE hMsi; + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + + EXPECT_NE(0u, hFunc(hMsi)); + EXPECT_NE(expectedVersion, getRegistryValue("UpgradingTo")); + } + + TEST_F(test_boinccas_CAAnnounceUpgrade, + With_ProductVersion) { + msiHelper.insertProperties({ + {"ProductVersion", expectedVersion} + }); + PMSIHANDLE hMsi; + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + + EXPECT_EQ(0u, hFunc(hMsi)); + EXPECT_EQ(expectedVersion, getRegistryValue("UpgradingTo")); + } +#endif +} diff --git a/tests/unit-tests/boinccas/test_boinccas_CACleanupOldBinaries.cpp b/tests/unit-tests/boinccas/test_boinccas_CACleanupOldBinaries.cpp new file mode 100644 index 00000000000..c4b05f591cf --- /dev/null +++ b/tests/unit-tests/boinccas/test_boinccas_CACleanupOldBinaries.cpp @@ -0,0 +1,157 @@ +// This file is part of BOINC. +// https://boinc.berkeley.edu +// Copyright (C) 2025 University of California +// +// BOINC 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 3 of the License, or (at your option) any later version. +// +// BOINC 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. +// +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . + +#include +#include + +#include "gtest/gtest.h" + +#include "boinccas_helper.h" + +using namespace std; + +namespace test_boinccas_CACleanupOldBinaries { + using CleanupOldBinariesFn = UINT(WINAPI*)(MSIHANDLE); + + class test_boinccas_CACleanupOldBinaries : public ::testing::Test { + protected: + test_boinccas_CACleanupOldBinaries() { + std::tie(hDll, hFunc) = + load_function_from_boinccas( + "CleanupOldBinaries"); + } + + void TearDown() override { + if (!testDir.empty() && std::filesystem::exists(testDir)) { + std::filesystem::remove_all(testDir); + } + } + + void createDummyFile(const std::filesystem::path& filePath) { + std::ofstream ofs(filePath); + ofs << "dummy content"; + ofs.close(); + } + + void createTestFilesInDir(const std::filesystem::path& dirPath) { + createDummyFile(dirPath / "boinc.exe"); + createDummyFile(dirPath / "boincmgr.exe"); + createDummyFile(dirPath / "boinccmd.exe"); + createDummyFile(dirPath / "boinc.dll"); + createDummyFile(dirPath / "libcurl.dll"); + createDummyFile(dirPath / "libeay32.dll"); + createDummyFile(dirPath / "ssleay32.dll"); + createDummyFile(dirPath / "zlib1.dll"); + createDummyFile(dirPath / "dbghelp.dll"); + createDummyFile(dirPath / "dbghelp95.dll"); + createDummyFile(dirPath / "srcsrv.dll"); + createDummyFile(dirPath / "symsrv.dll"); + } + + CleanupOldBinariesFn hFunc = nullptr; + MsiHelper msiHelper; + std::filesystem::path testDir; + private: + wil::unique_hmodule hDll = nullptr; + }; + +#ifdef BOINCCAS_TEST + TEST_F(test_boinccas_CACleanupOldBinaries, + Empty_INSTALLDIR_Property) { + PMSIHANDLE hMsi; + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + + EXPECT_NE(0u, hFunc(hMsi)); + } + + TEST_F(test_boinccas_CACleanupOldBinaries, + NonExistent_INSTALLDIR_Directory) { + const auto dir = + std::filesystem::current_path() /= "non_existent_directory"; + msiHelper.insertProperties({ + {"INSTALLDIR", dir.string().c_str()} + }); + + PMSIHANDLE hMsi; + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + + EXPECT_EQ(0u, hFunc(hMsi)); + } + + TEST_F(test_boinccas_CACleanupOldBinaries, + Empty_INSTALLDIR_Directory) { + testDir = std::filesystem::current_path() /= "empty"; + std::filesystem::create_directory(testDir); + msiHelper.insertProperties({ + {"INSTALLDIR", testDir.string().c_str()} + }); + + PMSIHANDLE hMsi; + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + + EXPECT_EQ(0u, hFunc(hMsi)); + } + + TEST_F(test_boinccas_CACleanupOldBinaries, + NonEmpty_INSTALLDIR_Directory_RemoveAllListedFiles) { + testDir = std::filesystem::current_path() /= "non_empty"; + std::filesystem::create_directory(testDir); + createTestFilesInDir(testDir); + + msiHelper.insertProperties({ + {"INSTALLDIR", testDir.string().c_str()} + }); + PMSIHANDLE hMsi; + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + EXPECT_EQ(0u, hFunc(hMsi)); + // verify that the directory is now empty + EXPECT_TRUE(std::filesystem::is_empty(testDir)); + } + + TEST_F(test_boinccas_CACleanupOldBinaries, + NonEmpty_INSTALLDIR_Directory_KeepUnListedFiles) { + testDir = std::filesystem::current_path() /= "non_empty"; + std::filesystem::create_directory(testDir); + createTestFilesInDir(testDir); + createDummyFile(testDir / "dummy.bin"); + createDummyFile(testDir / "dummy.exe"); + createDummyFile(testDir / "dummy.dll"); + + msiHelper.insertProperties({ + {"INSTALLDIR", testDir.string().c_str()} + }); + PMSIHANDLE hMsi; + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + EXPECT_EQ(0u, hFunc(hMsi)); + // verify that the directory is now empty + EXPECT_FALSE(std::filesystem::is_empty(testDir)); + EXPECT_TRUE(std::filesystem::exists(testDir / "dummy.bin")); + EXPECT_TRUE(std::filesystem::exists(testDir / "dummy.exe")); + EXPECT_TRUE(std::filesystem::exists(testDir / "dummy.dll")); + } +#endif +} diff --git a/tests/unit-tests/boinccas/test_boinccas_CACreateAcctMgrLoginFile.cpp b/tests/unit-tests/boinccas/test_boinccas_CACreateAcctMgrLoginFile.cpp new file mode 100644 index 00000000000..966714f6f78 --- /dev/null +++ b/tests/unit-tests/boinccas/test_boinccas_CACreateAcctMgrLoginFile.cpp @@ -0,0 +1,219 @@ +// This file is part of BOINC. +// https://boinc.berkeley.edu +// Copyright (C) 2025 University of California +// +// BOINC 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 3 of the License, or (at your option) any later version. +// +// BOINC 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. +// +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . + +#include +#include + +#include "gtest/gtest.h" +#include "tinyxml2.h" + +#include "boinccas_helper.h" + +using namespace std; + +namespace test_boinccas_CACreateAcctMgrLoginFile { + using CreateAcctMgrLoginFileFn = UINT(WINAPI*)(MSIHANDLE); + + class test_boinccas_CACreateAcctMgrLoginFile : public ::testing::Test { + protected: + test_boinccas_CACreateAcctMgrLoginFile() { + std::tie(hDll, hFunc) = + load_function_from_boinccas( + "CreateAcctMgrLoginFile"); + } + + void TearDown() override { + if (!testDir.empty() && std::filesystem::exists(testDir)) { + std::filesystem::remove_all(testDir); + } + } + + void createDummyFile(const std::filesystem::path& filePath) { + std::ofstream ofs(filePath); + ofs << "dummy content"; + ofs.close(); + } + + CreateAcctMgrLoginFileFn hFunc = nullptr; + MsiHelper msiHelper; + std::filesystem::path testDir; + private: + wil::unique_hmodule hDll = nullptr; + }; + + struct AccountData { + std::string login; + std::string passwordHash; + }; + + std::pair parseAccountXml(const std::string& filename) + { + AccountData result; + + tinyxml2::XMLDocument doc; + const auto err = doc.LoadFile(filename.c_str()); + if (err != tinyxml2::XML_SUCCESS) { + return { false, result }; + } + + auto* root = doc.FirstChildElement("acct_mgr_login"); + if (!root) { + return { false, result }; + } + + const auto* const loginElem = root->FirstChildElement("login"); + if (loginElem && loginElem->GetText()) { + result.login = loginElem->GetText(); + } + + const auto* const hashElem = root->FirstChildElement("password_hash"); + if (hashElem && hashElem->GetText()) { + result.passwordHash = hashElem->GetText(); + } + + return { true, result }; + } + +#ifdef BOINCCAS_TEST + TEST_F(test_boinccas_CACreateAcctMgrLoginFile, + Empty_DATADIR_Property) { + PMSIHANDLE hMsi; + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + + EXPECT_NE(0u, hFunc(hMsi)); + } + + TEST_F(test_boinccas_CACreateAcctMgrLoginFile, + Empty_DATADIR_Directory) { + PMSIHANDLE hMsi; + const auto dir = std::filesystem::current_path() /= "test_data"; + msiHelper.insertProperties({ + {"DATADIR", dir.string().c_str()} + }); + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + + EXPECT_NE(0u, hFunc(hMsi)); + } + + TEST_F(test_boinccas_CACreateAcctMgrLoginFile, + Empty_DATADIR_Directory_And_ACCTMGR_LOGIN_Property_Set) { + PMSIHANDLE hMsi; + testDir = std::filesystem::current_path() /= "test_data"; + std::filesystem::create_directory(testDir); + msiHelper.insertProperties({ + {"DATADIR", testDir.string().c_str()}, + {"ACCTMGR_LOGIN", "testuser"} + }); + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + + EXPECT_EQ(0u, hFunc(hMsi)); + } + + TEST_F(test_boinccas_CACreateAcctMgrLoginFile, + Empty_ACCTMGR_LOGIN_Property) { + PMSIHANDLE hMsi; + testDir = std::filesystem::current_path() /= "test_data"; + std::filesystem::create_directory(testDir); + msiHelper.insertProperties({ + {"DATADIR", testDir.string().c_str()} + }); + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + + EXPECT_EQ(0u, hFunc(hMsi)); + } + + TEST_F(test_boinccas_CACreateAcctMgrLoginFile, + ACCTMGR_LOGIN_Property_Set_And_Empty_ACCTMGR_PASSWORDHASH_Property) { + PMSIHANDLE hMsi; + testDir = std::filesystem::current_path() /= "test_data"; + std::filesystem::create_directory(testDir); + msiHelper.insertProperties({ + {"DATADIR", testDir.string().c_str()}, + {"ACCTMGR_LOGIN", "testuser"} + }); + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + + EXPECT_EQ(0u, hFunc(hMsi)); + const auto acctMgrLoginFile = testDir / "acct_mgr_login.xml"; + EXPECT_TRUE(std::filesystem::exists(acctMgrLoginFile)); + auto [parsed, accountData] = + parseAccountXml(acctMgrLoginFile.string()); + EXPECT_TRUE(parsed); + EXPECT_EQ("testuser", accountData.login); + EXPECT_TRUE(accountData.passwordHash.empty()); + } + + TEST_F(test_boinccas_CACreateAcctMgrLoginFile, + ACCTMGR_LOGIN_And_ACCTMGR_PASSWORDHASH_Properties_Set) { + PMSIHANDLE hMsi; + testDir = std::filesystem::current_path() /= "test_data"; + std::filesystem::create_directory(testDir); + msiHelper.insertProperties({ + {"DATADIR", testDir.string().c_str()}, + {"ACCTMGR_LOGIN", "testuser"}, + {"ACCTMGR_PASSWORDHASH", "abcd1234hashvalue"} + }); + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + EXPECT_EQ(0u, hFunc(hMsi)); + const auto acctMgrLoginFile = testDir / "acct_mgr_login.xml"; + EXPECT_TRUE(std::filesystem::exists(acctMgrLoginFile)); + auto [parsed, accountData] = + parseAccountXml(acctMgrLoginFile.string()); + EXPECT_TRUE(parsed); + EXPECT_EQ("testuser", accountData.login); + EXPECT_EQ("abcd1234hashvalue", accountData.passwordHash); + } + + TEST_F(test_boinccas_CACreateAcctMgrLoginFile, + acct_mgr_login_xml_overwrite) { + PMSIHANDLE hMsi; + testDir = std::filesystem::current_path() /= "test_data"; + std::filesystem::create_directory(testDir); + const auto acctMgrLoginFile = testDir / "acct_mgr_login.xml"; + std::ofstream ofs(acctMgrLoginFile); + ofs << "dummy content"; + ofs.close(); + msiHelper.insertProperties({ + {"DATADIR", testDir.string().c_str()}, + {"ACCTMGR_LOGIN", "testuser"}, + {"ACCTMGR_PASSWORDHASH", "abcd1234hashvalue"} + }); + const auto result = MsiOpenPackage(msiHelper.getMsiHandle().c_str(), + &hMsi); + ASSERT_EQ(0u, result); + EXPECT_EQ(0u, hFunc(hMsi)); + EXPECT_TRUE(std::filesystem::exists(acctMgrLoginFile)); + auto [parsed, accountData] = + parseAccountXml(acctMgrLoginFile.string()); + EXPECT_TRUE(parsed); + EXPECT_EQ("testuser", accountData.login); + EXPECT_EQ("abcd1234hashvalue", accountData.passwordHash); + } +#endif +} diff --git a/tests/unit-tests/boinccas/test_boinccas_CACreateBOINCAccounts.cpp b/tests/unit-tests/boinccas/test_boinccas_CACreateBOINCAccounts.cpp new file mode 100644 index 00000000000..1106cee92f3 --- /dev/null +++ b/tests/unit-tests/boinccas/test_boinccas_CACreateBOINCAccounts.cpp @@ -0,0 +1,50 @@ +// This file is part of BOINC. +// https://boinc.berkeley.edu +// Copyright (C) 2025 University of California +// +// BOINC 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 3 of the License, or (at your option) any later version. +// +// BOINC 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. +// +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . + +#include +#include + +#include "gtest/gtest.h" + +#include "boinccas_helper.h" + +using namespace std; + +namespace test_boinccas_CACreateBOINCAccounts { + using CreateBOINCAccountsFn = UINT(WINAPI*)(MSIHANDLE); + + class test_boinccas_CACreateBOINCAccounts : public ::testing::Test { + protected: + test_boinccas_CACreateBOINCAccounts() { + std::tie(hDll, hFunc) = + load_function_from_boinccas( + "CreateBOINCAccounts"); + } + + void TearDown() override { + } + + CreateBOINCAccountsFn hFunc = nullptr; + MsiHelper msiHelper; + private: + wil::unique_hmodule hDll = nullptr; + }; + +#ifndef BOINCCAS_TEST + +#endif +} diff --git a/win_build/boinc.sln b/win_build/boinc.sln index 573cf892482..960a42b63f5 100644 --- a/win_build/boinc.sln +++ b/win_build/boinc.sln @@ -116,8 +116,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vcpkg_3rdparty_dependencies EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittests", "unittests.vcxproj", "{E3CFBBA5-0BBC-4237-B3F8-7E7729DF7E42}" ProjectSection(ProjectDependencies) = postProject - {D3E5B5B5-4FB1-4877-9B2C-6708B3D568F7} = {D3E5B5B5-4FB1-4877-9B2C-6708B3D568F7} + {49723CA5-DA05-43C0-93AB-6FD30D046919} = {49723CA5-DA05-43C0-93AB-6FD30D046919} {753E897D-9ECE-42B1-9F0D-CD566775C77E} = {753E897D-9ECE-42B1-9F0D-CD566775C77E} + {D3E5B5B5-4FB1-4877-9B2C-6708B3D568F7} = {D3E5B5B5-4FB1-4877-9B2C-6708B3D568F7} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crypt_prog", "crypt_prog.vcxproj", "{C04F0F2C-B75D-4628-8656-6163B28BD69E}" diff --git a/win_build/boinccas.vcxproj b/win_build/boinccas.vcxproj index 32ad716325b..8c1b1afe6e9 100644 --- a/win_build/boinccas.vcxproj +++ b/win_build/boinccas.vcxproj @@ -16,6 +16,7 @@ ../win_build;../lib;%(AdditionalIncludeDirectories) Use _USRDLL;BOINCCAS_EXPORTS;%(PreprocessorDefinitions) + stdcpp17 atls.lib;msi.lib;delayimp.lib;netapi32.lib;version.lib;%(AdditionalDependencies) diff --git a/win_build/installer.vcxproj b/win_build/installer.vcxproj index 46a8abd8719..231504af35b 100644 --- a/win_build/installer.vcxproj +++ b/win_build/installer.vcxproj @@ -17,8 +17,9 @@ stdcpp17 - libcmtd.lib;libcpmtd.lib;msi.lib;cabinet.lib;Version.lib;%(AdditionalDependencies) - libcmt.lib;libcpmt.lib;msi.lib;cabinet.lib;Version.lib;%(AdditionalDependencies) + msi.lib;cabinet.lib;Version.lib;%(AdditionalDependencies) + libcmtd.lib;libcpmtd.lib;%(AdditionalDependencies) + libcmt.lib;libcpmt.lib;%(AdditionalDependencies) Console diff --git a/win_build/unittests.vcxproj b/win_build/unittests.vcxproj index ca394fbe53f..40692c927f7 100644 --- a/win_build/unittests.vcxproj +++ b/win_build/unittests.vcxproj @@ -18,10 +18,11 @@ ../win_build;../lib;../zip;%(AdditionalIncludeDirectories) _CONSOLE;%(PreprocessorDefinitions) + BOINCCAS_TEST;%(PreprocessorDefinitions) stdcpp17 - gtest_main.lib;gmock_main.lib;zip.lib;%(AdditionalDependencies) + gtest_main.lib;gmock_main.lib;zip.lib;msi.lib;cabinet.lib;Version.lib;tinyxml2.lib;%(AdditionalDependencies) zlibd.lib;%(AdditionalDependencies) zlib.lib;%(AdditionalDependencies) $(VcpkgInstalledDir)/debug/lib/manual-link;%(AdditionalLibraryDirectories) @@ -30,6 +31,11 @@ + + + + + @@ -47,6 +53,9 @@ {753e897d-9ece-42b1-9f0d-cd566775c77e} + + +