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)
+ stdcpp17atls.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}
+
+
+