From ff1c0ce8944fa19c5db3f4e415ff76cff3866e14 Mon Sep 17 00:00:00 2001 From: Pawel Sikorski Date: Thu, 13 Aug 2015 14:25:16 +0200 Subject: [PATCH] RequestMode (Global/User) type introduction Change-Id: I862d7c7df1ceb0f41b4daf4e34490ef21fb480ab --- src/common/CMakeLists.txt | 1 + src/common/context_installer.h | 5 ++++- src/common/pkgmgr_interface.h | 2 +- src/common/pkgmgr_registration.cc | 22 +++++++++++++--------- src/common/pkgmgr_registration.h | 14 ++++++++++---- src/common/recovery_file.h | 2 +- src/common/request.cc | 21 +++++++++++++++++++++ src/common/{request_type.h => request.h} | 18 +++++++++++++++--- src/common/step/step_configure.cc | 13 ++++++++----- src/common/step/step_configure.h | 1 + src/common/step/step_recover_application.cc | 17 +++++++++++------ src/common/step/step_recovery.cc | 2 +- src/common/step/step_register_app.cc | 11 +++++++---- src/common/step/step_unregister_app.cc | 11 +++++++---- src/common/step/step_update_app.cc | 11 +++++++---- src/tpk/tpk_app_query_interface.cc | 3 ++- src/unit_tests/smoke_test.cc | 23 +++++++++-------------- src/wgt/step/step_encrypt_resources.cc | 2 +- src/wgt/step/step_remove_encryption_data.cc | 2 +- src/wgt/wgt_app_query_interface.cc | 3 ++- 20 files changed, 123 insertions(+), 61 deletions(-) create mode 100644 src/common/request.cc rename src/common/{request_type.h => request.h} (54%) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index f965dee..6f35406 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -7,6 +7,7 @@ SET(SRCS pkgmgr_registration.cc pkgmgr_signal.cc recovery_file.cc + request.cc security_registration.cc step/step_backup_icons.cc step/step_backup_manifest.cc diff --git a/src/common/context_installer.h b/src/common/context_installer.h index 0438372..3e9d640 100644 --- a/src/common/context_installer.h +++ b/src/common/context_installer.h @@ -20,7 +20,7 @@ #include #include "common/recovery_file.h" -#include "common/request_type.h" +#include "common/request.h" #include "common/utils/property.h" namespace common_installer { @@ -144,6 +144,9 @@ class ContextInstaller { // information for recovery Property recovery_info; + + // user type of request (GLOBAL/USER) + Property request_mode; }; } // namespace common_installer diff --git a/src/common/pkgmgr_interface.h b/src/common/pkgmgr_interface.h index 98a4ae0..29a9106 100644 --- a/src/common/pkgmgr_interface.h +++ b/src/common/pkgmgr_interface.h @@ -10,7 +10,7 @@ #include #include "common/app_query_interface.h" -#include "common/request_type.h" +#include "common/request.h" #include "common/utils/macros.h" #include "common/utils/logging.h" diff --git a/src/common/pkgmgr_registration.cc b/src/common/pkgmgr_registration.cc index 0578aff..69ff5fa 100644 --- a/src/common/pkgmgr_registration.cc +++ b/src/common/pkgmgr_registration.cc @@ -58,8 +58,10 @@ namespace common_installer { bool RegisterAppInPkgmgr(const bf::path& xml_path, const std::string& pkgid, - const CertificateInfo& cert_info, uid_t uid) { - int ret = uid != tzplatform_getuid(TZ_SYS_GLOBALAPP_USER) ? + const CertificateInfo& cert_info, + uid_t uid, + RequestMode request_mode) { + int ret = request_mode != RequestMode::GLOBAL ? pkgmgr_parser_parse_usr_manifest_for_installation( xml_path.c_str(), uid, const_cast(kAppinstTags)) : pkgmgr_parser_parse_manifest_for_installation( @@ -81,8 +83,9 @@ bool RegisterAppInPkgmgr(const bf::path& xml_path, } bool UpgradeAppInPkgmgr(const bf::path& xml_path, const std::string& pkgid, - const CertificateInfo& cert_info, uid_t uid) { - int ret = uid != tzplatform_getuid(TZ_SYS_GLOBALAPP_USER) ? + const CertificateInfo& cert_info, uid_t uid, + RequestMode request_mode) { + int ret = request_mode != RequestMode::GLOBAL ? pkgmgr_parser_parse_usr_manifest_for_upgrade( xml_path.string().c_str(), uid, const_cast(kAppinstTags)) : @@ -106,8 +109,10 @@ bool UpgradeAppInPkgmgr(const bf::path& xml_path, const std::string& pkgid, } bool UnregisterAppInPkgmgr(const bf::path& xml_path, - const std::string& pkgid, uid_t uid) { - int ret = uid != tzplatform_getuid(TZ_SYS_GLOBALAPP_USER) ? + const std::string& pkgid, + uid_t uid, + RequestMode request_mode) { + int ret = request_mode != RequestMode::GLOBAL ? pkgmgr_parser_parse_usr_manifest_for_uninstallation( xml_path.string().c_str(), uid, const_cast(kAppinstTags)) : @@ -155,7 +160,7 @@ std::string QueryCertificateAuthorCertificate(const std::string& pkgid, return old_author_certificate; } -bool IsPackageInstalled(const std::string& pkg_id, uid_t uid) { +bool IsPackageInstalled(const std::string& pkg_id, RequestMode request_mode) { pkgmgrinfo_pkginfo_h handle; int ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkg_id.c_str(), getuid(), &handle); @@ -167,8 +172,7 @@ bool IsPackageInstalled(const std::string& pkg_id, uid_t uid) { pkgmgrinfo_pkginfo_destroy_pkginfo(handle); return false; } - bool global_user = uid == tzplatform_getuid(TZ_SYS_GLOBALAPP_USER); - if (!global_user && is_global) { + if (request_mode != RequestMode::GLOBAL && is_global) { pkgmgrinfo_pkginfo_destroy_pkginfo(handle); return false; } diff --git a/src/common/pkgmgr_registration.h b/src/common/pkgmgr_registration.h index 5d152b6..43ef34b 100644 --- a/src/common/pkgmgr_registration.h +++ b/src/common/pkgmgr_registration.h @@ -17,15 +17,21 @@ namespace common_installer { bool RegisterAppInPkgmgr(const boost::filesystem::path& xml_path, const std::string& pkgid, - const CertificateInfo& cert_info, uid_t uid); + const CertificateInfo& cert_info, + uid_t uid, + RequestMode request_mode); bool UpgradeAppInPkgmgr(const boost::filesystem::path& xml_path, const std::string& pkgid, - const CertificateInfo& cert_info, uid_t uid); + const CertificateInfo& cert_info, + uid_t uid, + RequestMode request_mode); bool UnregisterAppInPkgmgr(const boost::filesystem::path& xml_path, - const std::string& pkgid, uid_t uid); + const std::string& pkgid, + uid_t uid, + RequestMode request_mode); std::string QueryCertificateAuthorCertificate(const std::string& pkgid, uid_t uid); -bool IsPackageInstalled(const std::string& pkg_id, uid_t uid); +bool IsPackageInstalled(const std::string& pkg_id, RequestMode request_mode); } // namespace common_installer diff --git a/src/common/recovery_file.h b/src/common/recovery_file.h index 4c775c7..95358fd 100644 --- a/src/common/recovery_file.h +++ b/src/common/recovery_file.h @@ -10,7 +10,7 @@ #include #include -#include "common/request_type.h" +#include "common/request.h" namespace common_installer { diff --git a/src/common/request.cc b/src/common/request.cc new file mode 100644 index 0000000..6f9bbf5 --- /dev/null +++ b/src/common/request.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by an apache-2.0 license that can be +// found in the LICENSE file. + +#include "common/request.h" +#include +#include + +namespace common_installer { + +RequestMode GetRequestMode() { + return (getuid() == tzplatform_getuid(TZ_SYS_GLOBALAPP_USER)) ? + RequestMode::GLOBAL : RequestMode::USER; +} + +const char *GetRootAppPath() { + return GetRequestMode() == RequestMode::USER ? + tzplatform_getenv(TZ_USER_APP) : tzplatform_getenv(TZ_SYS_RW_APP); +} + +} // namespace common_installer diff --git a/src/common/request_type.h b/src/common/request.h similarity index 54% rename from src/common/request_type.h rename to src/common/request.h index 3a430bd..d248f9f 100644 --- a/src/common/request_type.h +++ b/src/common/request.h @@ -2,8 +2,8 @@ // Use of this source code is governed by an apache-2.0 license that can be // found in the LICENSE file. -#ifndef COMMON_REQUEST_TYPE_H_ -#define COMMON_REQUEST_TYPE_H_ +#ifndef COMMON_REQUEST_H_ +#define COMMON_REQUEST_H_ namespace common_installer { @@ -17,7 +17,19 @@ enum class RequestType : int { Recovery }; +/** Request mode (USER vs GLOBAL) */ +enum class RequestMode : int { + USER, + GLOBAL +}; + +/** Get mode for current request (GLOBAL/USER) */ +RequestMode GetRequestMode(); + +/** Get apps root path for current request (GLOBAL/USER) */ +const char *GetRootAppPath(); + } // namespace common_installer -#endif // COMMON_REQUEST_TYPE_H_ +#endif // COMMON_REQUEST_H_ diff --git a/src/common/step/step_configure.cc b/src/common/step/step_configure.cc index 9bd7409..8df7685 100644 --- a/src/common/step/step_configure.cc +++ b/src/common/step/step_configure.cc @@ -7,7 +7,7 @@ #include #include -#include "common/request_type.h" +#include "common/request.h" #include "common/utils/file_util.h" namespace common_installer { @@ -21,6 +21,8 @@ StepConfigure::StepConfigure(ContextInstaller* context, PkgMgrPtr pkgmgr) } Step::Status StepConfigure::process() { + SetupRequestMode(); + if (!SetupRootAppDirectory()) return Status::ERROR; @@ -93,10 +95,7 @@ Step::Status StepConfigure::clean() { bool StepConfigure::SetupRootAppDirectory() { if (context_->root_application_path.get().empty()) { - std::string root_app_path = - context_->uid.get() != tzplatform_getuid(TZ_SYS_GLOBALAPP_USER) - ? tzplatform_getenv(TZ_USER_APP) - : tzplatform_getenv(TZ_SYS_RW_APP); + std::string root_app_path = GetRootAppPath(); if (root_app_path.empty()) return false; @@ -115,5 +114,9 @@ bool StepConfigure::SetupRootAppDirectory() { return true; } +void StepConfigure::SetupRequestMode() { + context_->request_mode.set(GetRequestMode()); +} + } // namespace configuration } // namespace common_installer diff --git a/src/common/step/step_configure.h b/src/common/step/step_configure.h index d7344d0..f05e720 100644 --- a/src/common/step/step_configure.h +++ b/src/common/step/step_configure.h @@ -30,6 +30,7 @@ class StepConfigure : public Step { Status precheck() override; private: bool SetupRootAppDirectory(); + void SetupRequestMode(); PkgMgrPtr pkgmgr_; diff --git a/src/common/step/step_recover_application.cc b/src/common/step/step_recover_application.cc index e127636..ad948b0 100644 --- a/src/common/step/step_recover_application.cc +++ b/src/common/step/step_recover_application.cc @@ -17,8 +17,10 @@ namespace pkgmgr { Step::Status StepRecoverApplication::RecoveryNew() { if (!SetXmlPaths()) return Status::OK; - UnregisterAppInPkgmgr(context_->xml_path.get(), context_->pkgid.get(), - context_->uid.get()); + UnregisterAppInPkgmgr(context_->xml_path.get(), + context_->pkgid.get(), + context_->uid.get(), + context_->request_mode.get()); return Status::OK; } @@ -29,12 +31,15 @@ Step::Status StepRecoverApplication::RecoveryUpdate() { } bf::path xml_path = bf::exists(context_->backup_xml_path.get()) ? context_->backup_xml_path.get() : context_->xml_path.get(); - UnregisterAppInPkgmgr(xml_path, context_->pkgid.get(), - context_->uid.get()); + UnregisterAppInPkgmgr(xml_path, + context_->pkgid.get(), + context_->uid.get(), + context_->request_mode.get()); if (!RegisterAppInPkgmgr(xml_path, - context_->pkgid.get().c_str(), + context_->pkgid.get(), context_->certificate_info.get(), - context_->uid.get())) { + context_->uid.get(), + context_->request_mode.get())) { LOG(ERROR) << "Unsuccessful app registration"; return Status::ERROR; } diff --git a/src/common/step/step_recovery.cc b/src/common/step/step_recovery.cc index c38a66e..ad371cd 100644 --- a/src/common/step/step_recovery.cc +++ b/src/common/step/step_recovery.cc @@ -4,7 +4,7 @@ #include "common/step/step_recovery.h" -#include "common/request_type.h" +#include "common/request.h" #include "common/utils/logging.h" namespace common_installer { diff --git a/src/common/step/step_register_app.cc b/src/common/step/step_register_app.cc index 0b050b6..1dea8ad 100644 --- a/src/common/step/step_register_app.cc +++ b/src/common/step/step_register_app.cc @@ -34,9 +34,10 @@ Step::Status StepRegisterApplication::precheck() { Step::Status StepRegisterApplication::process() { if (!RegisterAppInPkgmgr(context_->xml_path.get(), - context_->pkgid.get().c_str(), + context_->pkgid.get(), context_->certificate_info.get(), - context_->uid.get())) { + context_->uid.get(), + context_->request_mode.get())) { LOG(ERROR) << "Failed to register the app"; return Step::Status::ERROR; } @@ -46,8 +47,10 @@ Step::Status StepRegisterApplication::process() { } Step::Status StepRegisterApplication::undo() { - if (!UnregisterAppInPkgmgr(context_->xml_path.get(), context_->pkgid.get(), - context_->uid.get())) { + if (!UnregisterAppInPkgmgr(context_->xml_path.get(), + context_->pkgid.get(), + context_->uid.get(), + context_->request_mode.get())) { LOG(ERROR) << "Application couldn't be unregistered"; return Status::ERROR; } diff --git a/src/common/step/step_unregister_app.cc b/src/common/step/step_unregister_app.cc index 9743aaa..2180a3e 100755 --- a/src/common/step/step_unregister_app.cc +++ b/src/common/step/step_unregister_app.cc @@ -76,8 +76,10 @@ Step::Status StepUnregisterApplication::process() { return Status::ERROR; } - if (!UnregisterAppInPkgmgr(context_->xml_path.get(), context_->pkgid.get(), - context_->uid.get())) { + if (!UnregisterAppInPkgmgr(context_->xml_path.get(), + context_->pkgid.get(), + context_->uid.get(), + context_->request_mode.get())) { LOG(ERROR) << "Failed to unregister package into database"; return Status::ERROR; } @@ -93,9 +95,10 @@ Step::Status StepUnregisterApplication::process() { Step::Status StepUnregisterApplication::undo() { if (!RegisterAppInPkgmgr(context_->backup_xml_path.get(), - context_->pkgid.get().c_str(), + context_->pkgid.get(), context_->certificate_info.get(), - context_->uid.get())) { + context_->uid.get(), + context_->request_mode.get())) { LOG(ERROR) << "Failed to restore the app registration in pkgmgr"; return Step::Status::ERROR; } diff --git a/src/common/step/step_update_app.cc b/src/common/step/step_update_app.cc index 037a2b1..bdec89c 100644 --- a/src/common/step/step_update_app.cc +++ b/src/common/step/step_update_app.cc @@ -34,8 +34,10 @@ Step::Status StepUpdateApplication::precheck() { Step::Status StepUpdateApplication::process() { if (!UpgradeAppInPkgmgr(context_->xml_path.get(), - context_->pkgid.get(), context_->certificate_info.get(), - context_->uid.get())) { + context_->pkgid.get(), + context_->certificate_info.get(), + context_->uid.get(), + context_->request_mode.get())) { LOG(ERROR) << "Cannot upgrade manifest for application"; return Status::ERROR; } @@ -63,8 +65,9 @@ Step::Status StepUpdateApplication::undo() { } if (!UpgradeAppInPkgmgr(context_->backup_xml_path.get(), - context_->pkgid.get(), certificate_info, - context_->uid.get())) { + context_->pkgid.get(), certificate_info, + context_->uid.get(), + context_->request_mode.get())) { LOG(ERROR) << "Cannot revert manifest for application"; return Status::ERROR; } diff --git a/src/tpk/tpk_app_query_interface.cc b/src/tpk/tpk_app_query_interface.cc index 40b0bff..6a06b13 100644 --- a/src/tpk/tpk_app_query_interface.cc +++ b/src/tpk/tpk_app_query_interface.cc @@ -15,6 +15,7 @@ #include #include "common/pkgmgr_registration.h" +#include "common/request.h" #include "common/utils/file_util.h" #include "common/utils/logging.h" #include "tpk/xml_parser/xml_parser.h" @@ -87,7 +88,7 @@ bool TpkAppQueryInterface::IsAppInstalledByArgv(int argc, char** argv) { std::string pkg_id = GetPkgIdFromPath(path); if (pkg_id.empty()) return false; - return ci::IsPackageInstalled(pkg_id, getuid()); + return ci::IsPackageInstalled(pkg_id, ci::GetRequestMode()); } } // namespace tpk diff --git a/src/unit_tests/smoke_test.cc b/src/unit_tests/smoke_test.cc index e443176..b682421 100644 --- a/src/unit_tests/smoke_test.cc +++ b/src/unit_tests/smoke_test.cc @@ -20,6 +20,7 @@ #include "common/backup_paths.h" #include "common/pkgmgr_interface.h" #include "common/pkgmgr_registration.h" +#include "common/request.h" #include "common/step/step_fail.h" #include "tpk/tpk_app_query_interface.h" #include "tpk/tpk_installer.h" @@ -65,8 +66,7 @@ class StepCrash : public ci::Step { }; void RemoveAllRecoveryFiles() { - bf::path root_path = getuid() != tzplatform_getuid(TZ_SYS_GLOBALAPP_USER) - ? tzplatform_getenv(TZ_USER_APP) : tzplatform_getenv(TZ_SYS_RW_APP); + bf::path root_path = ci::GetRootAppPath(); for (auto& dir_entry : boost::make_iterator_range( bf::directory_iterator(root_path), bf::directory_iterator())) { if (bf::is_regular_file(dir_entry)) { @@ -79,8 +79,7 @@ void RemoveAllRecoveryFiles() { } bf::path FindRecoveryFile() { - bf::path root_path = getuid() != tzplatform_getuid(TZ_SYS_GLOBALAPP_USER) - ? tzplatform_getenv(TZ_USER_APP) : tzplatform_getenv(TZ_SYS_RW_APP); + bf::path root_path = ci::GetRootAppPath(); for (auto& dir_entry : boost::make_iterator_range( bf::directory_iterator(root_path), bf::directory_iterator())) { if (bf::is_regular_file(dir_entry)) { @@ -95,8 +94,7 @@ bf::path FindRecoveryFile() { bool ValidateFileContentInPackage(const std::string& pkgid, const std::string& relative, const std::string& expected) { - bf::path root_path = getuid() != tzplatform_getuid(TZ_SYS_GLOBALAPP_USER) - ? tzplatform_getenv(TZ_USER_APP) : tzplatform_getenv(TZ_SYS_RW_APP); + bf::path root_path = ci::GetRootAppPath(); bf::path file_path = root_path / pkgid / relative; if (!bf::exists(file_path)) { LOG(ERROR) << file_path << " doesn't exist"; @@ -118,8 +116,7 @@ bool ValidateFileContentInPackage(const std::string& pkgid, void ValidatePackageFS(const std::string& pkgid, const std::string& appid, PackageType type) { - bf::path root_path = getuid() != tzplatform_getuid(TZ_SYS_GLOBALAPP_USER) - ? tzplatform_getenv(TZ_USER_APP) : tzplatform_getenv(TZ_SYS_RW_APP); + bf::path root_path = ci::GetRootAppPath(); bf::path package_path = root_path / pkgid; bf::path binary_path = package_path / "bin" / appid; bf::path data_path = package_path / "data"; @@ -153,8 +150,7 @@ void ValidatePackageFS(const std::string& pkgid, const std::string& appid, } void PackageCheckCleanup(const std::string& pkgid, const std::string& appid) { - bf::path root_path = getuid() != tzplatform_getuid(TZ_SYS_GLOBALAPP_USER) - ? tzplatform_getenv(TZ_USER_APP) : tzplatform_getenv(TZ_SYS_RW_APP); + bf::path root_path = ci::GetRootAppPath(); bf::path package_path = root_path / pkgid; ASSERT_FALSE(bf::exists(package_path)); @@ -175,13 +171,13 @@ void PackageCheckCleanup(const std::string& pkgid, const std::string& appid) { void ValidatePackage(const std::string& pkgid, const std::string& appid, PackageType type) { - ASSERT_TRUE(ci::IsPackageInstalled(pkgid, getuid())); + ASSERT_TRUE(ci::IsPackageInstalled(pkgid, ci::GetRequestMode())); ValidatePackageFS(pkgid, appid, type); } void CheckPackageNonExistance(const std::string& pkgid, const std::string& appid) { - ASSERT_FALSE(ci::IsPackageInstalled(pkgid, getuid())); + ASSERT_FALSE(ci::IsPackageInstalled(pkgid, ci::GetRequestMode())); PackageCheckCleanup(pkgid, appid); } @@ -386,8 +382,7 @@ TEST_F(SmokeTest, RDSMode) { ValidatePackage(pkgid, appid, PackageType::WGT); // Check delta modifications - bf::path root_path = getuid() != tzplatform_getuid(TZ_SYS_GLOBALAPP_USER) - ? tzplatform_getenv(TZ_USER_APP) : tzplatform_getenv(TZ_SYS_RW_APP); + bf::path root_path = ci::GetRootAppPath(); ASSERT_FALSE(bf::exists(root_path / pkgid / "res" / "wgt" / "DELETED")); ASSERT_TRUE(bf::exists(root_path / pkgid / "res" / "wgt" / "ADDED")); ValidateFileContentInPackage(pkgid, "res/wgt/MODIFIED", "2\n"); diff --git a/src/wgt/step/step_encrypt_resources.cc b/src/wgt/step/step_encrypt_resources.cc index a1ca7c5..88db671 100644 --- a/src/wgt/step/step_encrypt_resources.cc +++ b/src/wgt/step/step_encrypt_resources.cc @@ -127,7 +127,7 @@ bool StepEncryptResources::EncryptFile(const bf::path &src) { size_t enc_data_len = 0; // TODO(p.sikorski) check if it is Preloaded wae_app_type_e enc_type = - context_->uid.get() == tzplatform_getuid(TZ_SYS_GLOBALAPP_USER) ? + context_->request_mode.get() == common_installer::RequestMode::GLOBAL ? WAE_DOWNLOADED_GLOBAL_APP : WAE_DOWNLOADED_NORMAL_APP; diff --git a/src/wgt/step/step_remove_encryption_data.cc b/src/wgt/step/step_remove_encryption_data.cc index d22f0de..e74a9ff 100644 --- a/src/wgt/step/step_remove_encryption_data.cc +++ b/src/wgt/step/step_remove_encryption_data.cc @@ -14,7 +14,7 @@ namespace encrypt { common_installer::Step::Status StepRemoveEncryptionData::process() { wae_app_type_e enc_type = - context_->uid.get() == tzplatform_getuid(TZ_SYS_GLOBALAPP_USER) ? + context_->request_mode.get() == common_installer::RequestMode::GLOBAL ? WAE_DOWNLOADED_GLOBAL_APP : WAE_DOWNLOADED_NORMAL_APP; // There is no check, if application was encrypted or not diff --git a/src/wgt/wgt_app_query_interface.cc b/src/wgt/wgt_app_query_interface.cc index df188b2..4631d7c 100644 --- a/src/wgt/wgt_app_query_interface.cc +++ b/src/wgt/wgt_app_query_interface.cc @@ -21,6 +21,7 @@ #include #include "common/pkgmgr_registration.h" +#include "common/request.h" #include "common/utils/file_util.h" #include "common/utils/logging.h" @@ -93,7 +94,7 @@ bool WgtAppQueryInterface::IsAppInstalledByArgv(int argc, char** argv) { std::string pkg_id = GetPkgIdFromPath(path); if (pkg_id.empty()) return false; - return ci::IsPackageInstalled(pkg_id, getuid()); + return ci::IsPackageInstalled(pkg_id, ci::GetRequestMode()); } } // namespace wgt -- 2.7.4