diff options
Diffstat (limited to 'src/libs')
157 files changed, 4552 insertions, 1445 deletions
diff --git a/src/libs/3rdparty/libarchive/archive.h b/src/libs/3rdparty/libarchive/archive.h index 217ac1989..618b1a176 100644 --- a/src/libs/3rdparty/libarchive/archive.h +++ b/src/libs/3rdparty/libarchive/archive.h @@ -36,7 +36,7 @@ * assert that ARCHIVE_VERSION_NUMBER >= 2012108. */ /* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3006002 +#define ARCHIVE_VERSION_NUMBER 3007001 #include <sys/stat.h> #include <stddef.h> /* for wchar_t */ @@ -157,7 +157,7 @@ __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_ONLY_STRING "3.6.2" +#define ARCHIVE_VERSION_ONLY_STRING "3.7.1" #define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING __LA_DECL const char * archive_version_string(void); diff --git a/src/libs/3rdparty/libarchive/archive_digest.c b/src/libs/3rdparty/libarchive/archive_digest.c index 3361b19ad..08a9aeb02 100644 --- a/src/libs/3rdparty/libarchive/archive_digest.c +++ b/src/libs/3rdparty/libarchive/archive_digest.c @@ -36,6 +36,11 @@ #error Cannot use both OpenSSL and libmd. #endif +/* Common in other bcrypt implementations, but missing from VS2008. */ +#ifndef BCRYPT_SUCCESS +#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) +#endif + /* * Message digest functions for Windows platform. */ @@ -48,6 +53,26 @@ /* * Initialize a Message digest. */ +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +static int +win_crypto_init(Digest_CTX *ctx, const WCHAR *algo) +{ + NTSTATUS status; + ctx->valid = 0; + + status = BCryptOpenAlgorithmProvider(&ctx->hAlg, algo, NULL, 0); + if (!BCRYPT_SUCCESS(status)) + return (ARCHIVE_FAILED); + status = BCryptCreateHash(ctx->hAlg, &ctx->hHash, NULL, 0, NULL, 0, 0); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(ctx->hAlg, 0); + return (ARCHIVE_FAILED); + } + + ctx->valid = 1; + return (ARCHIVE_OK); +} +#else static int win_crypto_init(Digest_CTX *ctx, DWORD prov, ALG_ID algId) { @@ -70,6 +95,7 @@ win_crypto_init(Digest_CTX *ctx, DWORD prov, ALG_ID algId) ctx->valid = 1; return (ARCHIVE_OK); } +#endif /* * Update a Message digest. @@ -81,23 +107,37 @@ win_crypto_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len) if (!ctx->valid) return (ARCHIVE_FAILED); +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + BCryptHashData(ctx->hHash, + (PUCHAR)(uintptr_t)buf, + len, 0); +#else CryptHashData(ctx->hash, (unsigned char *)(uintptr_t)buf, (DWORD)len, 0); +#endif return (ARCHIVE_OK); } static int win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx) { +#if !(defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA) DWORD siglen = (DWORD)bufsize; +#endif if (!ctx->valid) return (ARCHIVE_FAILED); +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + BCryptFinishHash(ctx->hHash, buf, (ULONG)bufsize, 0); + BCryptDestroyHash(ctx->hHash); + BCryptCloseAlgorithmProvider(ctx->hAlg, 0); +#else CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0); CryptDestroyHash(ctx->hash); CryptReleaseContext(ctx->cryptProv, 0); +#endif ctx->valid = 0; return (ARCHIVE_OK); } @@ -276,7 +316,11 @@ __archive_md5final(archive_md5_ctx *ctx, void *md) static int __archive_md5init(archive_md5_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_MD5_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_MD5)); +#endif } static int @@ -659,7 +703,11 @@ __archive_sha1final(archive_sha1_ctx *ctx, void *md) static int __archive_sha1init(archive_sha1_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_SHA1_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_SHA1)); +#endif } static int @@ -919,7 +967,11 @@ __archive_sha256final(archive_sha256_ctx *ctx, void *md) static int __archive_sha256init(archive_sha256_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_SHA256_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_256)); +#endif } static int @@ -1155,7 +1207,11 @@ __archive_sha384final(archive_sha384_ctx *ctx, void *md) static int __archive_sha384init(archive_sha384_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_SHA384_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_384)); +#endif } static int @@ -1415,7 +1471,11 @@ __archive_sha512final(archive_sha512_ctx *ctx, void *md) static int __archive_sha512init(archive_sha512_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_SHA512_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_512)); +#endif } static int diff --git a/src/libs/3rdparty/libarchive/archive_digest_private.h b/src/libs/3rdparty/libarchive/archive_digest_private.h index 9b3bd6621..339b4edca 100644 --- a/src/libs/3rdparty/libarchive/archive_digest_private.h +++ b/src/libs/3rdparty/libarchive/archive_digest_private.h @@ -164,6 +164,15 @@ defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA512_WIN) +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +/* don't use bcrypt when XP needs to be supported */ +#include <bcrypt.h> +typedef struct { + int valid; + BCRYPT_ALG_HANDLE hAlg; + BCRYPT_HASH_HANDLE hHash; +} Digest_CTX; +#else #include <windows.h> #include <wincrypt.h> typedef struct { @@ -172,6 +181,7 @@ typedef struct { HCRYPTHASH hash; } Digest_CTX; #endif +#endif /* typedefs */ #if defined(ARCHIVE_CRYPTO_MD5_LIBC) diff --git a/src/libs/3rdparty/libarchive/archive_entry.h b/src/libs/3rdparty/libarchive/archive_entry.h index e579e9f33..c15460b96 100644 --- a/src/libs/3rdparty/libarchive/archive_entry.h +++ b/src/libs/3rdparty/libarchive/archive_entry.h @@ -30,7 +30,7 @@ #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3006002 +#define ARCHIVE_VERSION_NUMBER 3007001 /* * Note: archive_entry.h is for use outside of libarchive; the diff --git a/src/libs/3rdparty/libarchive/archive_getdate.c b/src/libs/3rdparty/libarchive/archive_getdate.c index 39e224cb9..20ab1b158 100644 --- a/src/libs/3rdparty/libarchive/archive_getdate.c +++ b/src/libs/3rdparty/libarchive/archive_getdate.c @@ -698,13 +698,9 @@ Convert(time_t Month, time_t Day, time_t Year, time_t Julian; int i; struct tm *ltime; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif if (Year < 69) Year += 2000; @@ -731,15 +727,10 @@ Convert(time_t Month, time_t Day, time_t Year, Julian *= DAY; Julian += Timezone; Julian += Hours * HOUR + Minutes * MINUTE + Seconds; -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + ltime = localtime_s(&tmbuf, &Julian) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) ltime = localtime_r(&Julian, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = Julian; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - ltime = NULL; - else - ltime = &tmbuf; #else ltime = localtime(&Julian); #endif @@ -755,36 +746,21 @@ DSTcorrect(time_t Start, time_t Future) time_t StartDay; time_t FutureDay; struct tm *ltime; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif - -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + ltime = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) ltime = localtime_r(&Start, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = Start; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - ltime = NULL; - else - ltime = &tmbuf; #else ltime = localtime(&Start); #endif StartDay = (ltime->tm_hour + 1) % 24; -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + ltime = localtime_s(&tmbuf, &Future) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) ltime = localtime_r(&Future, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = Future; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - ltime = NULL; - else - ltime = &tmbuf; #else ltime = localtime(&Future); #endif @@ -799,24 +775,15 @@ RelativeDate(time_t Start, time_t zone, int dstmode, { struct tm *tm; time_t t, now; -#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S) +#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__GMTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif t = Start - zone; -#if defined(HAVE_GMTIME_R) +#if defined(HAVE_GMTIME_S) + tm = gmtime_s(&tmbuf, &t) ? NULL : &tmbuf; +#elif defined(HAVE_GMTIME_R) tm = gmtime_r(&t, &tmbuf); -#elif defined(HAVE__GMTIME64_S) - tmptime = t; - terr = _gmtime64_s(&tmbuf, &tmptime); - if (terr) - tm = NULL; - else - tm = &tmbuf; #else tm = gmtime(&t); #endif @@ -835,25 +802,16 @@ RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth) struct tm *tm; time_t Month; time_t Year; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif if (RelMonth == 0) return 0; -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + tm = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) tm = localtime_r(&Start, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = Start; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - tm = NULL; - else - tm = &tmbuf; #else tm = localtime(&Start); #endif @@ -993,10 +951,6 @@ __archive_get_date(time_t now, const char *p) time_t Start; time_t tod; long tzone; -#if defined(HAVE__LOCALTIME64_S) || defined(HAVE__GMTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif /* Clear out the parsed token array. */ memset(tokens, 0, sizeof(tokens)); @@ -1005,36 +959,26 @@ __archive_get_date(time_t now, const char *p) gds = &_gds; /* Look up the current time. */ -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + tm = localtime_s(&local, &now) ? NULL : &local; +#elif defined(HAVE_LOCALTIME_R) tm = localtime_r(&now, &local); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = now; - terr = _localtime64_s(&local, &tmptime); - if (terr) - tm = NULL; - else - tm = &local; #else memset(&local, 0, sizeof(local)); tm = localtime(&now); #endif if (tm == NULL) return -1; -#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE__LOCALTIME64_S) +#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) local = *tm; #endif /* Look up UTC if we can and use that to determine the current * timezone offset. */ -#if defined(HAVE_GMTIME_R) +#if defined(HAVE_GMTIME_S) + gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt; +#elif defined(HAVE_GMTIME_R) gmt_ptr = gmtime_r(&now, &gmt); -#elif defined(HAVE__GMTIME64_S) - tmptime = now; - terr = _gmtime64_s(&gmt, &tmptime); - if (terr) - gmt_ptr = NULL; - else - gmt_ptr = &gmt; #else memset(&gmt, 0, sizeof(gmt)); gmt_ptr = gmtime(&now); @@ -1076,15 +1020,10 @@ __archive_get_date(time_t now, const char *p) * time components instead of the local timezone. */ if (gds->HaveZone && gmt_ptr != NULL) { now -= gds->Timezone; -#if defined(HAVE_GMTIME_R) +#if defined(HAVE_GMTIME_S) + gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt; +#elif defined(HAVE_GMTIME_R) gmt_ptr = gmtime_r(&now, &gmt); -#elif defined(HAVE__GMTIME64_S) - tmptime = now; - terr = _gmtime64_s(&gmt, &tmptime); - if (terr) - gmt_ptr = NULL; - else - gmt_ptr = &gmt; #else gmt_ptr = gmtime(&now); #endif diff --git a/src/libs/3rdparty/libarchive/archive_hmac.c b/src/libs/3rdparty/libarchive/archive_hmac.c index 012fe1596..edb3bf5ab 100644 --- a/src/libs/3rdparty/libarchive/archive_hmac.c +++ b/src/libs/3rdparty/libarchive/archive_hmac.c @@ -231,15 +231,20 @@ static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { #if OPENSSL_VERSION_NUMBER >= 0x30000000L - OSSL_PARAM params[2]; + EVP_MAC *mac; - EVP_MAC *mac = EVP_MAC_fetch(NULL, "HMAC", NULL); + char sha1[] = "SHA1"; + OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string("digest", sha1, sizeof(sha1) - 1), + OSSL_PARAM_END + }; + + mac = EVP_MAC_fetch(NULL, "HMAC", NULL); *ctx = EVP_MAC_CTX_new(mac); + EVP_MAC_free(mac); if (*ctx == NULL) return -1; - EVP_MAC_free(mac); - params[0] = OSSL_PARAM_construct_utf8_string("digest", "SHA1", 0); - params[1] = OSSL_PARAM_construct_end(); + EVP_MAC_init(*ctx, key, key_len, params); #else *ctx = HMAC_CTX_new(); diff --git a/src/libs/3rdparty/libarchive/archive_hmac_private.h b/src/libs/3rdparty/libarchive/archive_hmac_private.h index 50044a045..d0fda7f96 100644 --- a/src/libs/3rdparty/libarchive/archive_hmac_private.h +++ b/src/libs/3rdparty/libarchive/archive_hmac_private.h @@ -77,6 +77,8 @@ typedef struct hmac_sha1_ctx archive_hmac_sha1_ctx; #include <openssl/opensslv.h> #include <openssl/hmac.h> #if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include <openssl/params.h> + typedef EVP_MAC_CTX *archive_hmac_sha1_ctx; #else diff --git a/src/libs/3rdparty/libarchive/archive_openssl_evp_private.h b/src/libs/3rdparty/libarchive/archive_openssl_evp_private.h index ebb06702d..8ac477280 100644 --- a/src/libs/3rdparty/libarchive/archive_openssl_evp_private.h +++ b/src/libs/3rdparty/libarchive/archive_openssl_evp_private.h @@ -33,7 +33,8 @@ #include <openssl/evp.h> #include <openssl/opensslv.h> -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) #include <stdlib.h> /* malloc, free */ #include <string.h> /* memset */ static inline EVP_MD_CTX *EVP_MD_CTX_new(void) diff --git a/src/libs/3rdparty/libarchive/archive_random.c b/src/libs/3rdparty/libarchive/archive_random.c index 9d1aa493f..301765acd 100644 --- a/src/libs/3rdparty/libarchive/archive_random.c +++ b/src/libs/3rdparty/libarchive/archive_random.c @@ -51,16 +51,27 @@ __FBSDID("$FreeBSD$"); #include <pthread.h> #endif -static void arc4random_buf(void *, size_t); +static void la_arc4random_buf(void *, size_t); #endif /* HAVE_ARC4RANDOM_BUF */ #include "archive.h" #include "archive_random_private.h" -#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +/* don't use bcrypt when XP needs to be supported */ +#include <bcrypt.h> + +/* Common in other bcrypt implementations, but missing from VS2008. */ +#ifndef BCRYPT_SUCCESS +#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) +#endif + +#elif defined(HAVE_WINCRYPT_H) #include <wincrypt.h> #endif +#endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 @@ -75,6 +86,20 @@ int archive_random(void *buf, size_t nbytes) { #if defined(_WIN32) && !defined(__CYGWIN__) +# if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + NTSTATUS status; + BCRYPT_ALG_HANDLE hAlg; + + status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0); + if (!BCRYPT_SUCCESS(status)) + return ARCHIVE_FAILED; + status = BCryptGenRandom(hAlg, buf, nbytes, 0); + BCryptCloseAlgorithmProvider(hAlg, 0); + if (!BCRYPT_SUCCESS(status)) + return ARCHIVE_FAILED; + + return ARCHIVE_OK; +# else HCRYPTPROV hProv; BOOL success; @@ -92,6 +117,10 @@ archive_random(void *buf, size_t nbytes) } /* TODO: Does this case really happen? */ return ARCHIVE_FAILED; +# endif +#elif !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__)) + la_arc4random_buf(buf, nbytes); + return ARCHIVE_OK; #else arc4random_buf(buf, nbytes); return ARCHIVE_OK; @@ -256,7 +285,7 @@ arc4_getbyte(void) } static void -arc4random_buf(void *_buf, size_t n) +la_arc4random_buf(void *_buf, size_t n) { uint8_t *buf = (uint8_t *)_buf; _ARC4_LOCK(); diff --git a/src/libs/3rdparty/libarchive/archive_read_data_into_fd.c b/src/libs/3rdparty/libarchive/archive_read_data_into_fd.c index b4398f1ec..f16ca5c82 100644 --- a/src/libs/3rdparty/libarchive/archive_read_data_into_fd.c +++ b/src/libs/3rdparty/libarchive/archive_read_data_into_fd.c @@ -95,8 +95,13 @@ archive_read_data_into_fd(struct archive *a, int fd) "archive_read_data_into_fd"); can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode); - if (!can_lseek) + if (!can_lseek) { nulls = calloc(1, nulls_size); + if (!nulls) { + r = ARCHIVE_FATAL; + goto cleanup; + } + } while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) == ARCHIVE_OK) { diff --git a/src/libs/3rdparty/libarchive/archive_read_disk_posix.c b/src/libs/3rdparty/libarchive/archive_read_disk_posix.c index 5a94ec5d4..8d5c32f03 100644 --- a/src/libs/3rdparty/libarchive/archive_read_disk_posix.c +++ b/src/libs/3rdparty/libarchive/archive_read_disk_posix.c @@ -1670,6 +1670,11 @@ setup_current_filesystem(struct archive_read_disk *a) else t->current_filesystem->name_max = nm; #endif + if (t->current_filesystem->name_max == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Cannot determine name_max"); + return (ARCHIVE_FAILED); + } #endif /* USE_READDIR_R */ return (ARCHIVE_OK); } @@ -1860,8 +1865,17 @@ setup_current_filesystem(struct archive_read_disk *a) #if defined(USE_READDIR_R) /* Set maximum filename length. */ +#if defined(HAVE_STATVFS) + t->current_filesystem->name_max = svfs.f_namemax; +#else t->current_filesystem->name_max = sfs.f_namelen; #endif + if (t->current_filesystem->name_max == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Cannot determine name_max"); + return (ARCHIVE_FAILED); + } +#endif return (ARCHIVE_OK); } @@ -1942,6 +1956,11 @@ setup_current_filesystem(struct archive_read_disk *a) #if defined(USE_READDIR_R) /* Set maximum filename length. */ t->current_filesystem->name_max = svfs.f_namemax; + if (t->current_filesystem->name_max == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Cannot determine name_max"); + return (ARCHIVE_FAILED); + } #endif return (ARCHIVE_OK); } @@ -1996,6 +2015,11 @@ setup_current_filesystem(struct archive_read_disk *a) else t->current_filesystem->name_max = nm; # endif /* _PC_NAME_MAX */ + if (t->current_filesystem->name_max == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Cannot determine name_max"); + return (ARCHIVE_FAILED); + } #endif /* USE_READDIR_R */ return (ARCHIVE_OK); } @@ -2543,7 +2567,11 @@ tree_current_lstat(struct tree *t) #else if (tree_enter_working_dir(t) != 0) return NULL; +#ifdef HAVE_LSTAT if (lstat(tree_current_access_path(t), &t->lst) != 0) +#else + if (la_stat(tree_current_access_path(t), &t->lst) != 0) +#endif #endif return NULL; t->flags |= hasLstat; diff --git a/src/libs/3rdparty/libarchive/archive_read_disk_windows.c b/src/libs/3rdparty/libarchive/archive_read_disk_windows.c index f9d139557..f92a78a21 100644 --- a/src/libs/3rdparty/libarchive/archive_read_disk_windows.c +++ b/src/libs/3rdparty/libarchive/archive_read_disk_windows.c @@ -418,9 +418,19 @@ la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf, int *linktype) FILE_FLAG_OPEN_REPARSE_POINT; int ret; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = flag; + h = CreateFile2(path, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + OPEN_EXISTING, &createExParams); +#else h = CreateFileW(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flag, NULL); +#endif if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); return (-1); @@ -1067,16 +1077,29 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (archive_entry_filetype(entry) == AE_IFREG && archive_entry_size(entry) > 0) { DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; +#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif if (t->async_io) flags |= FILE_FLAG_OVERLAPPED; if (t->direct_io) flags |= FILE_FLAG_NO_BUFFERING; else flags |= FILE_FLAG_SEQUENTIAL_SCAN; +#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = flags; + t->entry_fh = CreateFile2(tree_current_access_path(t), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + OPEN_EXISTING, &createExParams); +#else t->entry_fh = CreateFileW(tree_current_access_path(t), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flags, NULL); +#endif if (t->entry_fh == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, @@ -1547,6 +1570,9 @@ close_and_restore_time(HANDLE h, struct tree *t, struct restore_time *rt) { HANDLE handle; int r = 0; +#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif if (h == INVALID_HANDLE_VALUE && AE_IFLNK == rt->filetype) return (0); @@ -1560,8 +1586,16 @@ close_and_restore_time(HANDLE h, struct tree *t, struct restore_time *rt) if ((t->flags & needsRestoreTimes) == 0) return (r); +#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS; + handle = CreateFile2(rt->full_path, FILE_WRITE_ATTRIBUTES, + 0, OPEN_EXISTING, &createExParams); +#else handle = CreateFileW(rt->full_path, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); +#endif if (handle == INVALID_HANDLE_VALUE) { errno = EINVAL; return (-1); @@ -2046,12 +2080,24 @@ tree_current_file_information(struct tree *t, BY_HANDLE_FILE_INFORMATION *st, HANDLE h; int r; DWORD flag = FILE_FLAG_BACKUP_SEMANTICS; - +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif + if (sim_lstat && tree_current_is_physical_link(t)) flag |= FILE_FLAG_OPEN_REPARSE_POINT; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = flag; + h = CreateFile2(tree_current_access_path(t), 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + OPEN_EXISTING, &createExParams); +#else h = CreateFileW(tree_current_access_path(t), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flag, NULL); +#endif if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); t->tree_errno = errno; @@ -2257,7 +2303,10 @@ archive_read_disk_entry_from_file(struct archive *_a, } else { WIN32_FIND_DATAW findData; DWORD flag, desiredAccess; - +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif + h = FindFirstFileW(path, &findData); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); @@ -2279,9 +2328,18 @@ archive_read_disk_entry_from_file(struct archive *_a, } else desiredAccess = GENERIC_READ; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = flag; + h = CreateFile2(path, desiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + OPEN_EXISTING, &createExParams); +#else h = CreateFileW(path, desiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flag, NULL); +#endif if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, @@ -2342,9 +2400,19 @@ archive_read_disk_entry_from_file(struct archive *_a, if (fd >= 0) { h = (HANDLE)_get_osfhandle(fd); } else { +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS; + h = CreateFile2(path, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + OPEN_EXISTING, &createExParams); +#else h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); +#endif if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, diff --git a/src/libs/3rdparty/libarchive/archive_read_open_file.c b/src/libs/3rdparty/libarchive/archive_read_open_file.c index 101dae6cd..03719e8bf 100644 --- a/src/libs/3rdparty/libarchive/archive_read_open_file.c +++ b/src/libs/3rdparty/libarchive/archive_read_open_file.c @@ -154,10 +154,10 @@ file_skip(struct archive *a, void *client_data, int64_t request) #ifdef __ANDROID__ /* fileno() isn't safe on all platforms ... see above. */ if (lseek(fileno(mine->f), skip, SEEK_CUR) < 0) -#elif HAVE_FSEEKO - if (fseeko(mine->f, skip, SEEK_CUR) != 0) #elif HAVE__FSEEKI64 if (_fseeki64(mine->f, skip, SEEK_CUR) != 0) +#elif HAVE_FSEEKO + if (fseeko(mine->f, skip, SEEK_CUR) != 0) #else if (fseek(mine->f, skip, SEEK_CUR) != 0) #endif diff --git a/src/libs/3rdparty/libarchive/archive_read_support_filter_bzip2.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_bzip2.c index 793d605c8..9158e668e 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_filter_bzip2.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_filter_bzip2.c @@ -230,7 +230,7 @@ bzip2_filter_read(struct archive_read_filter *self, const void **p) /* Empty our output buffer. */ state->stream.next_out = state->out_block; - state->stream.avail_out = state->out_block_size; + state->stream.avail_out = (uint32_t)state->out_block_size; /* Try to fill the output buffer. */ for (;;) { @@ -288,7 +288,7 @@ bzip2_filter_read(struct archive_read_filter *self, const void **p) return (ARCHIVE_FATAL); } state->stream.next_in = (char *)(uintptr_t)read_buf; - state->stream.avail_in = ret; + state->stream.avail_in = (uint32_t)ret; /* There is no more data, return whatever we have. */ if (ret == 0) { state->eof = 1; diff --git a/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c index 1e99542d7..d0fc1a83e 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c @@ -584,7 +584,7 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p) state->out_block + prefix64k, (int)compressed_size, state->flags.block_maximum_size, state->out_block, - prefix64k); + (int)prefix64k); #else uncompressed_size = LZ4_decompress_safe_withPrefix64k( read_buf + 4, diff --git a/src/libs/3rdparty/libarchive/archive_read_support_filter_zstd.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_zstd.c index 39f25f1bf..1959b5ac3 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_filter_zstd.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_filter_zstd.c @@ -115,9 +115,9 @@ zstd_bidder_bid(struct archive_read_filter_bidder *self, unsigned prefix; /* Zstd frame magic values */ - const unsigned zstd_magic = 0xFD2FB528U; - const unsigned zstd_magic_skippable_start = 0x184D2A50U; - const unsigned zstd_magic_skippable_mask = 0xFFFFFFF0; + unsigned zstd_magic = 0xFD2FB528U; + unsigned zstd_magic_skippable_start = 0x184D2A50U; + unsigned zstd_magic_skippable_mask = 0xFFFFFFF0; (void) self; /* UNUSED */ @@ -170,7 +170,7 @@ static int zstd_bidder_init(struct archive_read_filter *self) { struct private_data *state; - const size_t out_block_size = ZSTD_DStreamOutSize(); + size_t out_block_size = ZSTD_DStreamOutSize(); void *out_block; ZSTD_DStream *dstream; @@ -211,6 +211,7 @@ zstd_filter_read(struct archive_read_filter *self, const void **p) ssize_t avail_in; ZSTD_outBuffer out; ZSTD_inBuffer in; + size_t ret; state = (struct private_data *)self->data; @@ -219,7 +220,7 @@ zstd_filter_read(struct archive_read_filter *self, const void **p) /* Try to fill the output buffer. */ while (out.pos < out.size && !state->eof) { if (!state->in_frame) { - const size_t ret = ZSTD_initDStream(state->dstream); + ret = ZSTD_initDStream(state->dstream); if (ZSTD_isError(ret)) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, @@ -249,8 +250,7 @@ zstd_filter_read(struct archive_read_filter *self, const void **p) in.pos = 0; { - const size_t ret = - ZSTD_decompressStream(state->dstream, &out, &in); + ret = ZSTD_decompressStream(state->dstream, &out, &in); if (ZSTD_isError(ret)) { archive_set_error(&self->archive->archive, diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c b/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c index 0ba4bee35..b171bea01 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c @@ -41,6 +41,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_ZLIB_H #include <zlib.h> #endif +#ifdef HAVE_ZSTD_H +#include <zstd.h> +#endif #include "archive.h" #include "archive_entry.h" @@ -80,8 +83,11 @@ __FBSDID("$FreeBSD$"); #define _7Z_IA64 0x03030401 #define _7Z_ARM 0x03030501 #define _7Z_ARMTHUMB 0x03030701 +#define _7Z_ARM64 0xa #define _7Z_SPARC 0x03030805 +#define _7Z_ZSTD 0x4F71101 /* Copied from https://github.com/mcmilk/7-Zip-zstd.git */ + /* * 7-Zip header property IDs. */ @@ -278,6 +284,11 @@ struct _7zip { z_stream stream; int stream_valid; #endif + /* Decoding Zstandard data. */ +#if HAVE_ZSTD_H + ZSTD_DStream *zstd_dstream; + int zstdstream_valid; +#endif /* Decoding PPMd data. */ int ppmd7_stat; CPpmd7 ppmd7_context; @@ -397,6 +408,9 @@ static int setup_decode_folder(struct archive_read *, struct _7z_folder *, int); static void x86_Init(struct _7zip *); static size_t x86_Convert(struct _7zip *, uint8_t *, size_t); +static void arm_Init(struct _7zip *); +static size_t arm_Convert(struct _7zip *, uint8_t *, size_t); +static size_t arm64_Convert(struct _7zip *, uint8_t *, size_t); static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t); @@ -1027,10 +1041,13 @@ init_decompression(struct archive_read *a, struct _7zip *zip, case _7Z_COPY: case _7Z_BZ2: case _7Z_DEFLATE: + case _7Z_ZSTD: case _7Z_PPMD: if (coder2 != NULL) { if (coder2->codec != _7Z_X86 && - coder2->codec != _7Z_X86_BCJ2) { + coder2->codec != _7Z_X86_BCJ2 && + coder2->codec != _7Z_ARM && + coder2->codec != _7Z_ARM64) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unsupported filter %lx for %lx", @@ -1041,6 +1058,8 @@ init_decompression(struct archive_read *a, struct _7zip *zip, zip->bcj_state = 0; if (coder2->codec == _7Z_X86) x86_Init(zip); + else if (coder2->codec == _7Z_ARM) + arm_Init(zip); } break; default: @@ -1137,6 +1156,12 @@ init_decompression(struct archive_read *a, struct _7zip *zip, filters[fi].id = LZMA_FILTER_ARMTHUMB; fi++; break; +#ifdef LZMA_FILTER_ARM64 + case _7Z_ARM64: + filters[fi].id = LZMA_FILTER_ARM64; + fi++; + break; +#endif case _7Z_SPARC: filters[fi].id = LZMA_FILTER_SPARC; fi++; @@ -1222,6 +1247,22 @@ init_decompression(struct archive_read *a, struct _7zip *zip, "BZ2 codec is unsupported"); return (ARCHIVE_FAILED); #endif + case _7Z_ZSTD: + { +#if defined(HAVE_ZSTD_H) + if (zip->zstdstream_valid) { + ZSTD_freeDStream(zip->zstd_dstream); + zip->zstdstream_valid = 0; + } + zip->zstd_dstream = ZSTD_createDStream(); + zip->zstdstream_valid = 1; + break; +#else + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZSTD codec is unsupported"); + return (ARCHIVE_FAILED); +#endif + } case _7Z_DEFLATE: #ifdef HAVE_ZLIB_H if (zip->stream_valid) @@ -1436,9 +1477,9 @@ decompress(struct archive_read *a, struct _7zip *zip, #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) case _7Z_BZ2: zip->bzstream.next_in = (char *)(uintptr_t)t_next_in; - zip->bzstream.avail_in = t_avail_in; + zip->bzstream.avail_in = (uint32_t)t_avail_in; zip->bzstream.next_out = (char *)(uintptr_t)t_next_out; - zip->bzstream.avail_out = t_avail_out; + zip->bzstream.avail_out = (uint32_t)t_avail_out; r = BZ2_bzDecompress(&(zip->bzstream)); switch (r) { case BZ_STREAM_END: /* Found end of stream. */ @@ -1488,6 +1529,22 @@ decompress(struct archive_read *a, struct _7zip *zip, t_avail_out = zip->stream.avail_out; break; #endif +#ifdef HAVE_ZSTD_H + case _7Z_ZSTD: + { + ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; // src, size, pos + ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 }; // dst, size, pos + + size_t const zret = ZSTD_decompressStream(zip->zstd_dstream, &output, &input); + if (ZSTD_isError(zret)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(zret)); + return ARCHIVE_FAILED; + } + t_avail_in -= input.pos; + t_avail_out -= output.pos; + break; + } +#endif case _7Z_PPMD: { uint64_t flush_bytes; @@ -1572,16 +1629,23 @@ decompress(struct archive_read *a, struct _7zip *zip, /* * Decord BCJ. */ - if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) { - size_t l = x86_Convert(zip, buff, *outbytes); - zip->odd_bcj_size = *outbytes - l; - if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 && - o_avail_in && ret != ARCHIVE_EOF) { - memcpy(zip->odd_bcj, ((unsigned char *)buff) + l, - zip->odd_bcj_size); - *outbytes = l; - } else - zip->odd_bcj_size = 0; + if (zip->codec != _7Z_LZMA2) { + if (zip->codec2 == _7Z_X86) { + size_t l = x86_Convert(zip, buff, *outbytes); + + zip->odd_bcj_size = *outbytes - l; + if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 && + o_avail_in && ret != ARCHIVE_EOF) { + memcpy(zip->odd_bcj, ((unsigned char *)buff) + l, + zip->odd_bcj_size); + *outbytes = l; + } else + zip->odd_bcj_size = 0; + } else if (zip->codec2 == _7Z_ARM) { + *outbytes = arm_Convert(zip, buff, *outbytes); + } else if (zip->codec2 == _7Z_ARM64) { + *outbytes = arm64_Convert(zip, buff, *outbytes); + } } /* @@ -3727,6 +3791,116 @@ x86_Convert(struct _7zip *zip, uint8_t *data, size_t size) return (bufferPos); } +static void +arm_Init(struct _7zip *zip) +{ + zip->bcj_ip = 8; +} + +static size_t +arm_Convert(struct _7zip *zip, uint8_t *buf, size_t size) +{ + // This function was adapted from + // static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size) + // in https://git.tukaani.org/xz-embedded.git + + /* + * Branch/Call/Jump (BCJ) filter decoders + * + * Authors: Lasse Collin <lasse.collin@tukaani.org> + * Igor Pavlov <https://7-zip.org/> + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + + size_t i; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 4) { + if (buf[i + 3] == 0xEB) { + // Calculate the transformed addr. + addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8) + | ((uint32_t)buf[i + 2] << 16); + addr <<= 2; + addr -= zip->bcj_ip + (uint32_t)i; + addr >>= 2; + + // Store the transformed addr in buf. + buf[i] = (uint8_t)addr; + buf[i + 1] = (uint8_t)(addr >> 8); + buf[i + 2] = (uint8_t)(addr >> 16); + } + } + + zip->bcj_ip += (uint32_t)i; + + return i; +} + +static size_t +arm64_Convert(struct _7zip *zip, uint8_t *buf, size_t size) +{ + // This function was adapted from + // static size_t bcj_arm64(struct xz_dec_bcj *s, uint8_t *buf, size_t size) + // in https://git.tukaani.org/xz-embedded.git + + /* + * Branch/Call/Jump (BCJ) filter decoders + * + * Authors: Lasse Collin <lasse.collin@tukaani.org> + * Igor Pavlov <https://7-zip.org/> + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + + size_t i; + uint32_t instr; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 4) { + instr = (uint32_t)buf[i] + | ((uint32_t)buf[i+1] << 8) + | ((uint32_t)buf[i+2] << 16) + | ((uint32_t)buf[i+3] << 24); + + if ((instr >> 26) == 0x25) { + /* BL instruction */ + addr = instr - ((zip->bcj_ip + (uint32_t)i) >> 2); + instr = 0x94000000 | (addr & 0x03FFFFFF); + + buf[i] = (uint8_t)instr; + buf[i+1] = (uint8_t)(instr >> 8); + buf[i+2] = (uint8_t)(instr >> 16); + buf[i+3] = (uint8_t)(instr >> 24); + } else if ((instr & 0x9F000000) == 0x90000000) { + /* ADRP instruction */ + addr = ((instr >> 29) & 3) | ((instr >> 3) & 0x1FFFFC); + + /* Only convert values in the range +/-512 MiB. */ + if ((addr + 0x020000) & 0x1C0000) + continue; + + addr -= (zip->bcj_ip + (uint32_t)i) >> 12; + + instr &= 0x9000001F; + instr |= (addr & 3) << 29; + instr |= (addr & 0x03FFFC) << 3; + instr |= (0U - (addr & 0x020000)) & 0xE00000; + + buf[i] = (uint8_t)instr; + buf[i+1] = (uint8_t)(instr >> 8); + buf[i+2] = (uint8_t)(instr >> 16); + buf[i+3] = (uint8_t)(instr >> 24); + } + } + + zip->bcj_ip += (uint32_t)i; + + return i; +} + /* * Brought from LZMA SDK. * diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c b/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c index 4d5029b1b..3b552a84d 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c @@ -2294,10 +2294,10 @@ lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br) (br->cache_buffer << 48) | ((uint64_t)strm->next_in[1]) << 40 | ((uint64_t)strm->next_in[0]) << 32 | - ((uint32_t)strm->next_in[3]) << 24 | - ((uint32_t)strm->next_in[2]) << 16 | - ((uint32_t)strm->next_in[5]) << 8 | - (uint32_t)strm->next_in[4]; + ((uint64_t)strm->next_in[3]) << 24 | + ((uint64_t)strm->next_in[2]) << 16 | + ((uint64_t)strm->next_in[5]) << 8 | + (uint64_t)strm->next_in[4]; strm->next_in += 6; strm->avail_in -= 6; br->cache_avail += 6 * 8; diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_cpio.c b/src/libs/3rdparty/libarchive/archive_read_support_format_cpio.c index 6b8ae33a4..9adcfd335 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_format_cpio.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_format_cpio.c @@ -441,7 +441,7 @@ archive_read_format_cpio_read_header(struct archive_read *a, /* Compare name to "TRAILER!!!" to test for end-of-archive. */ if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!", - 11) == 0) { + 10) == 0) { /* TODO: Store file location of start of block. */ archive_clear_error(&a->archive); return (ARCHIVE_EOF); @@ -985,14 +985,14 @@ archive_read_format_cpio_cleanup(struct archive_read *a) static int64_t le4(const unsigned char *p) { - return ((p[0] << 16) + (((int64_t)p[1]) << 24) + (p[2] << 0) + (p[3] << 8)); + return ((p[0] << 16) | (((int64_t)p[1]) << 24) | (p[2] << 0) | (p[3] << 8)); } static int64_t be4(const unsigned char *p) { - return ((((int64_t)p[0]) << 24) + (p[1] << 16) + (p[2] << 8) + (p[3])); + return ((((int64_t)p[0]) << 24) | (p[1] << 16) | (p[2] << 8) | (p[3])); } /* diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c b/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c index 33bf330cb..f5414be2a 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c @@ -1901,7 +1901,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, * NUMBER of RRIP "PX" extension. * Note: Old mkisofs did not record that FILE SERIAL NUMBER * in ISO images. - * Note2: xorriso set 0 to the location of a symlink file. + * Note2: xorriso set 0 to the location of a symlink file. */ if (file->size == 0 && location >= 0) { /* If file->size is zero, its location points wrong place, @@ -1955,7 +1955,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, * made by makefs is not zero and its location is * the same as those of next regular file. That is * the same as hard like file and it causes unexpected - * error. + * error. */ if (file->size > 0 && (file->mode & AE_IFMT) == AE_IFLNK) { @@ -2747,7 +2747,7 @@ next_cache_entry(struct archive_read *a, struct iso9660 *iso9660, * If directory entries all which are descendant of * rr_moved are still remaining, expose their. */ - if (iso9660->re_files.first != NULL && + if (iso9660->re_files.first != NULL && iso9660->rr_moved != NULL && iso9660->rr_moved->rr_moved_has_re_only) /* Expose "rr_moved" entry. */ @@ -3180,11 +3180,11 @@ isodate17(const unsigned char *v) static time_t time_from_tm(struct tm *t) { -#if HAVE_TIMEGM +#if HAVE__MKGMTIME + return _mkgmtime(t); +#elif HAVE_TIMEGM /* Use platform timegm() if available. */ return (timegm(t)); -#elif HAVE__MKGMTIME64 - return (_mkgmtime64(t)); #else /* Else use direct calculation using POSIX assumptions. */ /* First, fix up tm_yday based on the year/month/day. */ diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c b/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c index bcfd42e1d..1c64b2900 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c @@ -1818,13 +1818,16 @@ lha_crc16(uint16_t crc, const void *pp, size_t len) /* This if statement expects compiler optimization will * remove the statement which will not be executed. */ #undef bswap16 +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Visual Studio */ # define bswap16(x) _byteswap_ushort(x) #elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4) /* GCC 4.8 and later has __builtin_bswap16() */ # define bswap16(x) __builtin_bswap16(x) -#elif defined(__clang__) -/* All clang versions have __builtin_bswap16() */ +#elif defined(__clang__) && __has_builtin(__builtin_bswap16) +/* Newer clang versions have __builtin_bswap16() */ # define bswap16(x) __builtin_bswap16(x) #else # define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8)) @@ -2009,10 +2012,10 @@ lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br) ((uint64_t)strm->next_in[0]) << 48 | ((uint64_t)strm->next_in[1]) << 40 | ((uint64_t)strm->next_in[2]) << 32 | - ((uint32_t)strm->next_in[3]) << 24 | - ((uint32_t)strm->next_in[4]) << 16 | - ((uint32_t)strm->next_in[5]) << 8 | - (uint32_t)strm->next_in[6]; + ((uint64_t)strm->next_in[3]) << 24 | + ((uint64_t)strm->next_in[4]) << 16 | + ((uint64_t)strm->next_in[5]) << 8 | + (uint64_t)strm->next_in[6]; strm->next_in += 7; strm->avail_in -= 7; br->cache_avail += 7 * 8; @@ -2022,10 +2025,10 @@ lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br) (br->cache_buffer << 48) | ((uint64_t)strm->next_in[0]) << 40 | ((uint64_t)strm->next_in[1]) << 32 | - ((uint32_t)strm->next_in[2]) << 24 | - ((uint32_t)strm->next_in[3]) << 16 | - ((uint32_t)strm->next_in[4]) << 8 | - (uint32_t)strm->next_in[5]; + ((uint64_t)strm->next_in[2]) << 24 | + ((uint64_t)strm->next_in[3]) << 16 | + ((uint64_t)strm->next_in[4]) << 8 | + (uint64_t)strm->next_in[5]; strm->next_in += 6; strm->avail_in -= 6; br->cache_avail += 6 * 8; diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c b/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c index 2bc3ba066..a5fa30e3c 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c @@ -1280,7 +1280,13 @@ parse_file(struct archive_read *a, struct archive_entry *entry, mtree->fd = -1; st = NULL; } - } else if (lstat(path, st) == -1) { + } +#ifdef HAVE_LSTAT + else if (lstat(path, st) == -1) +#else + else if (la_stat(path, st) == -1) +#endif + { st = NULL; } diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c b/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c index 793e8e985..16b6e6eed 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c @@ -1062,7 +1062,7 @@ archive_read_format_rar_read_header(struct archive_read *a, return (ARCHIVE_FATAL); } p = h; - crc32_val = crc32(crc32_val, (const unsigned char *)p, to_read); + crc32_val = crc32(crc32_val, (const unsigned char *)p, (unsigned int)to_read); __archive_read_consume(a, to_read); skip -= to_read; } @@ -1830,13 +1830,9 @@ read_exttime(const char *p, struct rar *rar, const char *endp) struct tm *tm; time_t t; long nsec; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif if (p + 2 > endp) return (-1); @@ -1868,15 +1864,10 @@ read_exttime(const char *p, struct rar *rar, const char *endp) rem = (((unsigned)(unsigned char)*p) << 16) | (rem >> 8); p++; } -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + tm = localtime_s(&tmbuf, &t) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) tm = localtime_r(&t, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = t; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - tm = NULL; - else - tm = &tmbuf; #else tm = localtime(&t); #endif @@ -3446,7 +3437,7 @@ compile_program(const uint8_t *bytes, size_t length) prog = calloc(1, sizeof(*prog)); if (!prog) return NULL; - prog->fingerprint = crc32(0, bytes, length) | ((uint64_t)length << 32); + prog->fingerprint = crc32(0, bytes, (unsigned int)length) | ((uint64_t)length << 32); if (membr_bits(&br, 1)) { diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c b/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c index 38979cbe9..1f9099439 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c @@ -2475,7 +2475,7 @@ static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) { * `stored_crc32` info filled in. */ if(rar->file.stored_crc32 > 0) { rar->file.calculated_crc32 = - crc32(rar->file.calculated_crc32, p, to_read); + crc32(rar->file.calculated_crc32, p, (unsigned int)to_read); } /* Check if the file uses an optional BLAKE2sp checksum diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_warc.c b/src/libs/3rdparty/libarchive/archive_read_support_format_warc.c index 27329962d..61ab29ea1 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_format_warc.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_format_warc.c @@ -530,11 +530,11 @@ strtoi_lim(const char *str, const char **ep, int llim, int ulim) static time_t time_from_tm(struct tm *t) { -#if HAVE_TIMEGM +#if HAVE__MKGMTIME + return _mkgmtime(t); +#elif HAVE_TIMEGM /* Use platform timegm() if available. */ return (timegm(t)); -#elif HAVE__MKGMTIME64 - return (_mkgmtime64(t)); #else /* Else use direct calculation using POSIX assumptions. */ /* First, fix up tm_yday based on the year/month/day. */ diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c b/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c index ec5b06eda..ec9cb1981 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c @@ -1127,7 +1127,7 @@ atohex(unsigned char *b, size_t bsize, const char *p, size_t psize) x |= p[1] - '0'; else return (-1); - + *b++ = x; bsize--; p += 2; @@ -1139,11 +1139,11 @@ atohex(unsigned char *b, size_t bsize, const char *p, size_t psize) static time_t time_from_tm(struct tm *t) { -#if HAVE_TIMEGM +#if HAVE__MKGMTIME + return _mkgmtime(t); +#elif HAVE_TIMEGM /* Use platform timegm() if available. */ return (timegm(t)); -#elif HAVE__MKGMTIME64 - return (_mkgmtime64(t)); #else /* Else use direct calculation using POSIX assumptions. */ /* First, fix up tm_yday based on the year/month/day. */ diff --git a/src/libs/3rdparty/libarchive/archive_read_support_format_zip.c b/src/libs/3rdparty/libarchive/archive_read_support_format_zip.c index 9d6c900b2..c3b9b5755 100644 --- a/src/libs/3rdparty/libarchive/archive_read_support_format_zip.c +++ b/src/libs/3rdparty/libarchive/archive_read_support_format_zip.c @@ -2186,11 +2186,11 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, /* Setup buffer boundaries. */ zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff; - zip->bzstream.avail_in = in_bytes; + zip->bzstream.avail_in = (uint32_t)in_bytes; zip->bzstream.total_in_hi32 = 0; zip->bzstream.total_in_lo32 = 0; zip->bzstream.next_out = (char*) zip->uncompressed_buffer; - zip->bzstream.avail_out = zip->uncompressed_buffer_size; + zip->bzstream.avail_out = (uint32_t)zip->uncompressed_buffer_size; zip->bzstream.total_out_hi32 = 0; zip->bzstream.total_out_lo32 = 0; @@ -2227,7 +2227,7 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, to_consume = zip->bzstream.total_in_lo32; __archive_read_consume(a, to_consume); - total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) + + total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) | zip->bzstream.total_out_lo32; zip->entry_bytes_remaining -= to_consume; diff --git a/src/libs/3rdparty/libarchive/archive_string.c b/src/libs/3rdparty/libarchive/archive_string.c index 69458e1a1..accf52631 100644 --- a/src/libs/3rdparty/libarchive/archive_string.c +++ b/src/libs/3rdparty/libarchive/archive_string.c @@ -1324,6 +1324,10 @@ free_sconv_object(struct archive_string_conv *sc) } #if defined(_WIN32) && !defined(__CYGWIN__) +# if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define GetOEMCP() CP_OEMCP +# endif + static unsigned my_atoi(const char *p) { diff --git a/src/libs/3rdparty/libarchive/archive_util.c b/src/libs/3rdparty/libarchive/archive_util.c index b1582edbe..40603c483 100644 --- a/src/libs/3rdparty/libarchive/archive_util.c +++ b/src/libs/3rdparty/libarchive/archive_util.c @@ -42,9 +42,20 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:1 #ifdef HAVE_STRING_H #include <string.h> #endif -#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +/* don't use bcrypt when XP needs to be supported */ +#include <bcrypt.h> + +/* Common in other bcrypt implementations, but missing from VS2008. */ +#ifndef BCRYPT_SUCCESS +#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) +#endif + +#elif defined(HAVE_WINCRYPT_H) #include <wincrypt.h> #endif +#endif #ifdef HAVE_ZLIB_H #include <zlib.h> #endif @@ -233,14 +244,16 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) L'm', L'n', L'o', L'p', L'q', L'r', L's', L't', L'u', L'v', L'w', L'x', L'y', L'z' }; - HCRYPTPROV hProv; struct archive_wstring temp_name; wchar_t *ws; DWORD attr; wchar_t *xp, *ep; int fd; - - hProv = (HCRYPTPROV)NULL; +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + BCRYPT_ALG_HANDLE hAlg = NULL; +#else + HCRYPTPROV hProv = (HCRYPTPROV)NULL; +#endif fd = -1; ws = NULL; @@ -314,23 +327,42 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) abort(); } +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, + NULL, 0))) { + la_dosmaperr(GetLastError()); + goto exit_tmpfile; + } +#else if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } +#endif for (;;) { wchar_t *p; HANDLE h; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif /* Generate a random file name through CryptGenRandom(). */ p = xp; +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p, + (DWORD)(ep - p)*sizeof(wchar_t), 0))) { + la_dosmaperr(GetLastError()); + goto exit_tmpfile; + } +#else if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t), (BYTE*)p)) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } +#endif for (; p < ep; p++) *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))]; @@ -347,6 +379,17 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) /* mkstemp */ attr = FILE_ATTRIBUTE_NORMAL; } +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileAttributes = attr & 0xFFFF; + createExParams.dwFileFlags = attr & 0xFFF00000; + h = CreateFile2(ws, + GENERIC_READ | GENERIC_WRITE | DELETE, + 0,/* Not share */ + CREATE_NEW, + &createExParams); +#else h = CreateFileW(ws, GENERIC_READ | GENERIC_WRITE | DELETE, 0,/* Not share */ @@ -354,6 +397,7 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) CREATE_NEW,/* Create a new file only */ attr, NULL); +#endif if (h == INVALID_HANDLE_VALUE) { /* The same file already exists. retry with * a new filename. */ @@ -372,8 +416,13 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) break;/* success! */ } exit_tmpfile: +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + if (hAlg != NULL) + BCryptCloseAlgorithmProvider(hAlg, 0); +#else if (hProv != (HCRYPTPROV)NULL) CryptReleaseContext(hProv, 0); +#endif free(ws); if (template == temp_name.s) archive_wstring_free(&temp_name); diff --git a/src/libs/3rdparty/libarchive/archive_windows.c b/src/libs/3rdparty/libarchive/archive_windows.c index 624e27009..ebc5eefb8 100644 --- a/src/libs/3rdparty/libarchive/archive_windows.c +++ b/src/libs/3rdparty/libarchive/archive_windows.c @@ -234,7 +234,11 @@ la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode, { wchar_t *wpath; HANDLE handle; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif +#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) handle = CreateFileA(path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); @@ -242,12 +246,25 @@ la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode, return (handle); if (GetLastError() != ERROR_PATH_NOT_FOUND) return (handle); +#endif wpath = __la_win_permissive_name(path); if (wpath == NULL) - return (handle); + return INVALID_HANDLE_VALUE; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileAttributes = dwFlagsAndAttributes & 0xFFFF; + createExParams.dwFileFlags = dwFlagsAndAttributes & 0xFFF00000; + createExParams.dwSecurityQosFlags = dwFlagsAndAttributes & 0x000F00000; + createExParams.lpSecurityAttributes = lpSecurityAttributes; + createExParams.hTemplateFile = hTemplateFile; + handle = CreateFile2(wpath, dwDesiredAccess, dwShareMode, + dwCreationDisposition, &createExParams); +#else /* !WINAPI_PARTITION_DESKTOP */ handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); +#endif /* !WINAPI_PARTITION_DESKTOP */ free(wpath); return (handle); } @@ -305,7 +322,10 @@ __la_open(const char *path, int flags, ...) * "Permission denied" error. */ attr = GetFileAttributesA(path); - if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) { +#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) + if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) +#endif + { ws = __la_win_permissive_name(path); if (ws == NULL) { errno = EINVAL; @@ -320,7 +340,7 @@ __la_open(const char *path, int flags, ...) } if (attr & FILE_ATTRIBUTE_DIRECTORY) { HANDLE handle; - +#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) if (ws != NULL) handle = CreateFileW(ws, 0, 0, NULL, OPEN_EXISTING, @@ -333,6 +353,15 @@ __la_open(const char *path, int flags, ...) FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_READONLY, NULL); +#else /* !WINAPI_PARTITION_DESKTOP */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileAttributes = FILE_ATTRIBUTE_READONLY; + createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS; + handle = CreateFile2(ws, 0, 0, + OPEN_EXISTING, &createExParams); +#endif /* !WINAPI_PARTITION_DESKTOP */ free(ws); if (handle == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); diff --git a/src/libs/3rdparty/libarchive/archive_write.c b/src/libs/3rdparty/libarchive/archive_write.c index 27626b541..ec3c95c56 100644 --- a/src/libs/3rdparty/libarchive/archive_write.c +++ b/src/libs/3rdparty/libarchive/archive_write.c @@ -310,6 +310,25 @@ __archive_write_output(struct archive_write *a, const void *buff, size_t length) return (__archive_write_filter(a->filter_first, buff, length)); } +static int +__archive_write_filters_flush(struct archive_write *a) +{ + struct archive_write_filter *f; + int ret, ret1; + + ret = ARCHIVE_OK; + for (f = a->filter_first; f != NULL; f = f->next_filter) { + if (f->flush != NULL && f->bytes_written > 0) { + ret1 = (f->flush)(f); + if (ret1 < ret) + ret = ret1; + if (ret1 < ARCHIVE_WARN) + f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL; + } + } + return (ret); +} + int __archive_write_nulls(struct archive_write *a, size_t length) { @@ -740,6 +759,18 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) return (ARCHIVE_FAILED); } + /* Flush filters at boundary. */ + r2 = __archive_write_filters_flush(a); + if (r2 == ARCHIVE_FAILED) { + return (ARCHIVE_FAILED); + } + if (r2 == ARCHIVE_FATAL) { + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); + } + if (r2 < ret) + ret = r2; + /* Format and write header. */ r2 = ((a->format_write_header)(a, entry)); if (r2 == ARCHIVE_FAILED) { diff --git a/src/libs/3rdparty/libarchive/archive_write_add_filter_bzip2.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_bzip2.c index 7001e9c6b..3e5c0891a 100644 --- a/src/libs/3rdparty/libarchive/archive_write_add_filter_bzip2.c +++ b/src/libs/3rdparty/libarchive/archive_write_add_filter_bzip2.c @@ -190,7 +190,7 @@ archive_compressor_bzip2_open(struct archive_write_filter *f) memset(&data->stream, 0, sizeof(data->stream)); data->stream.next_out = data->compressed; - data->stream.avail_out = data->compressed_buffer_size; + data->stream.avail_out = (uint32_t)data->compressed_buffer_size; f->write = archive_compressor_bzip2_write; /* Initialize compression library */ @@ -244,7 +244,7 @@ archive_compressor_bzip2_write(struct archive_write_filter *f, /* Compress input data to output buffer */ SET_NEXT_IN(data, buff); - data->stream.avail_in = length; + data->stream.avail_in = (uint32_t)length; if (drive_compressor(f, data, 0)) return (ARCHIVE_FATAL); return (ARCHIVE_OK); @@ -313,7 +313,7 @@ drive_compressor(struct archive_write_filter *f, return (ARCHIVE_FATAL); } data->stream.next_out = data->compressed; - data->stream.avail_out = data->compressed_buffer_size; + data->stream.avail_out = (uint32_t)data->compressed_buffer_size; } /* If there's nothing to do, we're done. */ diff --git a/src/libs/3rdparty/libarchive/archive_write_add_filter_compress.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_compress.c index d404fae7d..3ed269fce 100644 --- a/src/libs/3rdparty/libarchive/archive_write_add_filter_compress.c +++ b/src/libs/3rdparty/libarchive/archive_write_add_filter_compress.c @@ -352,7 +352,7 @@ archive_compressor_compress_write(struct archive_write_filter *f, while (length--) { c = *bp++; state->in_count++; - state->cur_fcode = (c << 16) + state->cur_code; + state->cur_fcode = (c << 16) | state->cur_code; i = ((c << HSHIFT) ^ state->cur_code); /* Xor hashing. */ if (state->hashtab[i] == state->cur_fcode) { diff --git a/src/libs/3rdparty/libarchive/archive_write_add_filter_lz4.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_lz4.c index cf19fadd5..6ac450357 100644 --- a/src/libs/3rdparty/libarchive/archive_write_add_filter_lz4.c +++ b/src/libs/3rdparty/libarchive/archive_write_add_filter_lz4.c @@ -518,10 +518,10 @@ drive_compressor_independence(struct archive_write_filter *f, const char *p, } else { /* The buffer is not compressed. The compressed size was * bigger than its uncompressed size. */ - archive_le32enc(data->out, length | 0x80000000); + archive_le32enc(data->out, (uint32_t)(length | 0x80000000)); data->out += 4; memcpy(data->out, p, length); - outsize = length; + outsize = (uint32_t)length; } data->out += outsize; if (data->block_checksum) { @@ -603,10 +603,10 @@ drive_compressor_dependence(struct archive_write_filter *f, const char *p, } else { /* The buffer is not compressed. The compressed size was * bigger than its uncompressed size. */ - archive_le32enc(data->out, length | 0x80000000); + archive_le32enc(data->out, (uint32_t)(length | 0x80000000)); data->out += 4; memcpy(data->out, p, length); - outsize = length; + outsize = (uint32_t)length; } data->out += outsize; if (data->block_checksum) { diff --git a/src/libs/3rdparty/libarchive/archive_write_add_filter_zstd.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_zstd.c index e85b7669c..584cfb668 100644 --- a/src/libs/3rdparty/libarchive/archive_write_add_filter_zstd.c +++ b/src/libs/3rdparty/libarchive/archive_write_add_filter_zstd.c @@ -31,6 +31,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include <errno.h> #endif +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif #ifdef HAVE_STDLIB_H #include <stdlib.h> #endif @@ -50,10 +53,21 @@ __FBSDID("$FreeBSD$"); struct private_data { int compression_level; - int threads; + int threads; #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR + enum { + running, + finishing, + resetting, + } state; + int frame_per_file; + size_t min_frame_size; + size_t max_frame_size; + size_t cur_frame; + size_t cur_frame_in; + size_t cur_frame_out; + size_t total_in; ZSTD_CStream *cstream; - int64_t total_in; ZSTD_outBuffer out; #else struct archive_write_program_data *pdata; @@ -75,6 +89,7 @@ static int archive_compressor_zstd_options(struct archive_write_filter *, static int archive_compressor_zstd_open(struct archive_write_filter *); static int archive_compressor_zstd_write(struct archive_write_filter *, const void *, size_t); +static int archive_compressor_zstd_flush(struct archive_write_filter *); static int archive_compressor_zstd_close(struct archive_write_filter *); static int archive_compressor_zstd_free(struct archive_write_filter *); #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR @@ -103,6 +118,7 @@ archive_write_add_filter_zstd(struct archive *_a) f->data = data; f->open = &archive_compressor_zstd_open; f->options = &archive_compressor_zstd_options; + f->flush = &archive_compressor_zstd_flush; f->close = &archive_compressor_zstd_close; f->free = &archive_compressor_zstd_free; f->code = ARCHIVE_FILTER_ZSTD; @@ -110,6 +126,11 @@ archive_write_add_filter_zstd(struct archive *_a) data->compression_level = CLEVEL_DEFAULT; data->threads = 0; #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR + data->frame_per_file = 0; + data->min_frame_size = 0; + data->max_frame_size = SIZE_MAX; + data->cur_frame_in = 0; + data->cur_frame_out = 0; data->cstream = ZSTD_createCStream(); if (data->cstream == NULL) { free(data); @@ -147,29 +168,18 @@ archive_compressor_zstd_free(struct archive_write_filter *f) return (ARCHIVE_OK); } -static int string_is_numeric (const char* value) +static int string_to_number(const char *string, intmax_t *numberp) { - size_t len = strlen(value); - size_t i; - - if (len == 0) { - return (ARCHIVE_WARN); - } - else if (len == 1 && !(value[0] >= '0' && value[0] <= '9')) { - return (ARCHIVE_WARN); - } - else if (!(value[0] >= '0' && value[0] <= '9') && - value[0] != '-' && value[0] != '+') { - return (ARCHIVE_WARN); - } - - for (i = 1; i < len; i++) { - if (!(value[i] >= '0' && value[i] <= '9')) { - return (ARCHIVE_WARN); - } - } - - return (ARCHIVE_OK); + char *end; + + if (string == NULL || *string == '\0') + return (ARCHIVE_WARN); + *numberp = strtoimax(string, &end, 10); + if (end == string || *end != '\0' || errno == EOVERFLOW) { + *numberp = 0; + return (ARCHIVE_WARN); + } + return (ARCHIVE_OK); } /* @@ -182,13 +192,13 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, struct private_data *data = (struct private_data *)f->data; if (strcmp(key, "compression-level") == 0) { - int level = atoi(value); + intmax_t level; + if (string_to_number(value, &level) != ARCHIVE_OK) { + return (ARCHIVE_WARN); + } /* If we don't have the library, hard-code the max level */ int minimum = CLEVEL_MIN; int maximum = CLEVEL_MAX; - if (string_is_numeric(value) != ARCHIVE_OK) { - return (ARCHIVE_WARN); - } #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR maximum = ZSTD_maxCLevel(); #if ZSTD_VERSION_NUMBER >= MINVER_MINCLEVEL @@ -204,22 +214,43 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, if (level < minimum || level > maximum) { return (ARCHIVE_WARN); } - data->compression_level = level; + data->compression_level = (int)level; return (ARCHIVE_OK); } else if (strcmp(key, "threads") == 0) { - int threads = atoi(value); - if (string_is_numeric(value) != ARCHIVE_OK) { + intmax_t threads; + if (string_to_number(value, &threads) != ARCHIVE_OK) { return (ARCHIVE_WARN); } - - int minimum = 0; - - if (threads < minimum) { + if (threads < 0) { return (ARCHIVE_WARN); } - - data->threads = threads; + data->threads = (int)threads; + return (ARCHIVE_OK); +#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR + } else if (strcmp(key, "frame-per-file") == 0) { + data->frame_per_file = 1; + return (ARCHIVE_OK); + } else if (strcmp(key, "min-frame-size") == 0) { + intmax_t min_frame_size; + if (string_to_number(value, &min_frame_size) != ARCHIVE_OK) { + return (ARCHIVE_WARN); + } + if (min_frame_size < 0) { + return (ARCHIVE_WARN); + } + data->min_frame_size = min_frame_size; + return (ARCHIVE_OK); + } else if (strcmp(key, "max-frame-size") == 0) { + intmax_t max_frame_size; + if (string_to_number(value, &max_frame_size) != ARCHIVE_OK) { + return (ARCHIVE_WARN); + } + if (max_frame_size < 1024) { + return (ARCHIVE_WARN); + } + data->max_frame_size = max_frame_size; return (ARCHIVE_OK); +#endif } /* Note: The "warn" return is just to inform the options @@ -281,15 +312,22 @@ archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff, size_t length) { struct private_data *data = (struct private_data *)f->data; - int ret; - /* Update statistics */ - data->total_in += length; + return (drive_compressor(f, data, 0, buff, length)); +} - if ((ret = drive_compressor(f, data, 0, buff, length)) != ARCHIVE_OK) - return (ret); +/* + * Flush the compressed stream. + */ +static int +archive_compressor_zstd_flush(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; - return (ARCHIVE_OK); + if (data->frame_per_file && data->state == running && + data->cur_frame_out > data->min_frame_size) + data->state = finishing; + return (drive_compressor(f, data, 1, NULL, 0)); } /* @@ -300,57 +338,72 @@ archive_compressor_zstd_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - /* Finish zstd frame */ - return drive_compressor(f, data, 1, NULL, 0); + if (data->state == running) + data->state = finishing; + return (drive_compressor(f, data, 1, NULL, 0)); } /* * Utility function to push input data through compressor, * writing full output blocks as necessary. - * - * Note that this handles both the regular write case (finishing == - * false) and the end-of-archive case (finishing == true). */ static int drive_compressor(struct archive_write_filter *f, - struct private_data *data, int finishing, const void *src, size_t length) + struct private_data *data, int flush, const void *src, size_t length) { - ZSTD_inBuffer in = (ZSTD_inBuffer) { src, length, 0 }; + ZSTD_inBuffer in = { .src = src, .size = length, .pos = 0 }; + size_t ipos, opos, zstdret = 0; + int ret; for (;;) { - if (data->out.pos == data->out.size) { - const int ret = __archive_write_filter(f->next_filter, - data->out.dst, data->out.size); + ipos = in.pos; + opos = data->out.pos; + switch (data->state) { + case running: + if (in.pos == in.size) + return (ARCHIVE_OK); + zstdret = ZSTD_compressStream(data->cstream, + &data->out, &in); + if (ZSTD_isError(zstdret)) + goto zstd_fatal; + break; + case finishing: + zstdret = ZSTD_endStream(data->cstream, &data->out); + if (ZSTD_isError(zstdret)) + goto zstd_fatal; + if (zstdret == 0) + data->state = resetting; + break; + case resetting: + ZSTD_CCtx_reset(data->cstream, ZSTD_reset_session_only); + data->cur_frame++; + data->cur_frame_in = 0; + data->cur_frame_out = 0; + data->state = running; + break; + } + data->total_in += in.pos - ipos; + data->cur_frame_in += in.pos - ipos; + data->cur_frame_out += data->out.pos - opos; + if (data->state == running && + data->cur_frame_in >= data->max_frame_size) { + data->state = finishing; + } + if (data->out.pos == data->out.size || + (flush && data->out.pos > 0)) { + ret = __archive_write_filter(f->next_filter, + data->out.dst, data->out.pos); if (ret != ARCHIVE_OK) - return (ARCHIVE_FATAL); + goto fatal; data->out.pos = 0; } - - /* If there's nothing to do, we're done. */ - if (!finishing && in.pos == in.size) - return (ARCHIVE_OK); - - { - const size_t zstdret = !finishing ? - ZSTD_compressStream(data->cstream, &data->out, &in) - : ZSTD_endStream(data->cstream, &data->out); - - if (ZSTD_isError(zstdret)) { - archive_set_error(f->archive, - ARCHIVE_ERRNO_MISC, - "Zstd compression failed: %s", - ZSTD_getErrorName(zstdret)); - return (ARCHIVE_FATAL); - } - - /* If we're finishing, 0 means nothing left to flush */ - if (finishing && zstdret == 0) { - const int ret = __archive_write_filter(f->next_filter, - data->out.dst, data->out.pos); - return (ret); - } - } } +zstd_fatal: + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "Zstd compression failed: %s", + ZSTD_getErrorName(zstdret)); +fatal: + return (ARCHIVE_FATAL); } #else /* HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR */ @@ -367,17 +420,9 @@ archive_compressor_zstd_open(struct archive_write_filter *f) archive_strcpy(&as, "zstd --no-check"); if (data->compression_level < CLEVEL_STD_MIN) { - struct archive_string as2; - archive_string_init(&as2); - archive_string_sprintf(&as2, " --fast=%d", -data->compression_level); - archive_string_concat(&as, &as2); - archive_string_free(&as2); + archive_string_sprintf(&as, " --fast=%d", -data->compression_level); } else { - struct archive_string as2; - archive_string_init(&as2); - archive_string_sprintf(&as2, " -%d", data->compression_level); - archive_string_concat(&as, &as2); - archive_string_free(&as2); + archive_string_sprintf(&as, " -%d", data->compression_level); } if (data->compression_level > CLEVEL_STD_MAX) { @@ -385,11 +430,7 @@ archive_compressor_zstd_open(struct archive_write_filter *f) } if (data->threads != 0) { - struct archive_string as2; - archive_string_init(&as2); - archive_string_sprintf(&as2, " --threads=%d", data->threads); - archive_string_concat(&as, &as2); - archive_string_free(&as2); + archive_string_sprintf(&as, " --threads=%d", data->threads); } f->write = archive_compressor_zstd_write; @@ -408,6 +449,14 @@ archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff, } static int +archive_compressor_zstd_flush(struct archive_write_filter *f) +{ + (void)f; /* UNUSED */ + + return (ARCHIVE_OK); +} + +static int archive_compressor_zstd_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; diff --git a/src/libs/3rdparty/libarchive/archive_write_disk_posix.c b/src/libs/3rdparty/libarchive/archive_write_disk_posix.c index 09a5eef03..c8c2e1058 100644 --- a/src/libs/3rdparty/libarchive/archive_write_disk_posix.c +++ b/src/libs/3rdparty/libarchive/archive_write_disk_posix.c @@ -397,6 +397,7 @@ static int set_times_from_entry(struct archive_write_disk *); static struct fixup_entry *sort_dir_list(struct fixup_entry *p); static ssize_t write_data_block(struct archive_write_disk *, const char *, size_t); +static void close_file_descriptor(struct archive_write_disk *); static int _archive_write_disk_close(struct archive *); static int _archive_write_disk_free(struct archive *); @@ -514,7 +515,12 @@ lazy_stat(struct archive_write_disk *a) * XXX At this point, symlinks should not be hit, otherwise * XXX a race occurred. Do we want to check explicitly for that? */ - if (lstat(a->name, &a->st) == 0) { +#ifdef HAVE_LSTAT + if (lstat(a->name, &a->st) == 0) +#else + if (la_stat(a->name, &a->st) == 0) +#endif + { a->pst = &a->st; return (ARCHIVE_OK); } @@ -1605,12 +1611,12 @@ hfs_write_data_block(struct archive_write_disk *a, const char *buff, "Seek failed"); return (ARCHIVE_FATAL); } else if (a->offset > a->fd_offset) { - int64_t skip = a->offset - a->fd_offset; + uint64_t skip = a->offset - a->fd_offset; char nullblock[1024]; memset(nullblock, 0, sizeof(nullblock)); while (skip > 0) { - if (skip > (int64_t)sizeof(nullblock)) + if (skip > sizeof(nullblock)) bytes_written = hfs_write_decmpfs_block( a, nullblock, sizeof(nullblock)); else @@ -1725,8 +1731,10 @@ _archive_write_disk_finish_entry(struct archive *_a) else r = hfs_write_data_block( a, null_d, a->file_remaining_bytes); - if (r < 0) + if (r < 0) { + close_file_descriptor(a); return ((int)r); + } } #endif } else { @@ -1735,6 +1743,7 @@ _archive_write_disk_finish_entry(struct archive *_a) a->filesize == 0) { archive_set_error(&a->archive, errno, "File size could not be restored"); + close_file_descriptor(a); return (ARCHIVE_FAILED); } #endif @@ -1744,8 +1753,10 @@ _archive_write_disk_finish_entry(struct archive *_a) * to see what happened. */ a->pst = NULL; - if ((ret = lazy_stat(a)) != ARCHIVE_OK) - return (ret); + if ((ret = lazy_stat(a)) != ARCHIVE_OK) { + close_file_descriptor(a); + return (ret); + } /* We can use lseek()/write() to extend the file if * ftruncate didn't work or isn't available. */ if (a->st.st_size < a->filesize) { @@ -1753,11 +1764,13 @@ _archive_write_disk_finish_entry(struct archive *_a) if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek failed"); + close_file_descriptor(a); return (ARCHIVE_FATAL); } if (write(a->fd, &nul, 1) < 0) { archive_set_error(&a->archive, errno, "Write to restore size failed"); + close_file_descriptor(a); return (ARCHIVE_FATAL); } a->pst = NULL; @@ -2154,7 +2167,11 @@ restore_entry(struct archive_write_disk *a) * then don't follow it. */ if (r != 0 || !S_ISDIR(a->mode)) +#ifdef HAVE_LSTAT r = lstat(a->name, &a->st); +#else + r = la_stat(a->name, &a->st); +#endif if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); @@ -2550,7 +2567,12 @@ _archive_write_disk_close(struct archive *_a) goto skip_fixup_entry; } else #endif - if (lstat(p->name, &st) != 0 || + if ( +#ifdef HAVE_LSTAT + lstat(p->name, &st) != 0 || +#else + la_stat(p->name, &st) != 0 || +#endif la_verify_filetype(st.st_mode, p->filetype) == 0) { goto skip_fixup_entry; @@ -2565,7 +2587,12 @@ _archive_write_disk_close(struct archive *_a) goto skip_fixup_entry; } else #endif - if (lstat(p->name, &st) != 0 || + if ( +#ifdef HAVE_LSTAT + lstat(p->name, &st) != 0 || +#else + la_stat(p->name, &st) != 0 || +#endif la_verify_filetype(st.st_mode, p->filetype) == 0) { goto skip_fixup_entry; @@ -2785,8 +2812,8 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, !(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)) /* Platform doesn't have lstat, so we can't look for symlinks. */ (void)path; /* UNUSED */ - (void)error_number; /* UNUSED */ - (void)error_string; /* UNUSED */ + (void)a_eno; /* UNUSED */ + (void)a_estr; /* UNUSED */ (void)flags; /* UNUSED */ (void)checking_linkname; /* UNUSED */ return (ARCHIVE_OK); @@ -2859,8 +2886,10 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, /* Check that we haven't hit a symlink. */ #if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) r = fstatat(chdir_fd, head, &st, AT_SYMLINK_NOFOLLOW); -#else +#elif defined(HAVE_LSTAT) r = lstat(head, &st); +#else + r = la_stat(head, &st); #endif if (r != 0) { tail[0] = c; @@ -3558,7 +3587,9 @@ set_time(int fd, int mode, const char *name, (void)fd; /* UNUSED */ (void)mode; /* UNUSED */ (void)name; /* UNUSED */ + (void)atime; /* UNUSED */ (void)atime_nsec; /* UNUSED */ + (void)mtime; /* UNUSED */ (void)mtime_nsec; /* UNUSED */ return (ARCHIVE_WARN); #endif @@ -4391,7 +4422,12 @@ fixup_appledouble(struct archive_write_disk *a, const char *pathname) */ archive_strncpy(&datafork, pathname, p - pathname); archive_strcat(&datafork, p + 2); - if (lstat(datafork.s, &st) == -1 || + if ( +#ifdef HAVE_LSTAT + lstat(datafork.s, &st) == -1 || +#else + la_stat(datafork.s, &st) == -1 || +#endif (st.st_mode & AE_IFMT) != AE_IFREG) goto skip_appledouble; @@ -4707,5 +4743,17 @@ archive_write_disk_set_acls(struct archive *a, int fd, const char *name, } #endif +/* + * Close the file descriptor if one is open. + */ +static void close_file_descriptor(struct archive_write_disk* a) +{ + if (a->fd >= 0) { + close(a->fd); + a->fd = -1; + } +} + + #endif /* !_WIN32 || __CYGWIN__ */ diff --git a/src/libs/3rdparty/libarchive/archive_write_disk_windows.c b/src/libs/3rdparty/libarchive/archive_write_disk_windows.c index 88df3ce02..7b9ea7493 100644 --- a/src/libs/3rdparty/libarchive/archive_write_disk_windows.c +++ b/src/libs/3rdparty/libarchive/archive_write_disk_windows.c @@ -254,9 +254,9 @@ static ssize_t _archive_write_disk_data_block(struct archive *, const void *, * which is high-16-bits of nFileIndexHigh. */ #define bhfi_ino(bhfi) \ ((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \ - + (bhfi)->nFileIndexLow) + | (bhfi)->nFileIndexLow) #define bhfi_size(bhfi) \ - ((((int64_t)(bhfi)->nFileSizeHigh) << 32) + (bhfi)->nFileSizeLow) + ((((int64_t)(bhfi)->nFileSizeHigh) << 32) | (bhfi)->nFileSizeLow) static int file_information(struct archive_write_disk *a, wchar_t *path, @@ -266,6 +266,9 @@ file_information(struct archive_write_disk *a, wchar_t *path, int r; DWORD flag = FILE_FLAG_BACKUP_SEMANTICS; WIN32_FIND_DATAW findData; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif if (sim_lstat || mode != NULL) { h = FindFirstFileW(path, &findData); @@ -290,14 +293,27 @@ file_information(struct archive_write_disk *a, wchar_t *path, (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK))) flag |= FILE_FLAG_OPEN_REPARSE_POINT; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = flag; + h = CreateFile2(a->name, 0, 0, + OPEN_EXISTING, &createExParams); +#else h = CreateFileW(a->name, 0, 0, NULL, OPEN_EXISTING, flag, NULL); +#endif if (h == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_NAME) { wchar_t *full; full = __la_win_permissive_name_w(path); +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + h = CreateFile2(full, 0, 0, + OPEN_EXISTING, &createExParams); +#else h = CreateFileW(full, 0, 0, NULL, OPEN_EXISTING, flag, NULL); +#endif free(full); } if (h == INVALID_HANDLE_VALUE) { @@ -559,6 +575,7 @@ la_mktemp(struct archive_write_disk *a) return (fd); } +#if _WIN32_WINNT < _WIN32_WINNT_VISTA static void * la_GetFunctionKernel32(const char *name) { @@ -574,18 +591,24 @@ la_GetFunctionKernel32(const char *name) } return (void *)GetProcAddress(lib, name); } +#endif static int la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) { - static BOOLEAN (WINAPI *f)(LPWSTR, LPWSTR, LPSECURITY_ATTRIBUTES); - static int set; + static BOOL (WINAPI *f)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); BOOL ret; +#if _WIN32_WINNT < _WIN32_WINNT_XP + static int set; +/* CreateHardLinkW is available since XP and always loaded */ if (!set) { set = 1; f = la_GetFunctionKernel32("CreateHardLinkW"); } +#else + f = CreateHardLinkW; +#endif if (!f) { errno = ENOTSUP; return (0); @@ -624,7 +647,6 @@ static int la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target, int linktype) { static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD); - static int set; wchar_t *ttarget, *p; size_t len; DWORD attrs = 0; @@ -632,10 +654,20 @@ la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target, DWORD newflags = 0; BOOL ret = 0; +#if _WIN32_WINNT < _WIN32_WINNT_VISTA +/* CreateSymbolicLinkW is available since Vista and always loaded */ + static int set; if (!set) { set = 1; f = la_GetFunctionKernel32("CreateSymbolicLinkW"); } +#else +# if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + f = CreateSymbolicLinkW; +# else + f = NULL; +# endif +#endif if (!f) return (0); @@ -1185,6 +1217,8 @@ _archive_write_disk_finish_entry(struct archive *_a) if (la_ftruncate(a->fh, a->filesize) == -1) { archive_set_error(&a->archive, errno, "File size could not be restored"); + CloseHandle(a->fh); + a->fh = INVALID_HANDLE_VALUE; return (ARCHIVE_FAILED); } } @@ -1656,6 +1690,9 @@ create_filesystem_object(struct archive_write_disk *a) mode_t final_mode, mode; int r; DWORD attrs = 0; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif /* We identify hard/symlinks according to the link names. */ /* Since link(2) and symlink(2) don't handle modes, we're done here. */ @@ -1719,8 +1756,16 @@ create_filesystem_object(struct archive_write_disk *a) a->todo = 0; a->deferred = 0; } else if (r == 0 && a->filesize > 0) { +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + a->fh = CreateFile2(namefull, GENERIC_WRITE, 0, + TRUNCATE_EXISTING, &createExParams); +#else a->fh = CreateFileW(namefull, GENERIC_WRITE, 0, NULL, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +#endif if (a->fh == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); r = errno; @@ -1783,14 +1828,27 @@ create_filesystem_object(struct archive_write_disk *a) a->tmpname = NULL; fullname = a->name; /* O_WRONLY | O_CREAT | O_EXCL */ +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + a->fh = CreateFile2(fullname, GENERIC_WRITE, 0, + CREATE_NEW, &createExParams); +#else a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); +#endif if (a->fh == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_NAME && fullname == a->name) { fullname = __la_win_permissive_name_w(a->name); +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + a->fh = CreateFile2(fullname, GENERIC_WRITE, 0, + CREATE_NEW, &createExParams); +#else a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); +#endif } if (a->fh == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_ACCESS_DENIED) { @@ -2551,14 +2609,25 @@ set_times(struct archive_write_disk *a, hw = NULL; } else { wchar_t *ws; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif if (S_ISLNK(mode)) return (ARCHIVE_OK); ws = __la_win_permissive_name_w(name); if (ws == NULL) goto settimes_failed; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS; + hw = CreateFile2(ws, FILE_WRITE_ATTRIBUTES, 0, + OPEN_EXISTING, &createExParams); +#else hw = CreateFileW(ws, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); +#endif free(ws); if (hw == INVALID_HANDLE_VALUE) goto settimes_failed; diff --git a/src/libs/3rdparty/libarchive/archive_write_private.h b/src/libs/3rdparty/libarchive/archive_write_private.h index 155fdd734..6522e6521 100644 --- a/src/libs/3rdparty/libarchive/archive_write_private.h +++ b/src/libs/3rdparty/libarchive/archive_write_private.h @@ -53,6 +53,7 @@ struct archive_write_filter { const char *key, const char *value); int (*open)(struct archive_write_filter *); int (*write)(struct archive_write_filter *, const void *, size_t); + int (*flush)(struct archive_write_filter *); int (*close)(struct archive_write_filter *); int (*free)(struct archive_write_filter *); void *data; diff --git a/src/libs/3rdparty/libarchive/archive_write_set_format_7zip.c b/src/libs/3rdparty/libarchive/archive_write_set_format_7zip.c index d5ca9a665..1e40601c4 100644 --- a/src/libs/3rdparty/libarchive/archive_write_set_format_7zip.c +++ b/src/libs/3rdparty/libarchive/archive_write_set_format_7zip.c @@ -165,7 +165,7 @@ struct file { mode_t mode; uint32_t crc32; - signed int dir:1; + unsigned dir:1; }; struct _7zip { @@ -1809,11 +1809,11 @@ compression_init_encoder_bzip2(struct archive *a, * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in; - strm->avail_in = lastrm->avail_in; + strm->avail_in = (uint32_t)lastrm->avail_in; strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff); strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32); strm->next_out = (char *)lastrm->next_out; - strm->avail_out = lastrm->avail_out; + strm->avail_out = (uint32_t)lastrm->avail_out; strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff); strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32); if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) { @@ -1842,11 +1842,11 @@ compression_code_bzip2(struct archive *a, * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in; - strm->avail_in = lastrm->avail_in; + strm->avail_in = (uint32_t)lastrm->avail_in; strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff); strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32); strm->next_out = (char *)lastrm->next_out; - strm->avail_out = lastrm->avail_out; + strm->avail_out = (uint32_t)lastrm->avail_out; strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff); strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32); r = BZ2_bzCompress(strm, diff --git a/src/libs/3rdparty/libarchive/archive_write_set_format_iso9660.c b/src/libs/3rdparty/libarchive/archive_write_set_format_iso9660.c index 58b7216a8..2a3ae07fa 100644 --- a/src/libs/3rdparty/libarchive/archive_write_set_format_iso9660.c +++ b/src/libs/3rdparty/libarchive/archive_write_set_format_iso9660.c @@ -289,12 +289,12 @@ struct isoent { struct extr_rec *current; } extr_rec_list; - signed int virtual:1; + unsigned int virtual:1; /* If set to one, this file type is a directory. * A convenience flag to be used as * "archive_entry_filetype(isoent->file->entry) == AE_IFDIR". */ - signed int dir:1; + unsigned int dir:1; }; struct hardlink { @@ -652,7 +652,7 @@ struct iso_option { #define VOLUME_IDENTIFIER_SIZE 32 /* - * Usage : !zisofs [DEFAULT] + * Usage : !zisofs [DEFAULT] * : Disable to generate RRIP 'ZF' extension. * : zisofs * : Make files zisofs file and generate RRIP 'ZF' @@ -689,7 +689,7 @@ struct iso9660 { uint64_t bytes_remaining; int need_multi_extent; - /* Temporary string buffer for Joliet extension. */ + /* Temporary string buffer for Joliet extension. */ struct archive_string utf16be; struct archive_string mbs; @@ -755,9 +755,9 @@ struct iso9660 { /* Used for making zisofs. */ struct { - signed int detect_magic:1; - signed int making:1; - signed int allzero:1; + unsigned int detect_magic:1; + unsigned int making:1; + unsigned int allzero:1; unsigned char magic_buffer[64]; int magic_cnt; @@ -2521,12 +2521,11 @@ get_gmoffset(struct tm *tm) static void get_tmfromtime(struct tm *tm, time_t *t) { -#if HAVE_LOCALTIME_R +#if HAVE_LOCALTIME_S + localtime_s(tm, t); +#elif HAVE_LOCALTIME_R tzset(); localtime_r(t, tm); -#elif HAVE__LOCALTIME64_S - __time64_t tmp_t = (__time64_t) *t; //time_t may be shorter than 64 bits - _localtime64_s(tm, &tmp_t); #else memcpy(tm, localtime(t), sizeof(*tm)); #endif @@ -4074,11 +4073,8 @@ write_information_block(struct archive_write *a) } memset(info.s, 0, info_size); opt = 0; -#if defined(HAVE__CTIME64_S) - { - __time64_t iso9660_birth_time_tmp = (__time64_t) iso9660->birth_time; //time_t may be shorter than 64 bits - _ctime64_s(buf, sizeof(buf), &(iso9660_birth_time_tmp)); - } +#if defined(HAVE_CTIME_S) + ctime_s(buf, sizeof(buf), &(iso9660->birth_time)); #elif defined(HAVE_CTIME_R) ctime_r(&(iso9660->birth_time), buf); #else @@ -7802,8 +7798,8 @@ struct zisofs_extract { uint64_t pz_uncompressed_size; size_t uncompressed_buffer_size; - signed int initialized:1; - signed int header_passed:1; + unsigned int initialized:1; + unsigned int header_passed:1; uint32_t pz_offset; unsigned char *block_pointers; diff --git a/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c b/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c index cf1f4770e..c9c159164 100644 --- a/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c +++ b/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c @@ -100,6 +100,7 @@ static int has_non_ASCII(const char *); static void sparse_list_clear(struct pax *); static int sparse_list_add(struct pax *, int64_t, int64_t); static char *url_encode(const char *in); +static time_t get_ustar_max_mtime(void); /* * Set output format to 'restricted pax' format. @@ -595,6 +596,8 @@ archive_write_pax_header(struct archive_write *a, need_extension = 0; pax = (struct pax *)a->format_data; + const time_t ustar_max_mtime = get_ustar_max_mtime(); + /* Sanity check. */ if (archive_entry_pathname(entry_original) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -1116,16 +1119,13 @@ archive_write_pax_header(struct archive_write *a, } /* - * Technically, the mtime field in the ustar header can - * support 33 bits, but many platforms use signed 32-bit time - * values. The cutoff of 0x7fffffff here is a compromise. * Yes, this check is duplicated just below; this helps to * avoid writing an mtime attribute just to handle a * high-resolution timestamp in "restricted pax" mode. */ if (!need_extension && ((archive_entry_mtime(entry_main) < 0) - || (archive_entry_mtime(entry_main) >= 0x7fffffff))) + || (archive_entry_mtime(entry_main) >= ustar_max_mtime))) need_extension = 1; /* I use a star-compatible file flag attribute. */ @@ -1190,7 +1190,7 @@ archive_write_pax_header(struct archive_write *a, if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED || need_extension) { if (archive_entry_mtime(entry_main) < 0 || - archive_entry_mtime(entry_main) >= 0x7fffffff || + archive_entry_mtime(entry_main) >= ustar_max_mtime || archive_entry_mtime_nsec(entry_main) != 0) add_pax_attr_time(&(pax->pax_header), "mtime", archive_entry_mtime(entry_main), @@ -1428,7 +1428,7 @@ archive_write_pax_header(struct archive_write *a, /* Copy mtime, but clip to ustar limits. */ s = archive_entry_mtime(entry_main); if (s < 0) { s = 0; } - if (s >= 0x7fffffff) { s = 0x7fffffff; } + if (s > ustar_max_mtime) { s = ustar_max_mtime; } archive_entry_set_mtime(pax_attr_entry, s, 0); /* Standard ustar doesn't support atime. */ @@ -2046,3 +2046,18 @@ sparse_list_add(struct pax *pax, int64_t offset, int64_t length) return (_sparse_list_add_block(pax, offset, length, 0)); } +static time_t +get_ustar_max_mtime(void) +{ + /* + * Technically, the mtime field in the ustar header can + * support 33 bits. We are using all of them to keep + * tar/test/test_option_C_mtree.c simple and passing after 2038. + * For platforms that use signed 32-bit time values we + * use the 32-bit maximum. + */ + if (sizeof(time_t) > sizeof(int32_t)) + return (time_t)0x1ffffffff; + else + return (time_t)0x7fffffff; +} diff --git a/src/libs/3rdparty/libarchive/archive_write_set_format_warc.c b/src/libs/3rdparty/libarchive/archive_write_set_format_warc.c index 46b057341..0ef003e2f 100644 --- a/src/libs/3rdparty/libarchive/archive_write_set_format_warc.c +++ b/src/libs/3rdparty/libarchive/archive_write_set_format_warc.c @@ -329,30 +329,21 @@ xstrftime(struct archive_string *as, const char *fmt, time_t t) { /** like strftime(3) but for time_t objects */ struct tm *rt; -#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S) +#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S) struct tm timeHere; #endif -#if defined(HAVE__GMTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif char strtime[100]; size_t len; -#ifdef HAVE_GMTIME_R - if ((rt = gmtime_r(&t, &timeHere)) == NULL) - return; -#elif defined(HAVE__GMTIME64_S) - tmptime = t; - terr = _gmtime64_s(&timeHere, &tmptime); - if (terr) - rt = NULL; - else - rt = &timeHere; +#if defined(HAVE_GMTIME_S) + rt = gmtime_s(&timeHere, &t) ? NULL : &timeHere; +#elif defined(HAVE_GMTIME_R) + rt = gmtime_r(&t, &timeHere); #else - if ((rt = gmtime(&t)) == NULL) - return; + rt = gmtime(&t); #endif + if (!rt) + return; /* leave the hard yacker to our role model strftime() */ len = strftime(strtime, sizeof(strtime)-1, fmt, rt); archive_strncat(as, strtime, len); diff --git a/src/libs/3rdparty/libarchive/archive_write_set_format_xar.c b/src/libs/3rdparty/libarchive/archive_write_set_format_xar.c index d885f5c25..7307757d3 100644 --- a/src/libs/3rdparty/libarchive/archive_write_set_format_xar.c +++ b/src/libs/3rdparty/libarchive/archive_write_set_format_xar.c @@ -212,8 +212,8 @@ struct file { struct heap_data data; struct archive_string script; - signed int virtual:1; - signed int dir:1; + unsigned int virtual:1; + unsigned int dir:1; }; struct hardlink { @@ -906,15 +906,11 @@ xmlwrite_time(struct archive_write *a, xmlTextWriterPtr writer, { char timestr[100]; struct tm tm; -#if defined(HAVE__GMTIME64_S) - __time64_t tmptime; -#endif -#if defined(HAVE_GMTIME_R) +#if defined(HAVE_GMTIME_S) + gmtime_s(&tm, &t); +#elif defined(HAVE_GMTIME_R) gmtime_r(&t, &tm); -#elif defined(HAVE__GMTIME64_S) - tmptime = t; - _gmtime64_s(&tm, &tmptime); #else memcpy(&tm, gmtime(&t), sizeof(tm)); #endif diff --git a/src/libs/3rdparty/libarchive/archive_write_set_format_zip.c b/src/libs/3rdparty/libarchive/archive_write_set_format_zip.c index 8c14a7027..6821049c9 100644 --- a/src/libs/3rdparty/libarchive/archive_write_set_format_zip.c +++ b/src/libs/3rdparty/libarchive/archive_write_set_format_zip.c @@ -1382,25 +1382,14 @@ dos_time(const time_t unix_time) { struct tm *t; unsigned int dt; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif - /* This will not preserve time when creating/extracting the archive - * on two systems with different time zones. */ -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + t = localtime_s(&tmbuf, &unix_time) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) t = localtime_r(&unix_time, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = unix_time; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - t = NULL; - else - t = &tmbuf; #else t = localtime(&unix_time); #endif diff --git a/src/libs/3rdparty/libarchive/config_freebsd.h b/src/libs/3rdparty/libarchive/config_freebsd.h index 758621c4b..669f27246 100644 --- a/src/libs/3rdparty/libarchive/config_freebsd.h +++ b/src/libs/3rdparty/libarchive/config_freebsd.h @@ -111,6 +111,8 @@ #define HAVE_FCNTL 1 #define HAVE_FCNTL_H 1 #define HAVE_FDOPENDIR 1 +#define HAVE_FNMATCH 1 +#define HAVE_FNMATCH_H 1 #define HAVE_FORK 1 #define HAVE_FSEEKO 1 #define HAVE_FSTAT 1 @@ -123,6 +125,8 @@ #define HAVE_GETEUID 1 #define HAVE_GETGRGID_R 1 #define HAVE_GETGRNAM_R 1 +#define HAVE_GETLINE 1 +#define HAVE_GETOPT_OPTRESET 1 #define HAVE_GETPID 1 #define HAVE_GETPWNAM_R 1 #define HAVE_GETPWUID_R 1 @@ -201,6 +205,7 @@ #define HAVE_SYS_MOUNT_H 1 #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_POLL_H 1 +#define HAVE_SYS_QUEUE_H 1 #define HAVE_SYS_SELECT_H 1 #define HAVE_SYS_STATVFS_H 1 #define HAVE_SYS_STAT_H 1 @@ -234,7 +239,7 @@ #define HAVE_WMEMCPY 1 #define HAVE_WMEMMOVE 1 #define HAVE_ZLIB_H 1 -#define TIME_WITH_SYS_TIME 1 +#define HAVE_SYS_TIME_H 1 #if __FreeBSD_version >= 800505 #define HAVE_LIBLZMA 1 diff --git a/src/libs/3rdparty/libarchive/filter_fork_windows.c b/src/libs/3rdparty/libarchive/filter_fork_windows.c index 0b963975b..9e49c5655 100644 --- a/src/libs/3rdparty/libarchive/filter_fork_windows.c +++ b/src/libs/3rdparty/libarchive/filter_fork_windows.c @@ -31,6 +31,7 @@ #include "filter_fork.h" +#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) /* There are some editions of Windows ("nano server," for example) that * do not host user32.dll. If we want to keep running on those editions, * we need to delay-load WaitForInputIdle. */ @@ -224,6 +225,14 @@ fail: __archive_cmdline_free(acmd); return ARCHIVE_FAILED; } +#else /* !WINAPI_PARTITION_DESKTOP */ +int +__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout, HANDLE *out_child) +{ + (void)cmd; (void)child_stdin; (void) child_stdout; (void) out_child; + return ARCHIVE_FAILED; +} +#endif /* !WINAPI_PARTITION_DESKTOP */ void __archive_check_child(int in, int out) diff --git a/src/libs/3rdparty/libarchive/qt_attribution.json b/src/libs/3rdparty/libarchive/qt_attribution.json index df03d1702..76db5e26a 100644 --- a/src/libs/3rdparty/libarchive/qt_attribution.json +++ b/src/libs/3rdparty/libarchive/qt_attribution.json @@ -5,7 +5,7 @@ "Description": "Multi-format archive and compression library.", "QtUsage": "Used for reading and writing archive files in Qt Installer Framework", "Homepage": "https://www.libarchive.org", - "Version": "3.6.2", + "Version": "3.7.1", "License": "BSD 2-clause \"Simplified\" License", "LicenseId": "BSD-2-Clause", "LicenseFile": "COPYING", diff --git a/src/libs/3rdparty/libarchive/xxhash.c b/src/libs/3rdparty/libarchive/xxhash.c index f96e9d934..beacd2391 100644 --- a/src/libs/3rdparty/libarchive/xxhash.c +++ b/src/libs/3rdparty/libarchive/xxhash.c @@ -149,6 +149,10 @@ typedef struct _U32_S { U32 v; } _PACKED U32_S; #if GCC_VERSION >= 409 __attribute__((__no_sanitize_undefined__)) +#else +# if defined(__clang__) +__attribute__((no_sanitize("undefined"))) +# endif #endif #if defined(_MSC_VER) static __inline U32 A32(const void * x) diff --git a/src/libs/ifwtools/binarycreator.cpp b/src/libs/ifwtools/binarycreator.cpp index 52603cad5..341052650 100644 --- a/src/libs/ifwtools/binarycreator.cpp +++ b/src/libs/ifwtools/binarycreator.cpp @@ -262,7 +262,7 @@ static int assemble(Input input, const QInstaller::Settings &settings, const Bin QFile pkgInfo(fi.filePath() + QLatin1String("/Contents/PkgInfo")); pkgInfo.open(QIODevice::WriteOnly); QTextStream pkgInfoStream(&pkgInfo); - pkgInfoStream << QLatin1String("APPL????") << endl; + pkgInfoStream << QLatin1String("APPL????") << Qt::endl; } QString iconFile; @@ -282,44 +282,44 @@ static int assemble(Input input, const QInstaller::Settings &settings, const Bin QFile infoPList(fi.filePath() + QLatin1String("/Contents/Info.plist")); infoPList.open(QIODevice::WriteOnly); QTextStream plistStream(&infoPList); - plistStream << QLatin1String("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") << endl; + plistStream << QLatin1String("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") << Qt::endl; plistStream << QLatin1String("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " - "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">") << endl; - plistStream << QLatin1String("<plist version=\"1.0\">") << endl; - plistStream << QLatin1String("<dict>") << endl; - plistStream << QLatin1String("\t<key>CFBundleIconFile</key>") << endl; + "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">") << Qt::endl; + plistStream << QLatin1String("<plist version=\"1.0\">") << Qt::endl; + plistStream << QLatin1String("<dict>") << Qt::endl; + plistStream << QLatin1String("\t<key>CFBundleIconFile</key>") << Qt::endl; plistStream << QLatin1String("\t<string>") << iconTargetFile << QLatin1String("</string>") - << endl; - plistStream << QLatin1String("\t<key>CFBundlePackageType</key>") << endl; - plistStream << QLatin1String("\t<string>APPL</string>") << endl; + << Qt::endl; + plistStream << QLatin1String("\t<key>CFBundlePackageType</key>") << Qt::endl; + plistStream << QLatin1String("\t<string>APPL</string>") << Qt::endl; #define QUOTE_(x) #x #define QUOTE(x) QUOTE_(x) - plistStream << QLatin1String("\t<key>CFBundleShortVersionString</key>") << endl; + plistStream << QLatin1String("\t<key>CFBundleShortVersionString</key>") << Qt::endl; plistStream << QLatin1String("\t<string>") << QLatin1String(QUOTE(IFW_VERSION_STR)) << ("</string>") - << endl; - plistStream << QLatin1String("\t<key>CFBundleVersion</key>") << endl; + << Qt::endl; + plistStream << QLatin1String("\t<key>CFBundleVersion</key>") << Qt::endl; plistStream << QLatin1String("\t<string>") << QLatin1String(QUOTE(IFW_VERSION_STR)) << ("</string>") - << endl; + << Qt::endl; #undef QUOTE #undef QUOTE_ - plistStream << QLatin1String("\t<key>CFBundleSignature</key>") << endl; - plistStream << QLatin1String("\t<string>\?\?\?\?</string>") << endl; - plistStream << QLatin1String("\t<key>CFBundleExecutable</key>") << endl; + plistStream << QLatin1String("\t<key>CFBundleSignature</key>") << Qt::endl; + plistStream << QLatin1String("\t<string>\?\?\?\?</string>") << Qt::endl; + plistStream << QLatin1String("\t<key>CFBundleExecutable</key>") << Qt::endl; plistStream << QLatin1String("\t<string>") << fi.completeBaseName() << QLatin1String("</string>") - << endl; - plistStream << QLatin1String("\t<key>CFBundleIdentifier</key>") << endl; - plistStream << QLatin1String("\t<string>com.yourcompany.installerbase</string>") << endl; - plistStream << QLatin1String("\t<key>NOTE</key>") << endl; + << Qt::endl; + plistStream << QLatin1String("\t<key>CFBundleIdentifier</key>") << Qt::endl; + plistStream << QLatin1String("\t<string>com.yourcompany.installerbase</string>") << Qt::endl; + plistStream << QLatin1String("\t<key>NOTE</key>") << Qt::endl; plistStream << QLatin1String("\t<string>This file was generated by Qt Installer Framework.</string>") - << endl; - plistStream << QLatin1String("\t<key>NSPrincipalClass</key>") << endl; - plistStream << QLatin1String("\t<string>NSApplication</string>") << endl; + << Qt::endl; + plistStream << QLatin1String("\t<key>NSPrincipalClass</key>") << Qt::endl; + plistStream << QLatin1String("\t<string>NSApplication</string>") << Qt::endl; if (!minimumSystemVersion.isEmpty()) { - plistStream << QLatin1String("\t<key>LSMinimumSystemVersion</key>") << endl; - plistStream << QLatin1String("\t<string>") << minimumSystemVersion << QLatin1String("</string>") << endl; + plistStream << QLatin1String("\t<key>LSMinimumSystemVersion</key>") << Qt::endl; + plistStream << QLatin1String("\t<string>") << minimumSystemVersion << QLatin1String("</string>") << Qt::endl; } - plistStream << QLatin1String("</dict>") << endl; - plistStream << QLatin1String("</plist>") << endl; + plistStream << QLatin1String("</dict>") << Qt::endl; + plistStream << QLatin1String("</plist>") << Qt::endl; input.outputPath = QString::fromLatin1("%1/Contents/MacOS/%2").arg(input.outputPath) .arg(fi.completeBaseName()); diff --git a/src/libs/ifwtools/repositorygen.cpp b/src/libs/ifwtools/repositorygen.cpp index c54fb846b..9232a02d4 100644 --- a/src/libs/ifwtools/repositorygen.cpp +++ b/src/libs/ifwtools/repositorygen.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -188,7 +188,7 @@ static bool findMetaFile(const QString &repositoryDir, const QDomElement &packag const QDomNodeList c1 = packageUpdate.childNodes(); for (int i = 0; i < c1.count(); ++i) { const QDomElement e1 = c1.at(i).toElement(); - for (const QString &meta : qAsConst(*scMetaElements)) { + for (const QString &meta : scMetaElements) { if (e1.tagName() == meta) { metaElementFound = true; break; @@ -377,7 +377,7 @@ void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &met if (!filePath.endsWith(QLatin1String(".sha1"), Qt::CaseInsensitive)) { const QString fileName = QFileInfo(filePath).fileName(); // remove unnecessary version string from filename and add it to the list - realContentFiles.append(fileName.mid(info.version.count())); + realContentFiles.append(fileName.mid(info.version.size())); } } diff --git a/src/libs/installer/binaryformat.cpp b/src/libs/installer/binaryformat.cpp index 9ed7742db..54af0c310 100644 --- a/src/libs/installer/binaryformat.cpp +++ b/src/libs/installer/binaryformat.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -151,14 +151,21 @@ void Resource::setName(const QByteArray &name) /*! Opens a resource in QIODevice::ReadOnly mode. The function returns \c true - if successful. + if successful. Optionally, \a permissions can be given. */ +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) bool Resource::open() +#else +bool Resource::open(std::optional<QFile::Permissions> permissions) +#endif { if (isOpen()) return false; - +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) if (!m_file.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) { +#else + if (!m_file.open(QIODevice::ReadOnly | QIODevice::Unbuffered, permissions)) { +#endif setErrorString(m_file.errorString()); return false; } diff --git a/src/libs/installer/binaryformat.h b/src/libs/installer/binaryformat.h index 26d510530..e7505a341 100644 --- a/src/libs/installer/binaryformat.h +++ b/src/libs/installer/binaryformat.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -58,7 +58,11 @@ public: Resource(const QString &path, const Range<qint64> &segment); ~Resource(); +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) bool open(); +#else + bool open(std::optional<QFile::Permissions> permissions = std::nullopt); +#endif void close() override; bool seek(qint64 pos) override; diff --git a/src/libs/installer/binaryformatengine.cpp b/src/libs/installer/binaryformatengine.cpp index 681e6db79..7f00c8d47 100644 --- a/src/libs/installer/binaryformatengine.cpp +++ b/src/libs/installer/binaryformatengine.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -127,9 +127,16 @@ bool BinaryFormatEngine::close() /*! \internal */ +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) bool BinaryFormatEngine::open(QIODevice::OpenMode mode) +#else +bool BinaryFormatEngine::open(QIODevice::OpenMode mode, std::optional<QFile::Permissions> permissions) +#endif { Q_UNUSED(mode) +#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0) + Q_UNUSED(permissions) +#endif return m_resource.isNull() ? false : m_resource->open(); } diff --git a/src/libs/installer/binaryformatengine.h b/src/libs/installer/binaryformatengine.h index bf72e5f1f..9321e9d9c 100644 --- a/src/libs/installer/binaryformatengine.h +++ b/src/libs/installer/binaryformatengine.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -47,7 +47,12 @@ public: bool copy(const QString &newName) override; bool close() override; +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) bool open(QIODevice::OpenMode mode) override; +#else + bool open(QIODevice::OpenMode mode, + std::optional<QFile::Permissions> permissions = std::nullopt) override; +#endif qint64 pos() const override; qint64 read(char *data, qint64 maxlen) override; bool seek(qint64 offset) override; diff --git a/src/libs/installer/calculatorbase.h b/src/libs/installer/calculatorbase.h index baf51c5b4..351658f99 100644 --- a/src/libs/installer/calculatorbase.h +++ b/src/libs/installer/calculatorbase.h @@ -49,7 +49,8 @@ public: VirtualDependent, // "No dependencies to virtual component" Dependent, // "Added as dependency for %1." / "Removed as dependency component is removed" Automatic, // "Component(s) added as automatic dependencies" / "Removed as autodependency component is removed" - Resolved // "Component(s) that have resolved Dependencies" + Resolved, // "Component(s) that have resolved Dependencies" + Alias // "Components added from selected alias" }; CalculatorBase(PackageManagerCore *core); diff --git a/src/libs/installer/commandlineparser.cpp b/src/libs/installer/commandlineparser.cpp index a91ab0128..f9e1f663e 100644 --- a/src/libs/installer/commandlineparser.cpp +++ b/src/libs/installer/commandlineparser.cpp @@ -48,7 +48,7 @@ CommandLineParser::CommandLineParser() "headless mode. The installation operations can be invoked with the following commands and " "options. Note that the options marked with \"CLI\" are available in the headless mode only.\n") + QLatin1String("\nCommands:\n") - + indent + QString::fromLatin1("%1, %2 - install default or selected packages - <pkg ...>\n") + + indent + QString::fromLatin1("%1, %2 - install default or selected packages and aliases - <pkg|alias ...>\n") .arg(CommandLineOptions::scInstallShort, CommandLineOptions::scInstallLong) + indent + QString::fromLatin1("%1, %2 - show available updates information on maintenance tool\n") .arg(CommandLineOptions::scCheckUpdatesShort, CommandLineOptions::scCheckUpdatesLong) @@ -56,13 +56,16 @@ CommandLineParser::CommandLineParser() .arg(CommandLineOptions::scUpdateShort, CommandLineOptions::scUpdateLong) + indent + QString::fromLatin1("%1, %2 - uninstall packages and their child components - <pkg ...>\n") .arg(CommandLineOptions::scRemoveShort, CommandLineOptions::scRemoveLong) - + indent + QString::fromLatin1("%1, %2 - list currently installed packages - <regexp>\n") + + indent + QString::fromLatin1("%1, %2 - list currently installed packages - <regexp for pkg>\n") .arg(CommandLineOptions::scListShort, CommandLineOptions::scListLong) - + indent + QString::fromLatin1("%1, %2 - search available packages - <regexp>\n") + + indent + QString::fromLatin1("%1, %2 - search available aliases or packages - <regexp for pkg|alias>\n") .arg(CommandLineOptions::scSearchShort, CommandLineOptions::scSearchLong) + indent + indent + QString::fromLatin1("Note: The --%1 option can be used to specify\n") .arg(CommandLineOptions::scFilterPackagesLong) + indent + indent + QLatin1String("additional filters for the search operation\n") + + indent + indent + QString::fromLatin1("Note: The --%1 option can be used to specify\n") + .arg(CommandLineOptions::scTypeLong) + + indent + indent + QLatin1String("the content type to search\n") + indent + QString::fromLatin1("%1, %2 - create offline installer from selected packages - <pkg ...>\n") .arg(CommandLineOptions::scCreateOfflineShort, CommandLineOptions::scCreateOfflineLong) + indent + QString::fromLatin1("%1, %2 - clear contents of the local metadata cache\n") @@ -177,6 +180,12 @@ CommandLineParser::CommandLineParser() << CommandLineOptions::scLocalCachePathShort << CommandLineOptions::scLocalCachePathLong, QLatin1String("Sets the path used for local metadata cache. The path must be writable by the current user."), QLatin1String("path"))); + addOption(QCommandLineOption(QStringList() + << CommandLineOptions::scTypeLong, + QLatin1String("[CLI] Sets the type of the given arguments for commands supporting multiple argument types, " + "like \"search\". By default aliases are searched first, and if no matching aliases are found, " + "then packages are searched with the same search pattern."), + QLatin1String("package|alias"))); // Message query options addOptionWithContext(QCommandLineOption(QStringList() << CommandLineOptions::scAcceptMessageQueryShort diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp index edd03a191..ce76a2927 100644 --- a/src/libs/installer/component.cpp +++ b/src/libs/installer/component.cpp @@ -46,6 +46,9 @@ #include <QtCore/QDirIterator> #include <QtCore/QTranslator> #include <QtCore/QRegularExpression> +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include <QtCore/QTextCodec> +#endif #include <QApplication> #include <QtConcurrentFilter> @@ -61,6 +64,11 @@ #include <private/qv4object_p.h> #include <algorithm> +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) +#include <QJSEngine> +#else +#include <QQmlEngine> +#endif using namespace QInstaller; @@ -244,6 +252,11 @@ Component::Component(PackageManagerCore *core) : d(new ComponentPrivate(core, this)) , m_defaultArchivePath(scTargetDirPlaceholder) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + QJSEngine::setObjectOwnership(this, QJSEngine::CppOwnership); +#else + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); +#endif setPrivate(d); connect(this, &Component::valueChanged, this, &Component::updateModelData); @@ -667,11 +680,11 @@ void Component::loadTranslations(const QDir &directory, const QStringList &qms) continue; // do not load the file if it does not match the UI language } - QScopedPointer<QTranslator> translator(new QTranslator(this)); + std::unique_ptr<QTranslator> translator(new QTranslator(this)); if (translator->load(filename)) { // Do not throw if translator returns false as it may just be an intentionally // empty file. See also QTBUG-31031 - qApp->installTranslator(translator.take()); + qApp->installTranslator(translator.release()); } } } @@ -693,13 +706,13 @@ void Component::loadUserInterfaces(const QDir &directory, const QStringList &uis it.fileName(), file.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath())); } - static QUiLoader loader; - loader.setTranslationEnabled(true); - loader.setLanguageChangeEnabled(true); - QWidget *const widget = loader.load(&file, 0); + QUiLoader *const loader = ProductKeyCheck::instance()->uiLoader(); + loader->setTranslationEnabled(true); + loader->setLanguageChangeEnabled(true); + QWidget *const widget = loader->load(&file, 0); if (!widget) { throw Error(tr("Cannot load the requested UI file \"%1\": %2.\n\n%3 \"%4\"").arg( - it.fileName(), loader.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath())); + it.fileName(), loader->errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath())); } d->scriptEngine()->newQObject(widget); d->m_userInterfaces.insert(widget->objectName(), widget); @@ -748,7 +761,9 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar file.fileName(), file.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath())); } QTextStream stream(&file); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) stream.setCodec("UTF-8"); +#endif license.insert(scContent, stream.readAll()); d->m_licenses.insert(it.key(), license); } @@ -960,6 +975,12 @@ QStringList Component::archives() const static const QRegularExpression regExp(scCaretSymbol); QString pathString = scInstallerPrefixWithOneArgs.arg(name()); QStringList archivesNameList = QDir(pathString).entryList(); + + // In resources we may have older version of archives, this can happen + // when there is offline installer with same component with lower version + // number and newer version is available online + archivesNameList = archivesNameList.filter(value(scVersion)); + //RegExp "^" means line beginning archivesNameList.replaceInStrings(regExp, pathString); return archivesNameList; @@ -1089,7 +1110,7 @@ OperationList Component::operations(const Operation::OperationGroups &mask) cons licenses.insert(values.at(i).value(scFile).toString(), values.at(i).value(scContent)); } - d->m_licenseOperation->setValue(scLicenses, licenses); + d->m_licenseOperation->setValue(scLicensesValue, licenses); d->m_operations.append(d->m_licenseOperation); } } @@ -1174,9 +1195,6 @@ Operation *Component::createOperation(const QString &operationName, const QStrin return operation; } - if (operation->name() == scDelete) - operation->setValue(scPerformUndo, false); - // Operation can contain variables which are resolved when performing the operation if (operation->requiresUnreplacedVariables()) operation->setArguments(parameters); @@ -1239,7 +1257,7 @@ inline bool convert(QQmlV4Function *func, QStringList *toArgs) QV4::Object *array = val->as<QV4::Object>(); uint length = array->getLength(); for (uint ii = 0; ii < length; ++ii) { - valtmp = array->getIndexed(ii); + valtmp = array->get(ii); *toArgs << valtmp->toQStringNoThrow(); } } else { diff --git a/src/libs/installer/component_p.cpp b/src/libs/installer/component_p.cpp index 7cf47c925..bf3941274 100644 --- a/src/libs/installer/component_p.cpp +++ b/src/libs/installer/component_p.cpp @@ -188,6 +188,7 @@ void ComponentModelHelper::setCheckable(bool checkable) setData(Qt::Unchecked, Qt::CheckStateRole); } changeFlags(checkable, Qt::ItemIsUserCheckable); + m_componentPrivate->m_vars[scCheckable] = checkable ? scTrue : scFalse; } /*! diff --git a/src/libs/installer/componentalias.cpp b/src/libs/installer/componentalias.cpp new file mode 100644 index 000000000..311ca4475 --- /dev/null +++ b/src/libs/installer/componentalias.cpp @@ -0,0 +1,672 @@ +/************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "componentalias.h" + +#include "constants.h" +#include "globals.h" +#include "packagemanagercore.h" +#include "updater.h" + +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +namespace QInstaller { + +static const QStringList scPossibleElements { + scName, + scDisplayName, + scDescription, + scVersion, + scVirtual, + scRequiredComponents, + scRequiredAliases, + scOptionalComponents, + scOptionalAliases, + scReleaseDate +}; + +/*! + \inmodule QtInstallerFramework + \class QInstaller::AliasSource + \brief Describes a source for alias declarations. +*/ + +/*! + \enum QInstaller::AliasSource::SourceFileFormat + + This enum type holds the possible file formats for alias source: + + \value Unknown + Invalid or unknown file format. + \value Xml + XML file format. + \value Json + JSON file format. +*/ + +/*! + Constructs an alias source with empty information. +*/ +AliasSource::AliasSource() + : priority(-1) +{} + +/*! + Constructs an alias source with source file format \a aFormat, filename \a aFilename, and priority \a aPriority. +*/ +AliasSource::AliasSource(SourceFileFormat aFormat, const QString &aFilename, int aPriority) + : format(aFormat) + , filename(aFilename) + , priority(aPriority) +{} + +/*! + Copy-constructs an alias source from \a other. +*/ +AliasSource::AliasSource(const AliasSource &other) +{ + format = other.format; + filename = other.filename; + priority = other.priority; +} + +/*! + Returns the hash value for the \a key, using \a seed to seed the calculation. +*/ +hashValue qHash(const AliasSource &key, hashValue seed) +{ + return qHash(key.filename, seed) ^ key.priority; +} + +/*! + Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false. +*/ +bool operator==(const AliasSource &lhs, const AliasSource &rhs) +{ + return lhs.filename == rhs.filename && lhs.priority == rhs.priority && lhs.format == rhs.format; +} + +/*! + \inmodule QtInstallerFramework + \class QInstaller::AliasFinder + \brief Creates component alias objects from parsed alias source files, based + on version and source priorities. +*/ + +/*! + Constructs a new alias finder with \a core as the package manager instance. +*/ +AliasFinder::AliasFinder(PackageManagerCore *core) + : m_core(core) +{ +} + +/*! + Destroys the finder and cleans unreleased results. +*/ +AliasFinder::~AliasFinder() +{ + clear(); +} + +/*! + Runs the finder. Parses the alias source files and creates component alias + objects based on the parsed data. Same alias may be declared in multiple source + files, thus source priority and version information is used to decide which + source is used for creating the alias object. + + Any previous results are cleared when calling this. + + Returns \c true if at least one alias was found, \c false otherwise. +*/ +bool AliasFinder::run() +{ + clear(); + + if (m_sources.isEmpty()) + return false; + + // 1. Parse source files + for (auto &source : qAsConst(m_sources)) { + if (source.format == AliasSource::SourceFileFormat::Unknown) { + qCWarning(QInstaller::lcInstallerInstallLog) + << "Unknown alias source format for file:" << source.filename; + continue; + } + if (source.format == AliasSource::SourceFileFormat::Xml) + parseXml(source); + else if (source.format == AliasSource::SourceFileFormat::Json) + parseJson(source); + } + + // 2. Create aliases based on priority & version + for (auto &data : qAsConst(m_aliasData)) { + const QString name = data.value(scName).toString(); + const Resolution resolution = checkPriorityAndVersion(data); + if (resolution == Resolution::KeepExisting) + continue; + + if (resolution == Resolution::RemoveExisting) + delete m_aliases.take(name); + + ComponentAlias *alias = new ComponentAlias(m_core); + AliasData::const_iterator it; + for (it = data.cbegin(); it != data.cend(); ++it) { + if (it.value().canConvert<QString>()) + alias->setValue(it.key(), it.value().toString()); + } + m_aliases.insert(name, alias); + } + + return !m_aliases.isEmpty(); +} + +/*! + Returns a list of the found aliases. +*/ +QList<ComponentAlias *> AliasFinder::aliases() const +{ + return m_aliases.values(); +} + +/*! + Sets the alias sources to look alias information from to \a sources. +*/ +void AliasFinder::setAliasSources(const QSet<AliasSource> &sources) +{ + clear(); + m_sources = sources; +} + +/*! + Clears the results of the finder. +*/ +void AliasFinder::clear() +{ + qDeleteAll(m_aliases); + + m_aliases.clear(); + m_aliasData.clear(); +} + +/*! + Reads an XML file specified by \a filename, and constructs a variant map of + the data for each alias. + + Returns \c true on success, \c false otherwise. +*/ +bool AliasFinder::parseXml(AliasSource source) +{ + QFile file(source.filename); + if (!file.open(QIODevice::ReadOnly)) { + qCWarning(QInstaller::lcInstallerInstallLog) + << "Cannot open alias definition for reading:" << file.errorString(); + return false; + } + + QString error; + int errorLine; + int errorColumn; + + QDomDocument doc; + if (!doc.setContent(&file, &error, &errorLine, &errorColumn)) { + qCWarning(QInstaller::lcInstallerInstallLog) + << "Cannot read alias definition document:" << error + << "line:" << errorLine << "column:" << errorColumn; + return false; + } + file.close(); + + const QDomElement root = doc.documentElement(); + const QDomNodeList children = root.childNodes(); + + for (int i = 0; i < children.count(); ++i) { + const QDomElement el = children.at(i).toElement(); + const QString tag = el.tagName(); + if (el.isNull() || tag != scAlias) { + qCWarning(lcInstallerInstallLog) << "Unexpected element name:" << tag; + continue; + } + + AliasData data; + data.insert(QLatin1String("source"), QVariant::fromValue(source)); + + const QDomNodeList c2 = el.childNodes(); + for (int j = 0; j < c2.count(); ++j) { + const QDomElement el2 = c2.at(j).toElement(); + const QString tag2 = el2.tagName(); + if (!scPossibleElements.contains(tag2)) { + qCWarning(lcInstallerInstallLog) << "Unexpected element name:" << tag2; + continue; + } + data.insert(tag2, el2.text()); + } + + m_aliasData.insert(data.value(scName).toString(), data); + } + + return true; +} + +/*! + Reads a JSON file specified by \a source, and constructs a variant map of + the data for each alias. + + Returns \c true on success, \c false otherwise. +*/ +bool AliasFinder::parseJson(AliasSource source) +{ + QFile file(source.filename); + if (!file.open(QIODevice::ReadOnly)) { + qCWarning(QInstaller::lcInstallerInstallLog) + << "Cannot open alias definition for reading:" << file.errorString(); + return false; + } + + const QByteArray jsonData = file.readAll(); + const QJsonDocument doc(QJsonDocument::fromJson(jsonData)); + const QJsonObject docJsonObject = doc.object(); + + const QJsonArray aliases = docJsonObject.value(QLatin1String("alias-packages")).toArray(); + for (auto &it : aliases) { + AliasData data; + data.insert(QLatin1String("source"), QVariant::fromValue(source)); + + QJsonObject aliasObj = it.toObject(); + for (const auto &key : aliasObj.keys()) { + if (!scPossibleElements.contains(key)) { + qCWarning(lcInstallerInstallLog) << "Unexpected element name:" << key; + continue; + } + + const QJsonValue jsonValue = aliasObj.value(key); + if (key == scRequiredComponents || key == scRequiredAliases + || key == scOptionalComponents || key == scOptionalAliases) { + const QJsonArray requirements = jsonValue.toArray(); + QString requiresString; + + for (const auto &it2 : requirements) { + requiresString.append(it2.toString()); + if (it2 != requirements.last()) + requiresString.append(QLatin1Char(',')); + } + + data.insert(key, requiresString); + } else if (key == scVirtual) { + data.insert(key, QVariant(jsonValue.toBool()))->toString(); + } else { + data.insert(key, jsonValue.toString()); + } + } + + m_aliasData.insert(data.value(scName).toString(), data); + } + + return true; +} + +/*! + Checks whether \a data should be used for creating a new alias object, + based on version and source priority. + + If an alias of the same name exists, always use the one with the higher + version. If the new alias has the same version but a higher + priority, use the new new alias. Otherwise keep the already existing alias. + + Returns the resolution of the check. +*/ +AliasFinder::Resolution AliasFinder::checkPriorityAndVersion(const AliasData &data) const +{ + for (const auto &existingData : m_aliasData.values(data.value(scName).toString())) { + if (existingData == data) + continue; + + const int versionMatch = KDUpdater::compareVersion(data.value(scVersion).toString(), + existingData.value(scVersion).toString()); + + const AliasSource newSource = data.value(QLatin1String("source")).value<AliasSource>(); + const AliasSource oldSource = existingData.value(QLatin1String("source")).value<AliasSource>(); + + if (versionMatch > 0) { + // new alias has higher version, use + qCDebug(QInstaller::lcDeveloperBuild).nospace() << "Remove Alias 'Name: " + << data.value(scName).toString() << ", Version: " << existingData.value(scVersion).toString() + << ", Source: " << oldSource.filename + << "' found an alias with higher version 'Name: " + << data.value(scName).toString() << ", Version: " << data.value(scVersion).toString() + << ", Source: " << newSource.filename << "'"; + + return Resolution::RemoveExisting; + } + + if ((versionMatch == 0) && (newSource.priority > oldSource.priority)) { + // new alias version equals but priority is higher, use + qCDebug(QInstaller::lcDeveloperBuild).nospace() << "Remove Alias 'Name: " + << data.value(scName).toString() << ", Priority: " << oldSource.priority + << ", Source: " << oldSource.filename + << "' found an alias with higher priority 'Name: " + << data.value(scName).toString() << ", Priority: " << newSource.priority + << ", Source: " << newSource.filename << "'"; + + return Resolution::RemoveExisting; + } + + return Resolution::KeepExisting; // otherwise keep existing + } + + return Resolution::AddNew; +} + +/*! + \inmodule QtInstallerFramework + \class QInstaller::ComponentAlias + \brief The ComponentAlias class represents an alias for single or multiple components. +*/ + +/*! + \enum QInstaller::ComponentAlias::UnstableError + + This enum type holds the possible reasons for marking an alias unstable: + + \value ReferenceToUnstable + Alias requires another alias that is marked unstable. + \value MissingComponent + Alias requires a component that is missing. + \value UnselectableComponent + Alias requires a component that cannot be selected. + \value MissingAlias + Alias requires another alias that is missing. + \value ComponentNameConfict + Alias has a name that conflicts with a name of a component +*/ + +/*! + Constructs a new component alias with \a core as the package manager instance. +*/ +ComponentAlias::ComponentAlias(PackageManagerCore *core) + : m_core(core) + , m_selected(false) + , m_unstable(false) + , m_missingOptionalComponents (false) +{ +} + +/*! + Destructs the alias. +*/ +ComponentAlias::~ComponentAlias() +{ +} + +/*! + Returns the name of the alias. +*/ +QString ComponentAlias::name() const +{ + return m_variables.value(scName); +} + +/*! + Returns the display name of the alias. +*/ +QString ComponentAlias::displayName() const +{ + return m_variables.value(scDisplayName); +} + +/*! + Returns the description text of the alias. +*/ +QString ComponentAlias::description() const +{ + return m_variables.value(scDescription); +} + +/*! + Returns the version of the alias. +*/ +QString ComponentAlias::version() const +{ + return m_variables.value(scVersion); +} + +/*! + Returns \c true if the alias is virtual, \c false otherwise. + + Virtual aliases are aliases that cannot be selected by the + user, and are invisible. They can be required by other aliases however. +*/ +bool ComponentAlias::isVirtual() const +{ + return m_variables.value(scVirtual, scFalse).toLower() == scTrue; +} + +/*! + Returns \c true if the alias is selected for installation, \c false otherwise. +*/ +bool ComponentAlias::isSelected() const +{ + return m_selected; +} + +/*! + Sets the selection state of the alias to \a selected. The selection + does not have an effect if the alias is unselectable. +*/ +void ComponentAlias::setSelected(bool selected) +{ + if (selected && (isUnstable() || isVirtual())) + return; + + m_selected = selected; +} + +/*! + Returns the list of components required by this alias, or an + empty list if this alias does not require any components. +*/ +QList<Component *> ComponentAlias::components() +{ + if (m_components.isEmpty()) { + m_componentErrorMessages.clear(); + m_missingOptionalComponents = false; + const QStringList componentList = QInstaller::splitStringWithComma( + m_variables.value(scRequiredComponents)); + + const QStringList optionalComponentList = QInstaller::splitStringWithComma( + m_variables.value(scOptionalComponents)); + + addRequiredComponents(componentList, false); + addRequiredComponents(optionalComponentList, true); + } + + return m_components; +} + +/*! + Returns the list of other aliases required by this alias, or an + empty list if this alias does not require any other aliases. +*/ +QList<ComponentAlias *> ComponentAlias::aliases() +{ + if (m_aliases.isEmpty()) { + const QStringList aliasList = QInstaller::splitStringWithComma( + m_variables.value(scRequiredAliases)); + + const QStringList optionalAliasList = QInstaller::splitStringWithComma( + m_variables.value(scOptionalAliases)); + + addRequiredAliases(aliasList, false); + addRequiredAliases(optionalAliasList, true); + } + + return m_aliases; +} + +/*! + Returns the value specified by \a key, with an optional default value \a defaultValue. +*/ +QString ComponentAlias::value(const QString &key, const QString &defaultValue) const +{ + return m_variables.value(key, defaultValue); +} + +/*! + Sets the value specified by \a key to \a value. If the value exists already, + it is replaced with the new value. +*/ +void ComponentAlias::setValue(const QString &key, const QString &value) +{ + const QString normalizedValue = m_core->replaceVariables(value); + if (m_variables.value(key) == normalizedValue) + return; + + m_variables[key] = normalizedValue; +} + +/*! + Returns all keys for the component alias values. +*/ +QStringList ComponentAlias::keys() const +{ + return m_variables.keys(); +} + +/*! + Returns \c true if the alias is marked unstable, \c false otherwise. +*/ +bool ComponentAlias::isUnstable() const +{ + return m_unstable; +} + +/*! + Sets the alias unstable with \a error, and a \a message describing the error. +*/ +void ComponentAlias::setUnstable(UnstableError error, const QString &message) +{ + setSelected(false); + m_unstable = true; + + const QMetaEnum metaEnum = QMetaEnum::fromType<ComponentAlias::UnstableError>(); + emit m_core->unstableComponentFound( + QLatin1String(metaEnum.valueToKey(error)), message, name()); +} + +QString ComponentAlias::componentErrorMessage() const +{ + return m_componentErrorMessages; +} + +bool ComponentAlias::missingOptionalComponents() const +{ + return m_missingOptionalComponents; +} + +/*! + \internal + + Adds the \a aliases to the list of required aliases by this alias. If \a optional + is \c true, missing alias references are ignored. +*/ +void ComponentAlias::addRequiredAliases(const QStringList &aliases, const bool optional) +{ + for (const auto &aliasName : aliases) { + ComponentAlias *alias = m_core->aliasByName(aliasName); + if (!alias) { + if (optional) + continue; + + const QString error = QLatin1String("No required alias found by name: ") + aliasName; + qCWarning(lcInstallerInstallLog) << error; + + setUnstable(UnstableError::MissingAlias, error); + continue; + } + + if (alias->isUnstable()) { + if (optional) + continue; + const QString error = QLatin1String("Alias requires another alias " + "that is marked unstable: ") + aliasName; + qCWarning(lcInstallerInstallLog) << error; + + setUnstable(UnstableError::ReferenceToUnstable, error); + continue; + } + + m_aliases.append(alias); + } +} + +/*! + \internal + + Adds the \a components to the list of required components by this alias. If \a optional + is \c true, missing component references are ignored. +*/ +void ComponentAlias::addRequiredComponents(const QStringList &components, const bool optional) +{ + for (const auto &componentName : components) { + Component *component = m_core->componentByName(componentName); + if (!component) { + if (optional) { + m_missingOptionalComponents = true; + continue; + } + + const QString error = QLatin1String("No required component found by name: ") + + componentName; + if (!m_componentErrorMessages.isEmpty()) + m_componentErrorMessages.append(QLatin1String("\n")); + m_componentErrorMessages.append(error); + + setUnstable(UnstableError::MissingComponent, error); + continue; + } + + if (component->isUnstable() || !component->isCheckable()) { + if (optional) + continue; + const QString error = QLatin1String("Alias requires component that is uncheckable or unstable: ") + + componentName; + if (!m_componentErrorMessages.isEmpty()) + m_componentErrorMessages.append(QLatin1String("\n")); + m_componentErrorMessages.append(error); + + setUnstable(UnstableError::UnselectableComponent, error); + continue; + } + + m_components.append(component); + } +} + +} // namespace QInstaller diff --git a/src/libs/installer/componentalias.h b/src/libs/installer/componentalias.h new file mode 100644 index 000000000..3083260cd --- /dev/null +++ b/src/libs/installer/componentalias.h @@ -0,0 +1,164 @@ +/************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef COMPONENTALIAS_H +#define COMPONENTALIAS_H + +#include "installer_global.h" + +#include <QHash> +#include <QMetaEnum> +#include <QString> +#include <QSet> + +namespace QInstaller { + +class Component; +class ComponentAlias; +class PackageManagerCore; + +struct INSTALLER_EXPORT AliasSource +{ + enum class SourceFileFormat { + Unknown = -1, + Xml = 0, + Json + }; + + AliasSource(); + AliasSource(SourceFileFormat aFormat, const QString &aFilename, int aPriority); + AliasSource(const AliasSource &other); + + SourceFileFormat format; + QString filename; + int priority; +}; + +INSTALLER_EXPORT hashValue qHash(const AliasSource &key, hashValue seed); +INSTALLER_EXPORT bool operator==(const AliasSource &lhs, const AliasSource &rhs); + +class INSTALLER_EXPORT AliasFinder +{ +public: + using AliasData = QVariantMap; + using AliasDataHash = QMultiHash<QString, AliasData>; + + enum struct Resolution { + AddNew, + KeepExisting, + RemoveExisting + }; + + explicit AliasFinder(PackageManagerCore *core); + ~AliasFinder(); + + bool run(); + QList<ComponentAlias *> aliases() const; + + void setAliasSources(const QSet<AliasSource> &sources); + +private: + void clear(); + Resolution checkPriorityAndVersion(const AliasData &data) const; + + bool parseXml(AliasSource source); + bool parseJson(AliasSource source); + +private: + PackageManagerCore *const m_core; + + QSet<AliasSource> m_sources; + AliasDataHash m_aliasData; + QHash<QString, ComponentAlias *> m_aliases; +}; + +class INSTALLER_EXPORT ComponentAlias : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(ComponentAlias) + +public: + enum UnstableError { + ReferenceToUnstable = 0, + MissingComponent, + UnselectableComponent, + MissingAlias, + ComponentNameConfict + }; + Q_ENUM(UnstableError) + + ComponentAlias(PackageManagerCore *core); + ~ComponentAlias(); + + QString name() const; + QString displayName() const; + QString description() const; + + QString version() const; + + bool isVirtual() const; + + bool isSelected() const; + void setSelected(bool selected); + + QList<Component *> components(); + QList<ComponentAlias *> aliases(); + + QString value(const QString &key, const QString &defaultValue = QString()) const; + void setValue(const QString &key, const QString &value); + QStringList keys() const; + + bool isUnstable() const; + void setUnstable(UnstableError error, const QString &message = QString()); + QString componentErrorMessage() const; + bool missingOptionalComponents() const; + +private: + void addRequiredAliases(const QStringList &aliases, const bool optional); + void addRequiredComponents(const QStringList &components, const bool optional); + +private: + PackageManagerCore *const m_core; + + QHash<QString, QString> m_variables; + + bool m_selected; + bool m_unstable; + + QList<Component *> m_components; + bool m_missingOptionalComponents; + QList<ComponentAlias *> m_aliases; + QString m_componentErrorMessages; +}; + +} // namespace QInstaller + +Q_DECLARE_METATYPE(QInstaller::ComponentAlias *) +Q_DECLARE_METATYPE(QInstaller::AliasSource); + +#endif // COMPONENTALIAS_H diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp index dbb80f2f6..1e8dd1ff7 100644 --- a/src/libs/installer/componentmodel.cpp +++ b/src/libs/installer/componentmodel.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -335,6 +335,22 @@ QSet<Component *> ComponentModel::uncheckable() const return m_uncheckable; } +bool ComponentModel::componentsSelected() const +{ + if (m_core->isInstaller() || m_core->isUpdater()) + return checked().count(); + + if (checkedState().testFlag(ComponentModel::DefaultChecked) == false) + return true; + + const QSet<Component *> uncheckables = uncheckable(); + for (auto &component : uncheckables) { + if (component->forcedInstallation() && !component->isInstalled()) + return true; // allow installation for new forced components + } + return false; +} + /*! Returns a pointer to the PackageManagerCore this model belongs to. */ @@ -562,12 +578,11 @@ QSet<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &compone for (int i = sortedNodes.count(); i > 0; i--) { Component * const node = sortedNodes.at(i - 1); - bool checkable = true; - if (node->value(scCheckable, scTrue).toLower() == scFalse) { - checkable = false; - } + if (!node->isEnabled() || node->isUnstable()) + continue; - if ((!node->isCheckable() && checkable) || !node->isEnabled() || node->isUnstable()) + //Do not let forced installations to be uninstalled + if (!m_core->isUpdater() && node->forcedInstallation() && (node->checkState() != Qt::Unchecked)) continue; if (!m_core->isUpdater() && !node->autoDependencies().isEmpty()) diff --git a/src/libs/installer/componentmodel.h b/src/libs/installer/componentmodel.h index 1e8a2d297..c93dd60ae 100644 --- a/src/libs/installer/componentmodel.h +++ b/src/libs/installer/componentmodel.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -80,6 +80,7 @@ public: QSet<Component *> partially() const; QSet<Component *> unchecked() const; QSet<Component *> uncheckable() const; + bool componentsSelected() const; PackageManagerCore *core() const; ComponentModel::ModelState checkedState() const; diff --git a/src/libs/installer/componentselectionpage_p.cpp b/src/libs/installer/componentselectionpage_p.cpp index 7f82ef47d..b68eebf06 100644 --- a/src/libs/installer/componentselectionpage_p.cpp +++ b/src/libs/installer/componentselectionpage_p.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2023 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -35,6 +35,7 @@ #include "component.h" #include "fileutils.h" #include "messageboxhandler.h" +#include "customcombobox.h" #include <QTreeView> #include <QLabel> @@ -50,7 +51,6 @@ #include <QStackedLayout> #include <QStackedWidget> #include <QLineEdit> -#include <QComboBox> #include <QStandardItemModel> #include <QStyledItemDelegate> @@ -71,22 +71,20 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP : q(qq) , m_core(core) , m_treeView(new QTreeView(q)) - , m_allModel(m_core->defaultComponentModel()) - , m_updaterModel(m_core->updaterComponentModel()) - , m_currentModel(m_allModel) - , m_allowCompressedRepositoryInstall(false) , m_tabWidget(nullptr) , m_descriptionBaseWidget(nullptr) , m_categoryWidget(Q_NULLPTR) + , m_allowCreateOfflineInstaller(false) , m_categoryLayoutVisible(false) - , m_proxyModel(new ComponentSortFilterProxyModel(q)) + , m_allModel(m_core->defaultComponentModel()) + , m_updaterModel(m_core->updaterComponentModel()) + , m_currentModel(m_allModel) + , m_proxyModel(m_core->componentSortFilterProxyModel()) , m_componentsResolved(false) , m_headerStretchLastSection(false) { m_treeView->setObjectName(QLatin1String("ComponentsTreeView")); m_treeView->setUniformRowHeights(true); - m_proxyModel->setRecursiveFilteringEnabled(true); - m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_descriptionBaseWidget = new QWidget(q); m_descriptionBaseWidget->setObjectName(QLatin1String("DescriptionBaseWidget")); @@ -121,10 +119,21 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP m_sizeLabel->setObjectName(QLatin1String("ComponentSizeLabel")); descriptionVLayout->addWidget(m_sizeLabel); + m_createOfflinePushButton = new QPushButton(q); + m_createOfflinePushButton->setVisible(false); + m_createOfflinePushButton->setText(ComponentSelectionPage::tr("Create Offline Installer")); + m_createOfflinePushButton->setToolTip( + ComponentSelectionPage::tr("Create offline installer from selected components, instead " + "of installing now.")); + + connect(m_createOfflinePushButton, &QPushButton::clicked, + this, &ComponentSelectionPagePrivate::createOfflineButtonClicked); + connect(q, &ComponentSelectionPage::completeChanged, + this, [&]() { m_createOfflinePushButton->setEnabled(q->isComplete()); }); + m_qbspPushButton = new QPushButton(q); m_qbspPushButton->setVisible(false); m_qbspPushButton->setText(ComponentSelectionPage::tr("Browse &QBSP files")); - m_qbspPushButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_qbspPushButton->setToolTip( ComponentSelectionPage::tr("Select a Qt Board Support Package file to install " "additional content that is not directly available from the online repositories.")); @@ -133,18 +142,28 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP this, &ComponentSelectionPagePrivate::qbspButtonClicked); m_rightSideVLayout->addWidget(m_descriptionBaseWidget); - m_rightSideVLayout->addWidget(m_qbspPushButton, 0, Qt::AlignRight | Qt::AlignBottom); + m_rightSideVLayout->addWidget(m_createOfflinePushButton); + m_rightSideVLayout->addWidget(m_qbspPushButton); QHBoxLayout *topHLayout = new QHBoxLayout; - m_checkStateComboBox = new QComboBox(q); + + // Using custom combobox to workaround QTBUG-90595 + m_checkStateComboBox = new CustomComboBox(q); +#ifdef Q_OS_MACOS QStyledItemDelegate *delegate = new QStyledItemDelegate(this); m_checkStateComboBox->setItemDelegate(delegate); +#endif m_checkStateComboBox->setObjectName(QLatin1String("CheckStateComboBox")); topHLayout->addWidget(m_checkStateComboBox); connect(m_checkStateComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ComponentSelectionPagePrivate::updateAllCheckStates); + // Workaround invisible placeholder text + QPalette palette = m_checkStateComboBox->palette(); + palette.setColor(QPalette::PlaceholderText, palette.color(QPalette::Text)); + m_checkStateComboBox->setPalette(palette); + m_checkStateComboBox->setPlaceholderText(ComponentSelectionPage::tr("Select")); if (m_core->isInstaller()) { m_checkStateComboBox->insertItem(scCheckDefaultIndex, ComponentSelectionPage::tr("Default")); @@ -215,10 +234,6 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP connect(m_core, SIGNAL(metaJobInfoMessage(QString)), this, SLOT(setMessage(QString))); connect(m_core, &PackageManagerCore::metaJobTotalProgress, this, &ComponentSelectionPagePrivate::setTotalProgress); - -#ifdef INSTALLCOMPRESSED - allowCompressedRepositoryInstall(); -#endif } ComponentSelectionPagePrivate::~ComponentSelectionPagePrivate() @@ -226,14 +241,14 @@ ComponentSelectionPagePrivate::~ComponentSelectionPagePrivate() } -void ComponentSelectionPagePrivate::allowCompressedRepositoryInstall() +void ComponentSelectionPagePrivate::setAllowCreateOfflineInstaller(bool allow) { - m_allowCompressedRepositoryInstall = true; + m_allowCreateOfflineInstaller = allow; } void ComponentSelectionPagePrivate::showCompressedRepositoryButton() { - if (m_allowCompressedRepositoryInstall) + if (m_core->allowCompressedRepositoryInstall()) m_qbspPushButton->setVisible(true); } @@ -242,6 +257,14 @@ void ComponentSelectionPagePrivate::hideCompressedRepositoryButton() m_qbspPushButton->setVisible(false); } +void ComponentSelectionPagePrivate::showCreateOfflineInstallerButton(bool show) +{ + if (show && m_allowCreateOfflineInstaller) + m_createOfflinePushButton->setVisible(m_core->isInstaller() && !m_core->isOfflineOnly()); + else + m_createOfflinePushButton->setVisible(false); +} + void ComponentSelectionPagePrivate::setupCategoryLayout() { if (m_categoryWidget) @@ -288,11 +311,11 @@ void ComponentSelectionPagePrivate::showCategoryLayout(bool show) if (show) { m_rightSideVLayout->removeWidget(m_descriptionBaseWidget); m_tabWidget->insertTab(0, m_descriptionBaseWidget, tr("Information")); - m_rightSideVLayout->insertWidget(m_rightSideVLayout->count() - 1, m_tabWidget); + m_rightSideVLayout->insertWidget(0, m_tabWidget); } else { m_tabWidget->removeTab(0); m_rightSideVLayout->removeWidget(m_tabWidget); - m_rightSideVLayout->insertWidget(m_rightSideVLayout->count() - 1, m_descriptionBaseWidget); + m_rightSideVLayout->insertWidget(0, m_descriptionBaseWidget); m_descriptionBaseWidget->setVisible(true); } m_tabWidget->setVisible(show); @@ -463,27 +486,6 @@ void ComponentSelectionPagePrivate::deselectAll() m_currentModel->setCheckedState(ComponentModel::AllUnchecked); } -void ComponentSelectionPagePrivate::enableRepositoryCategory(const QString &repositoryName, bool enable) -{ - QMap<QString, RepositoryCategory> organizedRepositoryCategories = m_core->settings().organizedRepositoryCategories(); - - QMap<QString, RepositoryCategory>::iterator i = organizedRepositoryCategories.find(repositoryName); - RepositoryCategory repoCategory; - while (i != organizedRepositoryCategories.end() && i.key() == repositoryName) { - repoCategory = i.value(); - i++; - } - - RepositoryCategory replacement = repoCategory; - replacement.setEnabled(enable); - QSet<RepositoryCategory> tmpRepoCategories = m_core->settings().repositoryCategories(); - if (tmpRepoCategories.contains(repoCategory)) { - tmpRepoCategories.remove(repoCategory); - tmpRepoCategories.insert(replacement); - m_core->settings().addRepositoryCategories(tmpRepoCategories); - } -} - void ComponentSelectionPagePrivate::updateWidgetVisibility(bool show) { if (show) @@ -511,7 +513,7 @@ void ComponentSelectionPagePrivate::fetchRepositoryCategories() QList<QCheckBox*> checkboxes = m_categoryGroupBox->findChildren<QCheckBox *>(); for (int i = 0; i < checkboxes.count(); i++) { QCheckBox *checkbox = checkboxes.at(i); - enableRepositoryCategory(checkbox->objectName(), checkbox->isChecked()); + m_core->enableRepositoryCategory(checkbox->objectName(), checkbox->isChecked()); } if (!m_core->fetchRemotePackagesTree()) { @@ -522,6 +524,12 @@ void ComponentSelectionPagePrivate::fetchRepositoryCategories() m_searchLineEdit->text().isEmpty() ? expandDefault() : expandSearchResults(); } +void ComponentSelectionPagePrivate::createOfflineButtonClicked() +{ + m_core->setOfflineGenerator(); + q->gui()->button(QWizard::NextButton)->click(); +} + void ComponentSelectionPagePrivate::qbspButtonClicked() { QString defaultDownloadDirectory = @@ -530,15 +538,8 @@ void ComponentSelectionPagePrivate::qbspButtonClicked() ComponentSelectionPage::tr("Open File"),defaultDownloadDirectory, QLatin1String("QBSP or 7z Files (*.qbsp *.7z)")); - QSet<Repository> set; - foreach (QString fileName, fileNames) { - Repository repository = Repository::fromUserInput(fileName, true); - repository.setEnabled(true); - set.insert(repository); - } - if (set.count() > 0) { + if (m_core->addQBspRepositories(fileNames)) { updateWidgetVisibility(true); - m_core->settings().addTemporaryRepositories(set, false); if (!m_core->fetchCompressedPackagesTree()) { MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error()); diff --git a/src/libs/installer/componentselectionpage_p.h b/src/libs/installer/componentselectionpage_p.h index ed68fafa4..187fce61d 100644 --- a/src/libs/installer/componentselectionpage_p.h +++ b/src/libs/installer/componentselectionpage_p.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2023 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -48,13 +48,13 @@ class QVBoxLayout; class QHBoxLayout; class QGridLayout; class QStackedLayout; -class QComboBox; namespace QInstaller { class PackageManagerCore; class ComponentModel; class ComponentSelectionPage; +class CustomComboBox; class ComponentSelectionPagePrivate : public QObject { @@ -66,9 +66,10 @@ public: explicit ComponentSelectionPagePrivate(ComponentSelectionPage *qq, PackageManagerCore *core); ~ComponentSelectionPagePrivate(); - void allowCompressedRepositoryInstall(); + void setAllowCreateOfflineInstaller(bool allow); void showCompressedRepositoryButton(); void hideCompressedRepositoryButton(); + void showCreateOfflineInstallerButton(bool show); void setupCategoryLayout(); void showCategoryLayout(bool show); void updateTreeView(); @@ -81,9 +82,9 @@ public slots: void updateAllCheckStates(int which); void selectAll(); void deselectAll(); - void enableRepositoryCategory(const QString &repositoryName, bool enable); void updateWidgetVisibility(bool show); void fetchRepositoryCategories(); + void createOfflineButtonClicked(); void qbspButtonClicked(); void onProgressChanged(int progress); void setMessage(const QString &msg); @@ -105,15 +106,16 @@ private: QWidget *m_descriptionBaseWidget; QLabel *m_sizeLabel; QLabel *m_descriptionLabel; + QPushButton *m_createOfflinePushButton; QPushButton *m_qbspPushButton; - QComboBox *m_checkStateComboBox; + CustomComboBox *m_checkStateComboBox; QWidget *m_categoryWidget; QGroupBox *m_categoryGroupBox; QLabel *m_metadataProgressLabel; QProgressBar *m_progressBar; QGridLayout *m_mainGLayout; QVBoxLayout *m_rightSideVLayout; - bool m_allowCompressedRepositoryInstall; + bool m_allowCreateOfflineInstaller; bool m_categoryLayoutVisible; ComponentModel *m_allModel; ComponentModel *m_updaterModel; diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h index ecd85fe11..7bf816b5f 100644 --- a/src/libs/installer/constants.h +++ b/src/libs/installer/constants.h @@ -60,6 +60,11 @@ static const QLatin1String scDisplayName("DisplayName"); static const QLatin1String scTreeName("TreeName"); static const QLatin1String scAutoTreeName("AutoTreeName"); static const QLatin1String scDependencies("Dependencies"); +static const QLatin1String scAlias("Alias"); +static const QLatin1String scRequiredAliases("RequiredAliases"); +static const QLatin1String scRequiredComponents("RequiredComponents"); +static const QLatin1String scOptionalAliases("OptionalAliases"); +static const QLatin1String scOptionalComponents("OptionalComponents"); static const QLatin1String scLocalDependencies("LocalDependencies"); static const QLatin1String scAutoDependOn("AutoDependOn"); static const QLatin1String scNewComponent("NewComponent"); @@ -75,7 +80,7 @@ static const QLatin1String scMetadataName("MetadataName"); static const QLatin1String scContentSha1("ContentSha1"); static const QLatin1String scCheckSha1CheckSum("CheckSha1CheckSum"); -static const char *scClearCacheHint = QT_TR_NOOP( +static const char * const scClearCacheHint = QT_TR_NOOP( "This may be solved by restarting the application after clearing the cache from:"); // symbols @@ -102,6 +107,7 @@ static const QLatin1String scInstallDate("InstallDate"); static const QLatin1String scUserInterfaces("UserInterfaces"); static const QLatin1String scTranslations("Translations"); static const QLatin1String scLicenses("Licenses"); +static const QLatin1String scLicensesValue("licenses"); static const QLatin1String scLicense("License"); static const QLatin1String scOperations("Operations"); static const QLatin1String scInstallScript("installScript"); @@ -123,7 +129,6 @@ static const QLatin1String scMinimumProgress("MinimumProgress"); static const QLatin1String scDelete("Delete"); static const QLatin1String scCopy("Copy"); static const QLatin1String scMkdir("Mkdir"); -static const QLatin1String scPerformUndo("performUndo"); static const QLatin1String scIsDefault("isDefault"); static const QLatin1String scAdmin("admin"); static const QLatin1String scTwoArgs("%1/%2/"); @@ -152,6 +157,7 @@ static const QLatin1String scRemoteRepositories("RemoteRepositories"); static const QLatin1String scRepositoryCategories("RepositoryCategories"); static const QLatin1String scRepositorySettingsPageVisible("RepositorySettingsPageVisible"); static const QLatin1String scAllowSpaceInPath("AllowSpaceInPath"); +static const QLatin1String scAllowRepositoriesForOfflineInstaller("AllowRepositoriesForOfflineInstaller"); static const QLatin1String scWizardStyle("WizardStyle"); static const QLatin1String scStyleSheet("StyleSheet"); static const QLatin1String scTitleColor("TitleColor"); @@ -174,7 +180,15 @@ static const QLatin1String scBanner("Banner"); static const QLatin1String scLogo("Logo"); static const QLatin1String scBackground("Background"); static const QLatin1String scPageListPixmap("PageListPixmap"); +static const QLatin1String scAliasDefinitionsFile("AliasDefinitionsFile"); const char scRelocatable[] = "@RELOCATABLE_PATH@"; + +static const QStringList scMetaElements = { + QLatin1String("Script"), + QLatin1String("Licenses"), + QLatin1String("UserInterfaces"), + QLatin1String("Translations") +}; } namespace CommandLineOptions { @@ -271,6 +285,7 @@ static const QLatin1String scFilterPackagesShort("fp"); static const QLatin1String scFilterPackagesLong("filter-packages"); static const QLatin1String scLocalCachePathShort("cp"); static const QLatin1String scLocalCachePathLong("cache-path"); +static const QLatin1String scTypeLong("type"); // Developer options static const QLatin1String scScriptShort("s"); diff --git a/src/libs/installer/copydirectoryoperation.cpp b/src/libs/installer/copydirectoryoperation.cpp index a2ef2cf5a..c0fec0649 100644 --- a/src/libs/installer/copydirectoryoperation.cpp +++ b/src/libs/installer/copydirectoryoperation.cpp @@ -153,7 +153,7 @@ bool CopyDirectoryOperation::performOperation() bool CopyDirectoryOperation::undoOperation() { - if (parseUndoOperationArguments().count() > 0) + if (skipUndoOperation()) return true; if (!checkArgumentCount(2)) diff --git a/src/libs/installer/copyfiletask.cpp b/src/libs/installer/copyfiletask.cpp index 72b28d896..856feda01 100644 --- a/src/libs/installer/copyfiletask.cpp +++ b/src/libs/installer/copyfiletask.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -112,7 +112,7 @@ void CopyFileTask::doTask(QFutureInterface<FileTaskResult> &fi) observer.addSample(read); observer.timerEvent(nullptr); observer.addBytesTransfered(read); - observer.addCheckSumData(buffer.data(), read); + observer.addCheckSumData(buffer.left(read)); fi.setProgressValueAndText(observer.progressValue(), observer.progressText()); } diff --git a/src/libs/installer/createdesktopentryoperation.cpp b/src/libs/installer/createdesktopentryoperation.cpp index c3988a8ec..a19fd773a 100644 --- a/src/libs/installer/createdesktopentryoperation.cpp +++ b/src/libs/installer/createdesktopentryoperation.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -32,6 +32,7 @@ #include "globals.h" #include "adminauthorization.h" #include "remoteclient.h" +#include "packagemanagercore.h" #include <QDir> #include <QFile> @@ -64,7 +65,7 @@ QString CreateDesktopEntryOperation::absoluteFileName() XDG_DATA_HOME.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share"))); // default user-specific path - if (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive()) + if (packageManager() && packageManager()->hasAdminRights()) XDG_DATA_HOME.push_front(QLatin1String("/usr/local/share")); // default system-wide path const QStringList directories = XDG_DATA_HOME; @@ -151,15 +152,16 @@ bool CreateDesktopEntryOperation::performOperation() setDefaultFilePermissions(filename, DefaultFilePermissions::Executable); - QTextStream stream(&file); - stream.setCodec("UTF-8"); - stream << QLatin1String("[Desktop Entry]") << endl; + QString outString; + QTextStream stream(&outString); + stream << QLatin1String("[Desktop Entry]") << Qt::endl; // Type=Application\nExec=qtcreator\nPath=... const QStringList pairs = values.split(QLatin1Char('\n')); for (QStringList::const_iterator it = pairs.begin(); it != pairs.end(); ++it) stream << *it << Qt::endl; + file.write(outString.toUtf8()); return true; } diff --git a/src/libs/installer/createlocalrepositoryoperation.cpp b/src/libs/installer/createlocalrepositoryoperation.cpp index 7090f9a8b..286cc9b5b 100644 --- a/src/libs/installer/createlocalrepositoryoperation.cpp +++ b/src/libs/installer/createlocalrepositoryoperation.cpp @@ -378,7 +378,7 @@ bool CreateLocalRepositoryOperation::performOperation() bool CreateLocalRepositoryOperation::undoOperation() { - if (parseUndoOperationArguments().count() > 0) + if (skipUndoOperation()) return true; if (!checkArgumentCount(2)) diff --git a/src/libs/installer/createshortcutoperation.cpp b/src/libs/installer/createshortcutoperation.cpp index 57f901c2f..894b5843b 100644 --- a/src/libs/installer/createshortcutoperation.cpp +++ b/src/libs/installer/createshortcutoperation.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -110,6 +110,7 @@ static bool createLink(const QString &fileName, const QString &linkName, QString IUnknown *iunkn = nullptr; if (fileName.toLower().startsWith(QLatin1String("http:")) + || fileName.toLower().startsWith(QLatin1String("https:")) || fileName.toLower().startsWith(QLatin1String("ftp:"))) { IUniformResourceLocator *iurl = nullptr; if (FAILED(CoCreateInstance(CLSID_InternetShortcut, nullptr, CLSCTX_INPROC_SERVER, @@ -176,6 +177,7 @@ static bool createLink(const QString &fileName, const QString &linkName, QString Q_UNUSED(linkName) Q_UNUSED(iconPath) Q_UNUSED(iconId) + Q_UNUSED(description) return true; #endif } diff --git a/src/libs/installer/customcombobox.cpp b/src/libs/installer/customcombobox.cpp new file mode 100644 index 000000000..998364fe4 --- /dev/null +++ b/src/libs/installer/customcombobox.cpp @@ -0,0 +1,54 @@ +/************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "customcombobox.h" + +#include <QStylePainter> + +using namespace QInstaller; + +CustomComboBox::CustomComboBox(QWidget *parent) + : QComboBox(parent) +{ +} + +void CustomComboBox::paintEvent(QPaintEvent *e) +{ + if (currentIndex() < 0 && !placeholderText().isEmpty()) { + QStylePainter painter(this); + painter.setPen(palette().color(QPalette::Text)); + QStyleOptionComboBox opt; + initStyleOption(&opt); + painter.drawComplexControl(QStyle::CC_ComboBox, opt); + opt.palette.setBrush(QPalette::ButtonText, opt.palette.placeholderText()); + opt.currentText = placeholderText(); + painter.drawControl(QStyle::CE_ComboBoxLabel, opt); + } else { + QComboBox::paintEvent(e); + } +} diff --git a/src/libs/installer/customcombobox.h b/src/libs/installer/customcombobox.h new file mode 100644 index 000000000..e022da5a8 --- /dev/null +++ b/src/libs/installer/customcombobox.h @@ -0,0 +1,48 @@ +/************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef CUSTOMCOMBOBOX_H +#define CUSTOMCOMBOBOX_H + +#include <QComboBox> + +namespace QInstaller { + +class CustomComboBox : public QComboBox +{ + Q_OBJECT +public: + CustomComboBox(QWidget *parent = nullptr); + +protected: + void paintEvent(QPaintEvent *e) override; +}; + +} // namespace QInstaller + +#endif // CUSTOMCOMBOBOX_H diff --git a/src/libs/installer/downloadarchivesjob.cpp b/src/libs/installer/downloadarchivesjob.cpp index 48700bd12..65eead1f9 100644 --- a/src/libs/installer/downloadarchivesjob.cpp +++ b/src/libs/installer/downloadarchivesjob.cpp @@ -44,10 +44,12 @@ using namespace QInstaller; using namespace KDUpdater; +static constexpr uint scMaxRetries = 5; + /*! Creates a new DownloadArchivesJob with parent \a core. */ -DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core) +DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core, const QString &objectName) : Job(core) , m_core(core) , m_downloader(nullptr) @@ -58,8 +60,10 @@ DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core) , m_progressChangedTimerId(0) , m_totalSizeToDownload(0) , m_totalSizeDownloaded(0) + , m_retryCount(scMaxRetries) { setCapabilities(Cancelable); + setObjectName(objectName); } /*! @@ -289,17 +293,24 @@ void DownloadArchivesJob::registerFile() const QMessageBox::Button res = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("DownloadError"), tr("Download Error"), tr("Hash verification while " - "downloading failed. This is a temporary error, please retry."), - QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Cancel); - - // If run from command line instance, do not continue if hash verification failed. - // Same download is tried again and again causing infinite loop if hash not - // fixed to repositories. - if (res == QMessageBox::Cancel || m_core->isCommandLineInstance()) { - finishWithError(tr("Cannot verify Hash")); + "downloading failed. This is a temporary error, please retry.\n\n" + "Expected: %1 \nDownloaded: %2").arg(QString::fromLatin1(m_currentHash), QString::fromLatin1(m_downloader->sha1Sum().toHex())), + QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Retry); + + if (res == QMessageBox::Cancel) { + finishWithError(tr("Cannot verify Hash\nExpected: %1 \nDownloaded: %2") + .arg(QString::fromLatin1(m_currentHash), QString::fromLatin1(m_downloader->sha1Sum().toHex()))); return; } + // When using command line instance, only retry a number of times to avoid + // infinite loop in case the automatic answer for the messagebox is "Retry" + if (m_core->isCommandLineInstance() && (--m_retryCount == 0)) { + finishWithError(tr("Retry count (%1) exceeded").arg(scMaxRetries)); + return; + } } else { + m_retryCount = scMaxRetries; + ++m_archivesDownloaded; m_totalSizeDownloaded += QFile(m_downloader->downloadedFileName()).size(); if (m_progressChangedTimerId) { @@ -330,14 +341,21 @@ void DownloadArchivesJob::downloadFailed(const QString &error) const QMessageBox::StandardButton b = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("archiveDownloadError"), tr("Download Error"), tr("Cannot download archive %1: %2") - .arg(m_archivesToDownload.first().sourceUrl, error), QMessageBox::Retry | QMessageBox::Cancel); + .arg(m_archivesToDownload.first().sourceUrl, error), QMessageBox::Retry | QMessageBox::Cancel, + QMessageBox::Retry); + + if (b == QMessageBox::Retry) { + // When using command line instance, only retry a number of times to avoid + // infinite loop in case the automatic answer for the messagebox is "Retry" + if (m_core->isCommandLineInstance() && (--m_retryCount == 0)) { + finishWithError(tr("Retry count (%1) exceeded").arg(scMaxRetries)); + return; + } - // Do not call fetchNextArchiveHash when using command line instance, - // installer tries to download the same archive causing infinite loop - if (b == QMessageBox::Retry && !m_core->isCommandLineInstance()) QMetaObject::invokeMethod(this, "fetchNextArchiveHash", Qt::QueuedConnection); - else + } else { downloadCanceled(); + } } void DownloadArchivesJob::finishWithError(const QString &error) diff --git a/src/libs/installer/downloadarchivesjob.h b/src/libs/installer/downloadarchivesjob.h index c156d0244..5155c881a 100644 --- a/src/libs/installer/downloadarchivesjob.h +++ b/src/libs/installer/downloadarchivesjob.h @@ -51,7 +51,7 @@ class DownloadArchivesJob : public Job Q_OBJECT public: - explicit DownloadArchivesJob(PackageManagerCore *core); + explicit DownloadArchivesJob(PackageManagerCore *core, const QString &objectName); ~DownloadArchivesJob(); int numberOfDownloads() const { return m_archivesDownloaded; } @@ -103,6 +103,8 @@ private: quint64 m_totalSizeToDownload; quint64 m_totalSizeDownloaded; QElapsedTimer m_totalDownloadSpeedTimer; + + uint m_retryCount; }; } // namespace QInstaller diff --git a/src/libs/installer/downloadfiletask.cpp b/src/libs/installer/downloadfiletask.cpp index 1b9f81ecc..a959677a9 100644 --- a/src/libs/installer/downloadfiletask.cpp +++ b/src/libs/installer/downloadfiletask.cpp @@ -1,7 +1,7 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -30,6 +30,7 @@ #include "downloadfiletask_p.h" #include "globals.h" +#include "productkeycheck.h" #include <QCoreApplication> #include <QDir> @@ -197,7 +198,7 @@ void Downloader::onReadyRead() data.observer->addSample(read); data.observer->addBytesTransfered(read); - data.observer->addCheckSumData(buffer.data(), read); + data.observer->addCheckSumData(buffer.left(read)); int progress = m_finished * 100; for (const auto &pair : m_downloads) @@ -246,7 +247,7 @@ void Downloader::onFinished(QNetworkReply *reply) if (!ba.isEmpty()) { data.observer->addSample(ba.size()); data.observer->addBytesTransfered(ba.size()); - data.observer->addCheckSumData(ba.data(), ba.size()); + data.observer->addCheckSumData(ba); } const QByteArray expectedCheckSum = data.taskItem.value(TaskRole::Checksum).toByteArray(); @@ -269,7 +270,7 @@ void Downloader::onFinished(QNetworkReply *reply) } } -void Downloader::onError(QNetworkReply::NetworkError error) +void Downloader::errorOccurred(QNetworkReply::NetworkError error) { QNetworkReply *const reply = qobject_cast<QNetworkReply *>(sender()); @@ -286,6 +287,10 @@ void Downloader::onError(QNetworkReply::NetworkError error) if (data.taskItem.source().contains(QLatin1String("Updates.xml"), Qt::CaseInsensitive)) { qCWarning(QInstaller::lcServer) << QString::fromLatin1("Network error while downloading '%1': %2.").arg( data.taskItem.source(), reply->errorString()); + } else if (data.taskItem.source().contains(QLatin1String("_meta"), Qt::CaseInsensitive)) { + QString errorString = tr("Network error while downloading '%1': %2.").arg(data.taskItem.source(), reply->errorString()); + errorString.append(ProductKeyCheck::instance()->additionalMetaDownloadWarning()); + m_futureInterface->reportException(TaskException(errorString)); } else { m_futureInterface->reportException( TaskException(tr("Network error while downloading '%1': %2.").arg( @@ -395,14 +400,17 @@ QNetworkReply *Downloader::startDownload(const FileTaskItem &item) .arg(source.toString(), source.errorString()))); return 0; } + QNetworkRequest request(source); + request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy); + request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false); - QNetworkReply *reply = m_nam.get(QNetworkRequest(source)); + QNetworkReply *reply = m_nam.get(request); std::unique_ptr<Data> data(new Data(item)); m_downloads[reply] = std::move(data); connect(reply, &QIODevice::readyRead, this, &Downloader::onReadyRead); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, - SLOT(onError(QNetworkReply::NetworkError))); + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, + SLOT(errorOccurred(QNetworkReply::NetworkError))); #ifndef QT_NO_SSL connect(reply, &QNetworkReply::sslErrors, this, &Downloader::onSslErrors); #endif diff --git a/src/libs/installer/downloadfiletask_p.h b/src/libs/installer/downloadfiletask_p.h index 3dfce27b4..4750d5134 100644 --- a/src/libs/installer/downloadfiletask_p.h +++ b/src/libs/installer/downloadfiletask_p.h @@ -86,7 +86,7 @@ private slots: void doDownload(); void onReadyRead(); void onFinished(QNetworkReply *reply); - void onError(QNetworkReply::NetworkError error); + void errorOccurred(QNetworkReply::NetworkError error); void onSslErrors(const QList<QSslError> &sslErrors); void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); diff --git a/src/libs/installer/environmentvariablesoperation.cpp b/src/libs/installer/environmentvariablesoperation.cpp index 902687164..94cd1e36f 100644 --- a/src/libs/installer/environmentvariablesoperation.cpp +++ b/src/libs/installer/environmentvariablesoperation.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -30,6 +30,7 @@ #include "qsettingswrapper.h" #include <stdlib.h> +#include <QDir> #include "environment.h" #include "globals.h" @@ -110,6 +111,12 @@ bool handleRegExpandSz(const QString ®Path, const QString &name, } } } +#else + Q_UNUSED(regPath) + Q_UNUSED(name) + Q_UNUSED(value) + Q_UNUSED(errorString) + Q_UNUSED(error) #endif return setAsExpandSZ; } @@ -162,11 +169,11 @@ UpdateOperation::Error undoSetting(const QString ®Path, if (actual != value) { - //For unknown reason paths with @TargetDir@ variable get modified - //so that Windows file separators get replaced with unix style separators, - //fix separators before matching to actual value in register + //Ignore the separators + static const QRegularExpression regex(QLatin1String("(\\\\|/)")); QString tempValue = value; - QString fixedValue = tempValue.replace(QLatin1Char('/'), QLatin1Char('\\')); + QString fixedValue = tempValue.replace(regex, QDir::separator()); + actual = actual.replace(regex, QDir::separator()); if (actual != fixedValue) //key changed, don't undo return UpdateOperation::UserDefinedError; diff --git a/src/libs/installer/extractarchiveoperation.cpp b/src/libs/installer/extractarchiveoperation.cpp index 162bd1609..b00a67190 100644 --- a/src/libs/installer/extractarchiveoperation.cpp +++ b/src/libs/installer/extractarchiveoperation.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -103,7 +103,7 @@ void ExtractArchiveOperation::backup() return; } - const bool hasAdminRights = (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive()); + const bool hasAdminRights = (packageManager() && packageManager()->hasAdminRights()); const bool canCreateSymLinks = QInstaller::canCreateSymbolicLinks(); bool needsAdminRights = false; @@ -215,7 +215,8 @@ bool ExtractArchiveOperation::performOperation() } files[i] = replacePath(files.at(i), installDir, QLatin1String(scRelocatable)); } - out << files; + if (!files.isEmpty()) + out << files; setValue(QLatin1String("files"), file.fileName()); file.close(); } else { @@ -253,7 +254,8 @@ bool ExtractArchiveOperation::undoOperation() if (!readDataFileContents(targetDir, &files)) return false; } - startUndoProcess(files); + if (!files.isEmpty()) + startUndoProcess(files); if (!useStringListType) deleteDataFile(m_relocatedDataFileName); diff --git a/src/libs/installer/fakestopprocessforupdateoperation.cpp b/src/libs/installer/fakestopprocessforupdateoperation.cpp index 67d60a92f..bdd8625eb 100644 --- a/src/libs/installer/fakestopprocessforupdateoperation.cpp +++ b/src/libs/installer/fakestopprocessforupdateoperation.cpp @@ -31,6 +31,8 @@ #include "messageboxhandler.h" #include "packagemanagercore.h" +#include <QDir> + using namespace KDUpdater; using namespace QInstaller; @@ -79,11 +81,11 @@ bool FakeStopProcessForUpdateOperation::undoOperation() if (processes.count() == 1) { setError(UpdateOperation::UserDefinedError, tr("This process should be stopped before " - "continuing: %1").arg(processes.first())); + "continuing: %1").arg(QDir::toNativeSeparators(processes.first()))); } else { const QString sep = QString::fromWCharArray(L"\n \u2022 "); // Unicode bullet setError(UpdateOperation::UserDefinedError, tr("These processes should be stopped before " - "continuing: %1").arg(sep + processes.join(sep))); + "continuing: %1").arg(sep + QDir::toNativeSeparators(processes.join(sep)))); } return false; } diff --git a/src/libs/installer/fileutils.cpp b/src/libs/installer/fileutils.cpp index 2147a8978..044eeb34f 100644 --- a/src/libs/installer/fileutils.cpp +++ b/src/libs/installer/fileutils.cpp @@ -734,7 +734,7 @@ quint64 QInstaller::fileSize(const QFileInfo &info) bool QInstaller::isInBundle(const QString &path, QString *bundlePath) { #ifdef Q_OS_MACOS - QFileInfo fi = QFileInfo(path).absoluteFilePath(); + QFileInfo fi(QFileInfo(path).absoluteFilePath()); while (!fi.isRoot()) { if (fi.isBundle()) { if (bundlePath) diff --git a/src/libs/installer/genericdatacache.cpp b/src/libs/installer/genericdatacache.cpp index fd264ce63..a1e31ccfe 100644 --- a/src/libs/installer/genericdatacache.cpp +++ b/src/libs/installer/genericdatacache.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -120,6 +120,16 @@ CacheableItem::~CacheableItem() */ /*! + \enum GenericDataCache::RegisterMode + This enum holds the possible values for modes of registering items to cache. + + \value Copy + The contents of the item are copied to the cache. + \value Move + The contents of the item are move to the cache. +*/ + +/*! \fn template <typename T> QInstaller::GenericDataCache<T>::GenericDataCache() Constructs a new empty cache. The cache is invalid until set with a @@ -411,19 +421,20 @@ T *GenericDataCache<T>::itemByPath(const QString &path) const } /*! - \fn template <typename T> QInstaller::GenericDataCache<T>::registerItem(T *item, bool replace) + \fn template <typename T> QInstaller::GenericDataCache<T>::registerItem(T *item, bool replace, RegisterMode mode) Registers the \a item to the cache. If \a replace is set to \c true, the new \a item replaces a previous item with the same checksum. The cache takes ownership of the object pointed by \a item. The contents of the - item are copied to the cache with a subdirectory name that matches the checksum - of the item. + item are copied or moved to the cache with a subdirectory name that matches the checksum + of the item. The \a mode decides how the contents of the item are registered, either by + copying or moving. Returns \c true on success or \c false if the item could not be registered. */ template <typename T> -bool GenericDataCache<T>::registerItem(T *item, bool replace) +bool GenericDataCache<T>::registerItem(T *item, bool replace, RegisterMode mode) { QMutexLocker _(&m_mutex); if (m_invalidated) { @@ -455,10 +466,27 @@ bool GenericDataCache<T>::registerItem(T *item, bool replace) const QString newPath = m_path + QDir::separator() + QString::fromLatin1(item->checksum()); try { // A directory is in the way but it isn't registered to the current cache, remove. - if (QDir().exists(newPath)) + QDir dir; + if (dir.exists(newPath)) QInstaller::removeDirectory(newPath); - QInstaller::copyDirectoryContents(item->path(), newPath); + switch (mode) { + case Copy: + QInstaller::copyDirectoryContents(item->path(), newPath); + break; + case Move: + // First, try moving the top level directory + if (!dir.rename(item->path(), newPath)) { + qCDebug(lcDeveloperBuild) << "Failed to rename directory" << item->path() + << "to" << newPath << ". Trying again."; + // If that does not work, fallback to moving the contents one by one + QInstaller::moveDirectoryContents(item->path(), newPath); + } + break; + default: + throw Error(QCoreApplication::translate("GenericDataCache", + "Unknown register mode selected!")); + } } catch (const Error &e) { setErrorString(QCoreApplication::translate("GenericDataCache", "Error while copying item to path \"%1\": %2").arg(newPath, e.message())); @@ -587,8 +615,8 @@ bool GenericDataCache<T>::fromDisk() for (const auto &itemJsonValue : itemsJsonArray) { const QString checksum = itemJsonValue.toString(); - QScopedPointer<T> item(new T(m_path + QDir::separator() + checksum)); - m_items.insert(checksum.toLatin1(), item.take()); + std::unique_ptr<T> item(new T(m_path + QDir::separator() + checksum)); + m_items.insert(checksum.toLatin1(), item.release()); // The cache directory may contain other entries (unrelated directories or // invalid old cache items) which we don't care about, unless registering diff --git a/src/libs/installer/genericdatacache.h b/src/libs/installer/genericdatacache.h index 2bdf6697e..94085502c 100644 --- a/src/libs/installer/genericdatacache.h +++ b/src/libs/installer/genericdatacache.h @@ -64,9 +64,14 @@ template <typename T> class INSTALLER_EXPORT GenericDataCache { public: + enum RegisterMode { + Copy = 0, + Move = 1 + }; + GenericDataCache(); explicit GenericDataCache(const QString &path, const QString &type, const QString &version); - ~GenericDataCache(); + virtual ~GenericDataCache(); void setType(const QString &type); void setVersion(const QString &version); @@ -85,7 +90,7 @@ public: T *itemByChecksum(const QByteArray &checksum) const; T *itemByPath(const QString &path) const; - bool registerItem(T *item, bool replace = false); + bool registerItem(T *item, bool replace = false, RegisterMode mode = Copy); bool removeItem(const QByteArray &checksum); QList<T *> obsoleteItems() const; diff --git a/src/libs/installer/globals.cpp b/src/libs/installer/globals.cpp index 5240138c4..3fd084768 100644 --- a/src/libs/installer/globals.cpp +++ b/src/libs/installer/globals.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2023 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -30,6 +30,14 @@ #include "globals.h" +#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX) +#include <termios.h> +#include <unistd.h> +#elif defined(Q_OS_WIN) +#include <conio.h> +#endif +#include <iostream> + const char IFW_SERVER[] = "ifw.server"; const char IFW_INSTALLER_INSTALLLOG[] = "ifw.installer.installlog"; const char IFW_DEVELOPER_BUILD[] = "ifw.developer.build"; @@ -78,7 +86,9 @@ QStringList loggingCategories() { static QStringList categories = QStringList() << QLatin1String(IFW_INSTALLER_INSTALLLOG) - << QLatin1String(IFW_SERVER); + << QLatin1String(IFW_SERVER) + << QLatin1String(IFW_DEVELOPER_BUILD) + << QLatin1String("js"); return categories; } @@ -117,5 +127,52 @@ QString enumToString(const QMetaObject& metaObject, const char *enumerator, int return value; } +void askForCredentials(QString *username, QString *password, const QString &usernameTitle, const QString &passwordTitle) +{ + std::string usernameStdStr; + std::string passwordStdStr; + + std::cout << qPrintable(usernameTitle); + std::cin >> usernameStdStr; + + std::cout << qPrintable(passwordTitle); +#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX) + termios oldTerm; + termios term; + + // Turn off echoing + tcgetattr(STDIN_FILENO, &oldTerm); + term = oldTerm; + term.c_lflag &= ~ECHO; + tcsetattr(STDIN_FILENO, TCSANOW, &term); + + std::cin >> passwordStdStr; + + // Clear input buffer + std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); + + // Restore old attributes + tcsetattr(STDIN_FILENO, TCSANOW, &oldTerm); +#elif defined(Q_OS_WIN) + char ch; + while ((ch = _getch()) != '\r') { // Return key + if (ch == '\b') { // Backspace key + if (!passwordStdStr.empty()) + passwordStdStr.pop_back(); + } else { + passwordStdStr.push_back(ch); + } + } + // Clear input buffer + int c; + while ((c = getchar()) != '\n' && c != EOF); + +#endif + std::cout << "\n"; + + *username = username->fromStdString(usernameStdStr); + *password = password->fromStdString(passwordStdStr); +} + } // namespace QInstaller diff --git a/src/libs/installer/globals.h b/src/libs/installer/globals.h index 3b3f4e3ab..2d119048b 100644 --- a/src/libs/installer/globals.h +++ b/src/libs/installer/globals.h @@ -59,6 +59,8 @@ QSet<T> toQSet(const C<T> &container) return QSet<T>(container.begin(), container.end()); } +void askForCredentials(QString *username, QString *password, const QString &usernameTitle, const QString &passwordTitle); + } // QInstaller #endif // GLOBALS_H diff --git a/src/libs/installer/globalsettingsoperation.cpp b/src/libs/installer/globalsettingsoperation.cpp index 9608bba66..6ca50f96f 100644 --- a/src/libs/installer/globalsettingsoperation.cpp +++ b/src/libs/installer/globalsettingsoperation.cpp @@ -77,7 +77,7 @@ bool GlobalSettingsOperation::performOperation() bool GlobalSettingsOperation::undoOperation() { - if (parseUndoOperationArguments().count() > 0) + if (skipUndoOperation()) return true; const QStringList args = parsePerformOperationArguments(); diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro index 5a58968ee..ff7a0eed2 100644 --- a/src/libs/installer/installer.pro +++ b/src/libs/installer/installer.pro @@ -47,11 +47,13 @@ greaterThan(QT_MAJOR_VERSION, 5):QT += core5compat HEADERS += packagemanagercore.h \ aspectratiolabel.h \ calculatorbase.h \ + componentalias.h \ componentsortfilterproxymodel.h \ concurrentoperationrunner.h \ genericdatacache.h \ loggingutils.h \ metadata.h \ + metadatacache.h \ packagemanagercore_p.h \ packagemanagergui.h \ binaryformat.h \ @@ -144,13 +146,15 @@ HEADERS += packagemanagercore.h \ abstractarchive.h \ directoryguard.h \ archivefactory.h \ - operationtracer.h + operationtracer.h \ + customcombobox.h SOURCES += packagemanagercore.cpp \ abstractarchive.cpp \ archivefactory.cpp \ aspectratiolabel.cpp \ calculatorbase.cpp \ + componentalias.cpp \ concurrentoperationrunner.cpp \ directoryguard.cpp \ fileguard.cpp \ @@ -158,6 +162,7 @@ SOURCES += packagemanagercore.cpp \ genericdatacache.cpp \ loggingutils.cpp \ metadata.cpp \ + metadatacache.cpp \ operationtracer.cpp \ packagemanagercore_p.cpp \ packagemanagergui.cpp \ @@ -231,7 +236,8 @@ SOURCES += packagemanagercore.cpp \ packagesource.cpp \ repositorycategory.cpp \ componentselectionpage_p.cpp \ - commandlineparser.cpp + commandlineparser.cpp \ + customcombobox.cpp macos:SOURCES += fileutils_mac.mm diff --git a/src/libs/installer/installer_global.h b/src/libs/installer/installer_global.h index ea6865042..285eff910 100644 --- a/src/libs/installer/installer_global.h +++ b/src/libs/installer/installer_global.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -40,4 +40,10 @@ # define INSTALLER_EXPORT #endif +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +typedef uint hashValue; +#else +typedef size_t hashValue; +#endif + #endif //INSTALLER_GLOBAL_H diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp index 105e99fd8..4c53824af 100644 --- a/src/libs/installer/installercalculator.cpp +++ b/src/libs/installer/installercalculator.cpp @@ -29,6 +29,8 @@ #include "installercalculator.h" #include "component.h" +#include "componentalias.h" +#include "componentmodel.h" #include "packagemanagercore.h" #include "settings.h" #include <globals.h> @@ -51,6 +53,19 @@ InstallerCalculator::~InstallerCalculator() { } +bool InstallerCalculator::solve() +{ + if (!solve(m_core->aliasesMarkedForInstallation())) + return false; + + // Subtract components added by aliases + QList<Component *> components = m_core->componentsMarkedForInstallation(); + for (auto *component : qAsConst(m_resolvedComponents)) + components.removeAll(component); + + return solve(components); +} + QString InstallerCalculator::resolutionText(Component *component) const { const Resolution reason = resolutionType(component); @@ -67,6 +82,9 @@ QString InstallerCalculator::resolutionText(Component *component) const case Resolution::Selected: return QCoreApplication::translate("InstallerCalculator", "Selected components without dependencies:"); + case Resolution::Alias: + return QCoreApplication::translate("InstallerCalculator", + "Components selected by alias \"%1\":").arg(referencedComponent(component)); default: Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid install resolution detected!"); } @@ -110,6 +128,44 @@ bool InstallerCalculator::solve(const QList<Component *> &components) return true; } +bool InstallerCalculator::solve(const QList<ComponentAlias *> &aliases) +{ + if (aliases.isEmpty()) + return true; + + QList<ComponentAlias *> notAppendedAliases; // Aliases that require other aliases + for (auto *alias : aliases) { + if (!alias) + continue; + + if (m_toInstallComponentAliases.contains(alias->name())) { + const QString errorMessage = QCoreApplication::translate("InstallerCalculator", + "Recursion detected, component alias \"%1\" already added.").arg(alias->name()); + qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage; + m_errorString.append(errorMessage); + + Q_ASSERT_X(!m_toInstallComponentAliases.contains(alias->name()), Q_FUNC_INFO, + qPrintable(errorMessage)); + + return false; + } + + if (alias->aliases().isEmpty()) { + if (!addComponentsFromAlias(alias)) + return false; + } else { + notAppendedAliases.append(alias); + } + } + + for (auto *alias : qAsConst(notAppendedAliases)) { + if (!solveAlias(alias)) + return false; + } + + return true; +} + void InstallerCalculator::addComponentForInstall(Component *component, const QString &version) { if (!m_componentsForAutodepencencyCheck.contains(component)) @@ -121,12 +177,40 @@ void InstallerCalculator::addComponentForInstall(Component *component, const QSt } } +bool InstallerCalculator::addComponentsFromAlias(ComponentAlias *alias) +{ + QList<Component *> componentsToAdd; + for (auto *component : alias->components()) { + if (m_toInstallComponentIds.contains(component->name())) + continue; // Already added + + componentsToAdd.append(component); + // Updates the model, so that we also check the descendant + // components when calculating components to install + updateCheckState(component, Qt::Checked); + insertResolution(component, Resolution::Alias, alias->name()); + } + + m_toInstallComponentAliases.insert(alias->name()); + return solve(componentsToAdd); +} + QString InstallerCalculator::recursionError(Component *component) const { return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" " "already added with reason: \"%2\"").arg(component->name(), resolutionText(component)); } +bool InstallerCalculator::updateCheckState(Component *component, Qt::CheckState state) +{ + ComponentModel *currentModel = m_core->isUpdater() + ? m_core->updaterComponentModel() + : m_core->defaultComponentModel(); + + const QModelIndex &idx = currentModel->indexFromComponentName(component->treeName()); + return currentModel->setData(idx, state, Qt::CheckStateRole); +} + bool InstallerCalculator::solveComponent(Component *component, const QString &version) { const QStringList dependenciesList = component->currentDependencies(); @@ -200,6 +284,19 @@ bool InstallerCalculator::solveComponent(Component *component, const QString &ve return true; } +bool InstallerCalculator::solveAlias(ComponentAlias *alias) +{ + for (auto *requiredAlias : alias->aliases()) { + if (!solveAlias(requiredAlias)) + return false; + } + + if (m_toInstallComponentAliases.contains(alias->name())) + return true; + + return addComponentsFromAlias(alias); +} + QSet<Component *> InstallerCalculator::autodependencyComponents() { // All regular dependencies are resolved. Now we are looking for auto depend on components. diff --git a/src/libs/installer/installercalculator.h b/src/libs/installer/installercalculator.h index 339dbeffd..e542dc664 100644 --- a/src/libs/installer/installercalculator.h +++ b/src/libs/installer/installercalculator.h @@ -40,6 +40,7 @@ namespace QInstaller { class Component; +class ComponentAlias; class PackageManagerCore; class INSTALLER_EXPORT InstallerCalculator : public CalculatorBase @@ -48,20 +49,28 @@ public: InstallerCalculator(PackageManagerCore *core, const AutoDependencyHash &autoDependencyComponentHash); ~InstallerCalculator(); + bool solve(); bool solve(const QList<Component *> &components) override; + bool solve(const QList<ComponentAlias *> &aliases); + QString resolutionText(Component *component) const override; private: bool solveComponent(Component *component, const QString &version = QString()) override; + bool solveAlias(ComponentAlias *alias); void addComponentForInstall(Component *component, const QString &version = QString()); + bool addComponentsFromAlias(ComponentAlias *alias); QSet<Component *> autodependencyComponents(); QString recursionError(Component *component) const; + bool updateCheckState(Component *component, Qt::CheckState state); + private: QHash<Component*, QSet<Component*> > m_visitedComponents; QList<const Component*> m_componentsForAutodepencencyCheck; QSet<QString> m_toInstallComponentIds; //for faster lookups + QSet<QString> m_toInstallComponentAliases; //Helper hash for quicker search for autodependency components AutoDependencyHash m_autoDependencyComponentHash; }; diff --git a/src/libs/installer/installiconsoperation.cpp b/src/libs/installer/installiconsoperation.cpp index 5927ebf74..b21634cd7 100644 --- a/src/libs/installer/installiconsoperation.cpp +++ b/src/libs/installer/installiconsoperation.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -57,7 +57,7 @@ QString InstallIconsOperation::targetDirectory() Qt::SkipEmptyParts); XDG_DATA_HOME.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share"))); // default user-specific path - if (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive()) + if (packageManager() && packageManager()->hasAdminRights()) XDG_DATA_HOME.push_front(QLatin1String("/usr/local/share")); // default system-wide path QString directory; diff --git a/src/libs/installer/libarchivewrapper_p.cpp b/src/libs/installer/libarchivewrapper_p.cpp index e5c1e8598..b4325243d 100644 --- a/src/libs/installer/libarchivewrapper_p.cpp +++ b/src/libs/installer/libarchivewrapper_p.cpp @@ -41,12 +41,14 @@ namespace QInstaller { */ /*! + \internal \fn QInstaller::LibArchiveWrapperPrivate::dataBlockRequested() Emitted when the server process has requested another data block. */ /*! + \internal \fn QInstaller::LibArchiveWrapperPrivate::remoteWorkerFinished() Emitted when the server process has finished extracting an archive. @@ -105,7 +107,7 @@ void LibArchiveWrapperPrivate::setFilename(const QString &filename) { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetFilename), filename, dummy); + callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetFilename), filename); m_lock.unlock(); } m_archive.setFilename(filename); @@ -145,7 +147,7 @@ bool LibArchiveWrapperPrivate::extract(const QString &dirPath, const quint64 tot timer.start(); m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::AbstractArchiveExtract), dirPath, total); + callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveExtract), dirPath, total); m_lock.unlock(); { QEventLoop loop; @@ -203,7 +205,7 @@ void LibArchiveWrapperPrivate::setCompressionLevel(const AbstractArchive::Compre { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetCompressionLevel), level, dummy); + callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetCompressionLevel), level); m_lock.unlock(); return; } @@ -219,7 +221,7 @@ void LibArchiveWrapperPrivate::cancel() { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::AbstractArchiveCancel)); + callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveCancel)); m_lock.unlock(); return; } @@ -354,7 +356,7 @@ void LibArchiveWrapperPrivate::addDataBlock(const QByteArray &buffer) { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::AbstractArchiveAddDataBlock), buffer, dummy); + callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveAddDataBlock), buffer); m_lock.unlock(); } } @@ -367,7 +369,7 @@ void LibArchiveWrapperPrivate::setClientDataAtEnd() { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetClientDataAtEnd)); + callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetClientDataAtEnd)); m_lock.unlock(); } } @@ -379,7 +381,7 @@ void LibArchiveWrapperPrivate::setClientFilePosition(qint64 pos) { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetFilePosition), pos, dummy); + callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetFilePosition), pos); m_lock.unlock(); } } diff --git a/src/libs/installer/licenseoperation.cpp b/src/libs/installer/licenseoperation.cpp index 6cf0c8e3e..0d30ab514 100644 --- a/src/libs/installer/licenseoperation.cpp +++ b/src/libs/installer/licenseoperation.cpp @@ -31,6 +31,7 @@ #include "packagemanagercore.h" #include "settings.h" #include "fileutils.h" +#include "globals.h" #include <QtCore/QDir> #include <QtCore/QFile> @@ -56,7 +57,7 @@ void LicenseOperation::backup() bool LicenseOperation::performOperation() { - QVariantMap licenses = value(scLicenses).toMap(); + QVariantMap licenses = value(scLicensesValue).toMap(); if (licenses.isEmpty()) { setError(UserDefinedError); setErrorString(tr("No license files found to copy.")); @@ -86,9 +87,11 @@ bool LicenseOperation::performOperation() return false; } - QTextStream stream(&file); - stream.setCodec("UTF-8"); + QString outString; + QTextStream stream(&outString); stream << it.value().toString(); + + file.write(outString.toUtf8()); } return true; @@ -96,11 +99,10 @@ bool LicenseOperation::performOperation() bool LicenseOperation::undoOperation() { - const QVariantMap licenses = value(scLicenses).toMap(); + const QVariantMap licenses = value(scLicensesValue).toMap(); if (licenses.isEmpty()) { - setError(UserDefinedError); - setErrorString(tr("No license files found to delete.")); - return false; + qCWarning(QInstaller::lcInstallerInstallLog) << "No license files found to delete."; + return true; } QString targetDir = arguments().value(0); diff --git a/src/libs/installer/loggingutils.cpp b/src/libs/installer/loggingutils.cpp index 42bbc0117..9a36720dd 100644 --- a/src/libs/installer/loggingutils.cpp +++ b/src/libs/installer/loggingutils.cpp @@ -29,6 +29,7 @@ #include "loggingutils.h" #include "component.h" +#include "componentalias.h" #include "globals.h" #include "fileutils.h" #include "packagemanagercore.h" @@ -367,6 +368,44 @@ void LoggingHandler::printPackageInformation(const PackagesList &matchedPackages } /*! + Prints basic or more detailed information about component \a aliases, + depending on the current verbosity level. +*/ +void LoggingHandler::printAliasInformation(const QList<ComponentAlias *> &aliases) +{ + QList<ComponentAlias *> sortedAliases = aliases; + std::sort(sortedAliases.begin(), sortedAliases.end(), + [](const ComponentAlias *lhs, const ComponentAlias *rhs) { + return lhs->name() < rhs->name(); + } + ); + + QString output; + QTextStream stream(&output); + + stream << Qt::endl; + for (auto *alias : qAsConst(sortedAliases)) { + stream << "Name: " << alias->name() << Qt::endl; + stream << "Display name: " << alias->displayName() << Qt::endl; + stream << "Description: " << alias->description() << Qt::endl; + stream << "Version: " << alias->version() << Qt::endl; + if (verboseLevel() == VerbosityLevel::Detailed) + stream << "Virtual: " << alias->value(scVirtual) << Qt::endl; + + stream << "Components: " << alias->value(scRequiredComponents) << Qt::endl; + stream << "Required aliases: " << alias->value(scRequiredAliases) << Qt::endl; + + stream << "Optional components: " << alias->value(scOptionalComponents) << Qt::endl; + stream << "Optional aliases: " << alias->value(scOptionalAliases) << Qt::endl; + + if (sortedAliases.indexOf(alias) != (sortedAliases.count() - 1)) + stream << "========================================" << Qt::endl; + } + + std::cout << qPrintable(output); +} + +/*! \internal */ VerboseWriter::VerboseWriter() diff --git a/src/libs/installer/loggingutils.h b/src/libs/installer/loggingutils.h index 8bd4217ee..18ff2d2c5 100644 --- a/src/libs/installer/loggingutils.h +++ b/src/libs/installer/loggingutils.h @@ -41,6 +41,7 @@ namespace QInstaller { class Component; +class ComponentAlias; class INSTALLER_EXPORT LoggingHandler { @@ -67,6 +68,7 @@ public: void printUpdateInformation(const QList<Component *> &components) const; void printLocalPackageInformation(const QList<KDUpdater::LocalPackage> &packages) const; void printPackageInformation(const PackagesList &matchedPackages, const LocalPackagesMap &installedPackages) const; + void printAliasInformation(const QList<ComponentAlias *> &aliases); friend VerbosityLevel &operator++(VerbosityLevel &level, int); friend VerbosityLevel &operator--(VerbosityLevel &level, int); diff --git a/src/libs/installer/messageboxhandler.cpp b/src/libs/installer/messageboxhandler.cpp index 78abc88fa..052709e51 100644 --- a/src/libs/installer/messageboxhandler.cpp +++ b/src/libs/installer/messageboxhandler.cpp @@ -368,7 +368,9 @@ static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, QMessageBo QMessageBox::StandardButton defaultButton) { QMessageBox msgBox(icon, title, text, QMessageBox::NoButton, parent); - msgBox.setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + msgBox.setTextInteractionFlags(Qt::TextBrowserInteraction); + msgBox.setTextFormat(Qt::RichText); + QDialogButtonBox *buttonBox = msgBox.findChild<QDialogButtonBox *>(); Q_ASSERT(buttonBox != nullptr); diff --git a/src/libs/installer/metadata.cpp b/src/libs/installer/metadata.cpp index 9ae817127..2eccb020e 100644 --- a/src/libs/installer/metadata.cpp +++ b/src/libs/installer/metadata.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -46,6 +46,52 @@ namespace QInstaller { \brief The Metadata class represents fetched metadata from a repository. */ + +/*! + \internal +*/ +static bool verifyFileIntegrityFromElement(const QDomElement &element, const QString &childNodeName, + const QString &attribute, const QString &metaDirectory, bool testChecksum) +{ + const QDomNodeList nodes = element.childNodes(); + for (int i = 0; i < nodes.count(); ++i) { + const QDomNode node = nodes.at(i); + if (node.nodeName() != childNodeName) + continue; + + const QDir dir(metaDirectory); + const QString filename = attribute.isEmpty() + ? node.toElement().text() + : node.toElement().attribute(attribute); + + if (filename.isEmpty()) + continue; + + QFile file(dir.absolutePath() + QDir::separator() + filename); + if (!file.open(QIODevice::ReadOnly)) { + qCWarning(QInstaller::lcInstallerInstallLog) + << "Cannot open" << file.fileName() + << "for reading:" << file.errorString(); + return false; + } + + if (!testChecksum) + continue; + + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(&file); + + const QByteArray checksum = hash.result().toHex(); + if (!QFileInfo::exists(dir.absolutePath() + QDir::separator() + + QString::fromLatin1(checksum) + QLatin1String(".sha1"))) { + qCWarning(QInstaller::lcInstallerInstallLog) + << "Unexpected checksum for file" << file.fileName(); + return false; + } + } + return true; +} + /*! Constructs a new metadata object. */ @@ -122,18 +168,23 @@ QDomDocument Metadata::updatesDocument() const } /*! - Returns \c true if the \c Updates.xml document of this metadata - exists, \c false otherwise. + Returns \c true if the \c Updates.xml document of this metadata exists, and that all + meta files referenced in the document exist. If the \c Updates.xml contains a \c Checksum + element with a value of \c true, the integrity of the files is also verified. + + Returns \c false otherwise. */ bool Metadata::isValid() const { - const QString updateFile(path() + QLatin1String("/Updates.xml")); - if (!QFileInfo::exists(updateFile)) { + QFile updateFile(path() + QLatin1String("/Updates.xml")); + if (!updateFile.open(QIODevice::ReadOnly)) { qCWarning(QInstaller::lcInstallerInstallLog) - << "File" << updateFile << "does not exist."; + << "Cannot open" << updateFile.fileName() + << "for reading:" << updateFile.errorString(); return false; } - return true; + + return verifyMetaFiles(&updateFile); } /*! @@ -282,4 +333,78 @@ bool Metadata::containsRepositoryUpdates() const return false; } +/*! + Verifies that the files referenced in \a updateFile document exist + on disk. If the document contains a \c Checksum element with a value + of \c true, the integrity of the files is also verified. + + Returns \c true if the meta files are valid, \c false otherwise. +*/ +bool Metadata::verifyMetaFiles(QFile *updateFile) const +{ + QDomDocument doc; + QString errorString; + if (!doc.setContent(updateFile, &errorString)) { + qCWarning(QInstaller::lcInstallerInstallLog) + << "Cannot set document content:" << errorString; + return false; + } + + const QDomElement rootElement = doc.documentElement(); + const QDomNodeList childNodes = rootElement.childNodes(); + + bool testChecksum = true; + const QDomElement checksumElement = rootElement.firstChildElement(QLatin1String("Checksum")); + if (!checksumElement.isNull()) + testChecksum = (checksumElement.text().toLower() == scTrue); + + for (int i = 0; i < childNodes.count(); ++i) { + const QDomElement element = childNodes.at(i).toElement(); + if (element.isNull() || element.tagName() != QLatin1String("PackageUpdate")) + continue; + + const QDomNodeList c2 = element.childNodes(); + QString packageName; + QString unused1; + QString unused2; + + // Only need the package name, so values for "online" and "testCheckSum" do not matter + if (!MetadataJob::parsePackageUpdate(c2, packageName, unused1, unused2, true, true)) + continue; // nothing to check for this package + + const QString packagePath = QString::fromLatin1("%1/%2/").arg(path(), packageName); + for (auto &metaTagName : scMetaElements) { + const QDomElement metaElement = element.firstChildElement(metaTagName); + if (metaElement.isNull()) + continue; + + if (metaElement.tagName() == QLatin1String("Licenses")) { + if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("License"), + QLatin1String("file"), packagePath, testChecksum)) { + return false; + } + } else if (metaElement.tagName() == QLatin1String("UserInterfaces")) { + if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("UserInterface"), + QString(), packagePath, testChecksum)) { + return false; + } + } else if (metaElement.tagName() == QLatin1String("Translations")) { + if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("Translation"), + QString(), packagePath, testChecksum)) { + return false; + } + } else if (metaElement.tagName() == QLatin1String("Script")) { + if (!verifyFileIntegrityFromElement(metaElement.parentNode().toElement(), + QLatin1String("Script"), QString(), packagePath, testChecksum)) { + return false; + } + } else { + Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown meta element."); + } + } + } + + return true; +} + } // namespace QInstaller diff --git a/src/libs/installer/metadata.h b/src/libs/installer/metadata.h index 3063be829..c7e4e857c 100644 --- a/src/libs/installer/metadata.h +++ b/src/libs/installer/metadata.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -35,6 +35,8 @@ #include <QDomDocument> +class QFile; + namespace QInstaller { class INSTALLER_EXPORT Metadata : public CacheableItem @@ -64,6 +66,9 @@ public: bool containsRepositoryUpdates() const; private: + bool verifyMetaFiles(QFile *updateFile) const; + +private: Repository m_repository; QString m_persistentRepositoryPath; mutable QByteArray m_checksum; @@ -71,13 +76,6 @@ private: bool m_fromDefaultRepository; }; -Q_GLOBAL_STATIC_WITH_ARGS(QStringList, scMetaElements, (QStringList( - QLatin1String("Script")) << - QLatin1String("Licenses") << - QLatin1String("UserInterfaces") << - QLatin1String("Translations") -)); - } // namespace QInstaller #endif // METADATA_H diff --git a/src/libs/installer/metadatacache.cpp b/src/libs/installer/metadatacache.cpp new file mode 100644 index 000000000..744e455f4 --- /dev/null +++ b/src/libs/installer/metadatacache.cpp @@ -0,0 +1,67 @@ +/************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "metadatacache.h" + +#define QUOTE_(x) #x +#define QUOTE(x) QUOTE_(x) + +namespace QInstaller { + +/*! + \inmodule QtInstallerFramework + \class QInstaller::MetadataCache + \brief The MetadataCache is a class for a checksum based storage of \c Metadata objects on disk. + + MetadataCache manages a cache storage for a set \l{path()}, which contains + a subdirectory for each registered \c Metadata item. The cache has a manifest file in + its root directory, which lists the version and type of the cache, and all its items. + The file is updated automatically when the metadata cache object is destructed, or + it can be updated periodically by calling \l{sync()}. +*/ + +/*! + Constructs a new empty cache. The cache is invalid until set with a + path and initialized. +*/ +MetadataCache::MetadataCache() + : GenericDataCache<Metadata>() +{ + setType(QLatin1String("Metadata")); + setVersion(QLatin1String(QUOTE(IFW_CACHE_FORMAT_VERSION))); +} + +/*! + Constructs a cache to \a path. The cache is initialized automatically. +*/ +MetadataCache::MetadataCache(const QString &path) + : GenericDataCache(path, QLatin1String("Metadata"), QLatin1String(QUOTE(IFW_CACHE_FORMAT_VERSION))) +{ +} + +} // namespace QInstaller diff --git a/src/libs/installer/metadatacache.h b/src/libs/installer/metadatacache.h new file mode 100644 index 000000000..804d1b6db --- /dev/null +++ b/src/libs/installer/metadatacache.h @@ -0,0 +1,46 @@ +/************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef METADATACACHE_H +#define METADATACACHE_H + +#include "genericdatacache.h" +#include "metadata.h" + +namespace QInstaller { + +class MetadataCache : public GenericDataCache<Metadata> +{ +public: + MetadataCache(); + explicit MetadataCache(const QString &path); +}; + +} // namespace QInstaller + +#endif // METADATACACHE_H diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp index ff15d7b0d..1bed304c6 100644 --- a/src/libs/installer/metadatajob.cpp +++ b/src/libs/installer/metadatajob.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2023 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -41,9 +41,7 @@ #include <QtConcurrent> #include <QtMath> #include <QRandomGenerator> - -#define QUOTE_(x) #x -#define QUOTE(x) QUOTE_(x) +#include <QApplication> namespace QInstaller { @@ -52,7 +50,6 @@ namespace QInstaller { \value All \value CompressedPackage - \value UpdatesXML */ /*! @@ -145,9 +142,6 @@ QList<Metadata *> MetadataJob::metadata() const QHash<RepositoryCategory, QSet<Repository>>::const_iterator it; for (it = repositoryHash.constBegin(); it != repositoryHash.constEnd(); ++it) { - if (m_core->isUpdater()) - return true; - if (!it.key().isEnabled()) continue; // Let's try the next one @@ -192,8 +186,6 @@ bool MetadataJob::resetCache(bool init) m_metaFromCache.clear(); m_metaFromCache.setPath(m_core->settings().localCachePath()); - m_metaFromCache.setType(QLatin1String("Metadata")); - m_metaFromCache.setVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION))); if (!init) return true; @@ -218,6 +210,11 @@ bool MetadataJob::clearCache() return false; } +bool MetadataJob::isValidCache() const +{ + return m_metaFromCache.isValid(); +} + // -- private slots void MetadataJob::doStart() @@ -240,13 +237,15 @@ void MetadataJob::doStart() if (m_downloadType != DownloadType::CompressedPackage) { emit infoMessage(this, tr("Fetching latest update information...")); const bool onlineInstaller = m_core->isInstaller() && !m_core->isOfflineOnly(); - if (onlineInstaller || m_core->isMaintainer()) { + const QSet<Repository> repositories = getRepositories(); + + if (onlineInstaller || m_core->isMaintainer() + || (m_core->settings().allowRepositoriesForOfflineInstaller() && !repositories.isEmpty())) { static const QString updateFilePath(QLatin1Char('/') + scUpdatesXML + QLatin1Char('?')); static const QString randomQueryString = QString::number(QRandomGenerator::global()->generate()); - QList<FileTaskItem> items; - QSet<Repository> repositories = getRepositories(); quint64 cachedCount = 0; + setProgressTotalAmount(0); // Show only busy indicator during this loop as we have no progress to measure foreach (const Repository &repo, repositories) { // For not blocking the UI qApp->processEvents(); @@ -289,17 +288,24 @@ void MetadataJob::doStart() FileTaskItem item(url, tmp.path() + QLatin1String("/Updates.xml")); item.insert(TaskRole::UserRole, QVariant::fromValue(repo)); item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator)); - items.append(item); + m_updatesXmlItems.append(item); } } + setProgressTotalAmount(100); const quint64 totalCount = repositories.count(); if (cachedCount > 0) { qCDebug(lcInstallerInstallLog).nospace() << "Loaded from cache " << cachedCount << "/" << totalCount << ". Downloading remaining " - << items.count() << "/" << totalCount <<"."; + << m_updatesXmlItems.count() << "/" << totalCount <<"."; + } else { + qCDebug(lcInstallerInstallLog).nospace() <<"Downloading " << m_updatesXmlItems.count() + << " items to cache."; } - if (items.count() > 0) { - startXMLTask(items); + if (m_updatesXmlItems.count() > 0) { + double taskCount = m_updatesXmlItems.length()/static_cast<double>(m_downloadableChunkSize); + m_totalTaskCount = qCeil(taskCount); + m_taskNumber = 0; + startXMLTask(); } else { emitFinished(); } @@ -338,13 +344,22 @@ void MetadataJob::doStart() } } -void MetadataJob::startXMLTask(const QList<FileTaskItem> &items) +bool MetadataJob::startXMLTask() { - DownloadFileTask *const xmlTask = new DownloadFileTask(items); - xmlTask->setProxyFactory(m_core->proxyFactory()); - connect(&m_xmlTask, &QFutureWatcher<FileTaskResult>::progressValueChanged, this, - &MetadataJob::progressChanged); - m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask)); + int chunkSize = qMin(m_updatesXmlItems.length(), m_downloadableChunkSize); + QList<FileTaskItem> tempPackages = m_updatesXmlItems.mid(0, chunkSize); + m_updatesXmlItems = m_updatesXmlItems.mid(chunkSize, m_updatesXmlItems.length()); + if (tempPackages.length() > 0) { + DownloadFileTask *const xmlTask = new DownloadFileTask(tempPackages); + xmlTask->setProxyFactory(m_core->proxyFactory()); + connect(&m_xmlTask, &QFutureWatcher<FileTaskResult>::progressValueChanged, this, + &MetadataJob::progressChanged); + m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask)); + + setInfoMessage(tr("Retrieving information from remote repositories...")); + return true; + } + return false; } void MetadataJob::doCancel() @@ -431,7 +446,7 @@ void MetadataJob::unzipRepositoryTaskFinished() FileTaskItem item(url, tmp.path() + QLatin1String("/Updates.xml")); item.insert(TaskRole::UserRole, QVariant::fromValue(repo)); - m_unzipRepositoryitems.append(item); + m_updatesXmlItems.append(item); } else { //Repository is not valid, remove it Settings &s = m_core->settings(); @@ -451,8 +466,8 @@ void MetadataJob::unzipRepositoryTaskFinished() //One can specify many zipped repository items at once. As the repositories are //unzipped one by one, we collect here all items before parsing xml files from those. - if (m_unzipRepositoryitems.count() > 0 && m_unzipRepositoryTasks.isEmpty()) { - startXMLTask(m_unzipRepositoryitems); + if (m_updatesXmlItems.count() > 0 && m_unzipRepositoryTasks.isEmpty()) { + startXMLTask(); } else { if (error != Job::NoError) { emitFinishedWithError(QInstaller::DownloadError, errorString); @@ -476,19 +491,34 @@ void MetadataJob::xmlTaskFinished() Status status = XmlDownloadFailure; try { m_xmlTask.waitForFinished(); - status = parseUpdatesXml(m_xmlTask.future().results()); + m_updatesXmlResult.append(m_xmlTask.future().results()); + if (!startXMLTask()) { + status = parseUpdatesXml(m_updatesXmlResult); + m_updatesXmlResult.clear(); + } else { + return; + } } catch (const AuthenticationRequiredException &e) { if (e.type() == AuthenticationRequiredException::Type::Proxy) { - const QNetworkProxy proxy = e.proxy(); - ProxyCredentialsDialog proxyCredentials(proxy); qCWarning(QInstaller::lcInstallerInstallLog) << e.message(); - - if (proxyCredentials.exec() == QDialog::Accepted) { + QString username; + QString password; + const QNetworkProxy proxy = e.proxy(); + if (m_core->isCommandLineInstance()) { + qCDebug(QInstaller::lcInstallerInstallLog).noquote() << QString::fromLatin1("The proxy %1:%2 requires a username and password").arg(proxy.hostName(), proxy.port()); + askForCredentials(&username, &password, QLatin1String("Username: "), QLatin1String("Password: ")); + } else { + ProxyCredentialsDialog proxyCredentials(proxy); + if (proxyCredentials.exec() == QDialog::Accepted) { + username = proxyCredentials.userName(); + password = proxyCredentials.password(); + } + } + if (!username.isEmpty()) { qCDebug(QInstaller::lcInstallerInstallLog) << "Retrying with new credentials ..."; PackageManagerProxyFactory *factory = m_core->proxyFactory(); - factory->setProxyCredentials(proxy, proxyCredentials.userName(), - proxyCredentials.password()); + factory->setProxyCredentials(proxy, username, password); m_core->setProxyFactory(factory); status = XmlDownloadRetry; } else { @@ -497,13 +527,25 @@ void MetadataJob::xmlTaskFinished() } } else if (e.type() == AuthenticationRequiredException::Type::Server) { qCWarning(QInstaller::lcInstallerInstallLog) << e.message(); - ServerAuthenticationDialog dlg(e.message(), e.taskItem()); - if (dlg.exec() == QDialog::Accepted) { + QString username; + QString password; + if (m_core->isCommandLineInstance()) { + qCDebug(QInstaller::lcInstallerInstallLog) << "Server Requires Authentication"; + qCDebug(QInstaller::lcInstallerInstallLog) << "You need to supply a username and password to access this site."; + askForCredentials(&username, &password, QLatin1String("Username: "), QLatin1String("Password: ")); + } else { + ServerAuthenticationDialog dlg(e.message(), e.taskItem()); + if (dlg.exec() == QDialog::Accepted) { + username = dlg.user(); + password = dlg.password(); + } + } + if (!username.isEmpty()) { Repository original = e.taskItem().value(TaskRole::UserRole) .value<Repository>(); Repository replacement = original; - replacement.setUsername(dlg.user()); - replacement.setPassword(dlg.password()); + replacement.setUsername(username); + replacement.setPassword(password); Settings &s = m_core->settings(); QSet<Repository> temporaries = s.temporaryRepositories(); @@ -557,8 +599,9 @@ void MetadataJob::xmlTaskFinished() // No new metadata packages to fetch, still need to update the cache // for refreshed repositories. startUpdateCacheTask(); - } + } } else if (status == XmlDownloadRetry) { + reset(); QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); } else { reset(); @@ -630,6 +673,7 @@ void MetadataJob::metadataTaskFinished() UnzipArchiveTask *task = new UnzipArchiveTask(result.target(), item.value(TaskRole::UserRole).toString()); task->setRemoveArchive(true); + task->setStoreChecksums(true); QFutureWatcher<void> *watcher = new QFutureWatcher<void>(); m_unzipTasks.insert(watcher, qobject_cast<QObject*> (task)); @@ -684,17 +728,11 @@ bool MetadataJob::fetchMetaDataPackages() QList<FileTaskItem> tempPackages = m_packages.mid(0, chunkSize); m_packages = m_packages.mid(chunkSize, m_packages.length()); if (tempPackages.length() > 0) { - m_taskNumber++; setProcessedAmount(0); DownloadFileTask *const metadataTask = new DownloadFileTask(tempPackages); metadataTask->setProxyFactory(m_core->proxyFactory()); m_metadataTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, metadataTask)); - QString metaInformation; - if (m_totalTaskCount > 1) - metaInformation = tr("Retrieving meta information from remote repository... %1/%2 ").arg(m_taskNumber).arg(m_totalTaskCount); - else - metaInformation = tr("Retrieving meta information from remote repository... "); - emit infoMessage(this, metaInformation); + setInfoMessage(tr("Retrieving meta information from remote repository...")); return true; } return false; @@ -703,6 +741,7 @@ bool MetadataJob::fetchMetaDataPackages() void MetadataJob::reset() { m_packages.clear(); + m_updatesXmlItems.clear(); m_defaultRepositoriesFetched = false; m_fetchedCategorizedRepositories.clear(); @@ -721,6 +760,7 @@ void MetadataJob::reset() } catch (...) {} m_tempDirDeleter.releaseAndDeleteAll(); m_metadataResult.clear(); + m_updatesXmlResult.clear(); m_taskNumber = 0; } @@ -728,7 +768,6 @@ void MetadataJob::resetCompressedFetch() { setError(Job::NoError); setErrorString(QString()); - m_unzipRepositoryitems.clear(); try { foreach (QFutureWatcher<void> *const watcher, m_unzipTasks.keys()) { @@ -762,7 +801,7 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re } QFileInfo fileInfo(result.target()); - QScopedPointer<Metadata> metadata(new Metadata(fileInfo.absolutePath())); + std::unique_ptr<Metadata> metadata(new Metadata(fileInfo.absolutePath())); QFile file(result.target()); if (!file.open(QIODevice::ReadOnly)) { qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot open Updates.xml for reading:" @@ -770,13 +809,21 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re return XmlDownloadFailure; } const FileTaskItem item = result.value(TaskRole::TaskItem).value<FileTaskItem>(); + const Repository repository = item.value(TaskRole::UserRole).value<Repository>(); - // Check if we have cached the metadata for this repository already QCryptographicHash hash(QCryptographicHash::Sha1); hash.addData(&file); const QByteArray updatesChecksum = hash.result().toHex(); + if (!repository.xmlChecksum().isEmpty() && updatesChecksum != repository.xmlChecksum()) { + qCWarning(lcDeveloperBuild).noquote().nospace() << "The checksum for Updates.xml " + "file downloaded from repository:\n" << repository.url().toString() << "\ndoes not " + "match the expected value:\n\tActual SHA1: " << updatesChecksum << "\n\tExpected SHA1: " + << repository.xmlChecksum() << Qt::endl; + } + bool refreshed; + // Check if we have cached the metadata for this repository already Status status = refreshCacheItem(result, updatesChecksum, &refreshed); if (status != XmlDownloadSuccess) return status; @@ -798,7 +845,7 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re } file.close(); - metadata->setRepository(item.value(TaskRole::UserRole).value<Repository>()); + metadata->setRepository(repository); const bool online = !(metadata->repository().url().scheme()).isEmpty(); bool testCheckSum = true; @@ -855,7 +902,7 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re m_fetchedCategorizedRepositories.insert(metadataPtr->repository()); // For faster lookups const QString metadataPath = metadata->path(); - m_fetchedMetadata.insert(metadataPath, metadata.take()); + m_fetchedMetadata.insert(metadataPath, metadata.release()); // search for additional repositories that we might need to check status = parseRepositoryUpdates(root, result, metadataPtr); @@ -885,6 +932,7 @@ MetadataJob::Status MetadataJob::refreshCacheItem(const FileTaskResult &result, const FileTaskItem item = result.value(TaskRole::TaskItem).value<FileTaskItem>(); const Repository repository = item.value(TaskRole::UserRole).value<Repository>(); + if (cachedMetadata->isValid() && !repository.isCompressed()) { // Refresh repository information to cache. Same repository may appear in multiple // categories and the metadata may be available from default repositories simultaneously. @@ -973,9 +1021,8 @@ QSet<Repository> MetadataJob::getRepositories() // Fetch repositories under archive which are selected in UI. // If repository is already fetched, do not fetch it again. - // In updater mode, fetch always all archive repositories to get updates for (const RepositoryCategory &repositoryCategory : m_core->settings().repositoryCategories()) { - if (!m_core->isUpdater() && !repositoryCategory.isEnabled()) + if (!repositoryCategory.isEnabled()) continue; for (const Repository &repository : repositoryCategory.repositories()) { @@ -1015,7 +1062,7 @@ bool MetadataJob::parsePackageUpdate(const QDomNodeList &c2, QString &packageNam else if ((element.tagName() == QLatin1String("SHA1")) && testCheckSum) packageHash = element.text(); else { - foreach (QString meta, *scMetaElements) { + foreach (QString meta, scMetaElements) { if (element.tagName() == meta) { metaFound = true; break; @@ -1122,4 +1169,14 @@ MetadataJob::Status MetadataJob::setAdditionalRepositories(QMultiHash<QString, Q } return status; } + +void MetadataJob::setInfoMessage(const QString &message) +{ + m_taskNumber++; + QString metaInformation = message; + if (m_totalTaskCount > 1) + metaInformation = QLatin1String(" %1 %2/%3 ").arg(message).arg(m_taskNumber).arg(m_totalTaskCount); + emit infoMessage(this, metaInformation); + +} } // namespace QInstaller diff --git a/src/libs/installer/metadatajob.h b/src/libs/installer/metadatajob.h index 37d9367e5..13ad3ea8c 100644 --- a/src/libs/installer/metadatajob.h +++ b/src/libs/installer/metadatajob.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -33,7 +33,7 @@ #include "fileutils.h" #include "job.h" #include "metadata.h" -#include "genericdatacache.h" +#include "metadatacache.h" #include "repository.h" #include <QFutureWatcher> @@ -74,6 +74,7 @@ public: bool resetCache(bool init = false); bool clearCache(); + bool isValidCache() const; private slots: void doStart() override; @@ -86,7 +87,7 @@ private slots: void progressChanged(int progress); void setProgressTotalAmount(int maximum); void unzipRepositoryTaskFinished(); - void startXMLTask(const QList<FileTaskItem> &items); + bool startXMLTask(); private: bool fetchMetaDataPackages(); @@ -108,6 +109,7 @@ private: const FileTaskResult &result, const Metadata &metadata); MetadataJob::Status setAdditionalRepositories(QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates, const FileTaskResult &result, const Metadata& metadata); + void setInfoMessage(const QString &message); private: friend class Metadata; @@ -116,6 +118,7 @@ private: PackageManagerCore *m_core; QList<FileTaskItem> m_packages; + QList<FileTaskItem> m_updatesXmlItems; TempPathDeleter m_tempDirDeleter; QFutureWatcher<FileTaskResult> m_xmlTask; QFutureWatcher<FileTaskResult> m_metadataTask; @@ -123,8 +126,8 @@ private: QHash<QFutureWatcher<void> *, QObject*> m_unzipTasks; QHash<QFutureWatcher<void> *, QObject*> m_unzipRepositoryTasks; DownloadType m_downloadType; - QList<FileTaskItem> m_unzipRepositoryitems; QList<FileTaskResult> m_metadataResult; + QList<FileTaskResult> m_updatesXmlResult; int m_downloadableChunkSize; int m_taskNumber; int m_totalTaskCount; @@ -133,7 +136,7 @@ private: QSet<Repository> m_fetchedCategorizedRepositories; QHash<QString, Metadata *> m_fetchedMetadata; - GenericDataCache<Metadata> m_metaFromCache; + MetadataCache m_metaFromCache; }; } // namespace QInstaller diff --git a/src/libs/installer/metadatajob_p.h b/src/libs/installer/metadatajob_p.h index ef2729dbb..837a7e9ae 100644 --- a/src/libs/installer/metadatajob_p.h +++ b/src/libs/installer/metadatajob_p.h @@ -62,11 +62,16 @@ class UnzipArchiveTask : public AbstractTask<void> public: UnzipArchiveTask(const QString &arcive, const QString &target) - : m_archive(arcive), m_targetDir(target), m_removeArchive(false) + : m_archive(arcive) + , m_targetDir(target) + , m_removeArchive(false) + , m_storeChecksums(false) {} + QString target() { return m_targetDir; } QString archive() { return m_archive; } void setRemoveArchive(bool remove) { m_removeArchive = remove; } + void setStoreChecksums(bool store) { m_storeChecksums = store; } void doTask(QFutureInterface<void> &fi) override { @@ -82,12 +87,43 @@ public: if (!archive) { fi.reportException(UnzipArchiveException(MetadataJob::tr("Unsupported archive \"%1\": no handler " "registered for file suffix \"%2\".").arg(m_archive, QFileInfo(m_archive).suffix()))); + return; } else if (!archive->open(QIODevice::ReadOnly)) { fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open file \"%1\" for " "reading: %2").arg(QDir::toNativeSeparators(m_archive), archive->errorString()))); + return; } else if (!archive->extract(m_targetDir)) { fi.reportException(UnzipArchiveException(MetadataJob::tr("Error while extracting " "archive \"%1\": %2").arg(QDir::toNativeSeparators(m_archive), archive->errorString()))); + return; + } + + if (m_storeChecksums) { + // Calculate and store checksums of extracted files for later use + const QVector<ArchiveEntry> entries = archive->list(); + for (auto &entry : entries) { + if (entry.isDirectory) + continue; + + QFile file(m_targetDir + QDir::separator() + entry.path); + if (!file.open(QIODevice::ReadOnly)) { + fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open extracted file \"%1\" for " + "reading: %2").arg(QDir::toNativeSeparators(file.fileName()), file.errorString()))); + break; + } + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(&file); + + const QByteArray hexChecksum = hash.result().toHex(); + QFileInfo fileInfo(file.fileName()); + QFile hashFile(fileInfo.absolutePath() + QDir::separator() + + QString::fromLatin1(hexChecksum) + QLatin1String(".sha1")); + if (!hashFile.open(QIODevice::WriteOnly)) { + fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open file \"%1\" for " + "writing: %2").arg(QDir::toNativeSeparators(hashFile.fileName()), hashFile.errorString()))); + break; + } + } } archive->close(); @@ -101,6 +137,7 @@ private: QString m_archive; QString m_targetDir; bool m_removeArchive; + bool m_storeChecksums; }; class CacheTaskException : public QException @@ -126,7 +163,7 @@ class UpdateCacheTask : public AbstractTask<void> Q_DISABLE_COPY(UpdateCacheTask) public: - UpdateCacheTask(GenericDataCache<Metadata> &cache, QHash<QString, Metadata *> &updates) + UpdateCacheTask(MetadataCache &cache, QHash<QString, Metadata *> &updates) : m_cache(&cache) , m_updates(&updates) {} @@ -140,7 +177,7 @@ public: QStringList registeredKeys; bool success = true; for (auto *meta : qAsConst(*m_updates)) { - if (!m_cache->registerItem(meta, true)) { + if (!m_cache->registerItem(meta, true, MetadataCache::Move)) { success = false; break; } @@ -174,7 +211,7 @@ public: } private: - GenericDataCache<Metadata> *const m_cache; + MetadataCache *const m_cache; QHash<QString, Metadata *> *const m_updates; }; diff --git a/src/libs/installer/observer.cpp b/src/libs/installer/observer.cpp index 30afce719..57b67d8e1 100644 --- a/src/libs/installer/observer.cpp +++ b/src/libs/installer/observer.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -121,9 +121,9 @@ QByteArray FileTaskObserver::checkSum() const return m_hash.result(); } -void FileTaskObserver::addCheckSumData(const char *data, int length) +void FileTaskObserver::addCheckSumData(const QByteArray &data) { - m_hash.addData(data, length); + m_hash.addData(data); } void FileTaskObserver::addSample(qint64 sample) diff --git a/src/libs/installer/observer.h b/src/libs/installer/observer.h index 198a0f89c..d638d8ee4 100644 --- a/src/libs/installer/observer.h +++ b/src/libs/installer/observer.h @@ -60,7 +60,7 @@ public: QString progressText() const override; QByteArray checkSum() const; - void addCheckSumData(const char *data, int length); + void addCheckSumData(const QByteArray &data); void addSample(qint64 sample); void timerEvent(QTimerEvent *event) override; diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp index b7d7d2a26..0eae41ea3 100644 --- a/src/libs/installer/packagemanagercore.cpp +++ b/src/libs/installer/packagemanagercore.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2023 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -31,6 +31,7 @@ #include "adminauthorization.h" #include "binarycontent.h" #include "component.h" +#include "componentalias.h" #include "componentmodel.h" #include "downloadarchivesjob.h" #include "errors.h" @@ -46,6 +47,7 @@ #include "installercalculator.h" #include "uninstallercalculator.h" #include "loggingutils.h" +#include "componentsortfilterproxymodel.h" #include <productkeycheck.h> @@ -56,9 +58,15 @@ #include <QtCore/QMutex> #include <QtCore/QSettings> #include <QtCore/QTemporaryFile> +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include <QtCore5Compat/QTextCodec> +#include <QtCore5Compat/QTextDecoder> +#include <QtCore5Compat/QTextEncoder> +#else #include <QtCore/QTextCodec> #include <QtCore/QTextDecoder> #include <QtCore/QTextEncoder> +#endif #include <QtCore/QTextStream> #include <QDesktopServices> @@ -138,6 +146,8 @@ using namespace QInstaller; Installation has to be updated. \value EssentialUpdated Installation essential components were updated. + \value NoPackagesFound + No packages found from remote. */ /*! @@ -175,25 +185,6 @@ using namespace QInstaller; Emitted when the new root component \a comp is added. \sa {installer::componentAdded}{installer.componentAdded} - \sa rootComponentsAdded(), updaterComponentsAdded() -*/ - -/*! - \fn QInstaller::PackageManagerCore::rootComponentsAdded(QList<QInstaller::Component*> components) - - Emitted when the list of root components specified by \a components is added. - - \sa {installer::rootComponentsAdded}{installer.rootComponentsAdded} - \sa componentAdded(), updaterComponentsAdded() -*/ - -/*! - \fn QInstaller::PackageManagerCore::updaterComponentsAdded(QList<QInstaller::Component*> components) - - Emitted when a new list of updater components specified by \a components is added. - - \sa {installer::updaterComponentsAdded}{installer.updaterComponentsAdded} - \sa componentAdded(), rootComponentsAdded() */ /*! @@ -213,7 +204,7 @@ using namespace QInstaller; */ /*! - \fn QInstaller::PackageManagerCore::defaultTranslationsLoadedForLanguage(QLocale::Language lang) + \fn QInstaller::PackageManagerCore::defaultTranslationsLoadedForLanguage(QLocale lang) Emitted when the language \a lang has changed. @@ -600,6 +591,14 @@ bool PackageManagerCore::clearLocalCache(QString *error) } /*! + Returns \c true if the metadata cache is initialized and valid, \c false otherwise. +*/ +bool PackageManagerCore::isValidCache() const +{ + return d->m_metadataJob.isValidCache(); +} + +/*! \internal */ template <typename T> @@ -612,6 +611,22 @@ template bool PackageManagerCore::loadComponentScripts<QList<Component *>>(const template bool PackageManagerCore::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool); /*! + Saves the installer \a args user has given when running installer. Command and option arguments + are not saved. +*/ +void PackageManagerCore::saveGivenArguments(const QStringList &args) +{ + m_arguments = args; +} + +/*! + Returns the commands and options user has given when running installer. +*/ +QStringList PackageManagerCore::givenArguments() const +{ + return m_arguments; +} +/*! \deprecated [4.5] Use recalculateAllComponents() instead. \sa {installer::componentsToInstallNeedsRecalculation}{installer.componentsToInstallNeedsRecalculation} @@ -639,6 +654,12 @@ void PackageManagerCore::componentsToInstallNeedsRecalculation() */ bool PackageManagerCore::recalculateAllComponents() { + // Clear previous results first, as the check states are updated + // at the end of both calculate methods, which refer to the results + // from both calculators. Needed to keep the state correct. + d->clearInstallerCalculator(); + d->clearUninstallerCalculator(); + if (!calculateComponentsToInstall()) return false; if (!isInstaller() && !calculateComponentsToUninstall()) @@ -792,12 +813,13 @@ quint64 PackageManagerCore::requiredDiskSpace() const */ quint64 PackageManagerCore::requiredTemporaryDiskSpace() const { - if (isOfflineOnly()) - return 0; - quint64 result = 0; - foreach (QInstaller::Component *component, orderedComponentsToInstall()) + foreach (QInstaller::Component *component, orderedComponentsToInstall()) { + if (!component->isFromOnlineRepository()) + continue; + result += size(component, scCompressedSize); + } return result; } @@ -833,7 +855,7 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize) ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n') + tr("Downloading packages...")); - DownloadArchivesJob archivesJob(this); + DownloadArchivesJob archivesJob(this, QLatin1String("downloadArchiveJob")); archivesJob.setAutoDelete(false); archivesJob.setArchivesToDownload(archivesToDownload); archivesJob.setExpectedTotalSize(archivesToDownloadTotalSize); @@ -1026,8 +1048,7 @@ QString PackageManagerCore::readFile(const QString &filePath, const QString &cod return QString(); QTextStream stream(&f); - stream.setCodec(codec); - return stream.readAll(); + return QString::fromUtf8(codec->fromUnicode(stream.readAll())); } /*! @@ -1516,7 +1537,7 @@ bool PackageManagerCore::fetchLocalPackagesTree() continue; } - QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this)); + std::unique_ptr<QInstaller::Component> component(new QInstaller::Component(this)); component->loadDataFromPackage(package); QString name = component->treeName(); if (components.contains(name)) { @@ -1542,7 +1563,7 @@ bool PackageManagerCore::fetchLocalPackagesTree() if (!treeName.isEmpty()) treeNameComponents.insert(component->name(), treeName); - components.insert(name, component.take()); + components.insert(name, component.release()); } // Second pass with leftover packages if (firstRun) @@ -1587,10 +1608,11 @@ void PackageManagerCore::networkSettingsChanged() cancelMetaInfoJob(); d->m_updates = false; + d->m_aliases = false; d->m_repoFetched = false; d->m_updateSourcesAdded = false; - if (isMaintainer() ) { + if (!isInstaller()) { bool gainedAdminRights = false; if (!directoryWritable(d->targetDir())) { gainAdminRights(); @@ -1663,11 +1685,42 @@ bool PackageManagerCore::fetchCompressedPackagesTree() return fetchPackagesTree(packages, installedPackages); } +bool PackageManagerCore::fetchPackagesWithFallbackRepositories(const QStringList& components, bool &fallBackReposFetched) +{ + auto checkComponents = [&]() { + if (!fetchRemotePackagesTree(components)) + return false; + return true; + }; + + if (!checkComponents()) { + // error when fetching packages tree + if (status() != NoPackagesFound) + return false; + //retry fetching packages with all categories enabled + fallBackReposFetched = true; + if (!d->enableAllCategories()) + return false; + + qCDebug(QInstaller::lcInstallerInstallLog).noquote() + << "Components not found with the current selection." + << "Searching from additional repositories"; + if (!ProductKeyCheck::instance()->securityWarning().isEmpty()) { + qCWarning(QInstaller::lcInstallerInstallLog) << ProductKeyCheck::instance()->securityWarning(); + } + if (!checkComponents()) { + return false; + } + } + return true; +} + /*! Checks for packages to install. Returns \c true if newer versions exist - and they can be installed. + and they can be installed. Returns \c false if not \a components are found + for install, or if error occurred when fetching and generating package tree. */ -bool PackageManagerCore::fetchRemotePackagesTree() +bool PackageManagerCore::fetchRemotePackagesTree(const QStringList& components) { d->setStatus(Running); @@ -1695,8 +1748,15 @@ bool PackageManagerCore::fetchRemotePackagesTree() return false; const PackagesList &packages = d->remotePackages(); - if (packages.isEmpty()) + if (packages.isEmpty()) { + d->setStatus(PackageManagerCore::NoPackagesFound); + return false; + } + + if (!d->installablePackagesFound(components)) return false; + + d->m_componentsToBeInstalled = components; return fetchPackagesTree(packages, installedPackages); } @@ -1945,6 +2005,82 @@ void PackageManagerCore::setTemporaryRepositories(const QStringList &repositorie settings().setTemporaryRepositories(repositorySet, replace); } +bool PackageManagerCore::addQBspRepositories(const QStringList &repositories) +{ + QSet<Repository> set; + foreach (QString fileName, repositories) { + Repository repository = Repository::fromUserInput(fileName, true); + repository.setEnabled(true); + set.insert(repository); + } + if (set.count() > 0) { + settings().addTemporaryRepositories(set, false); + return true; + } + return false; +} + +bool PackageManagerCore::validRepositoriesAvailable() const +{ + foreach (const Repository &repo, settings().repositories()) { + if (repo.isEnabled() && repo.isValid()) { + return true; + } + } + return false; +} + +void PackageManagerCore::setAllowCompressedRepositoryInstall(bool allow) +{ + d->m_allowCompressedRepositoryInstall = allow; +} + +bool PackageManagerCore::allowCompressedRepositoryInstall() const +{ + return d->m_allowCompressedRepositoryInstall; +} + +bool PackageManagerCore::showRepositoryCategories() const +{ + bool showCagetories = settings().repositoryCategories().count() > 0 && !isOfflineOnly() && !isUpdater(); + if (showCagetories) + settings().setAllowUnstableComponents(true); + return showCagetories; +} + +QVariantMap PackageManagerCore::organizedRepositoryCategories() const +{ + QVariantMap map; + QSet<RepositoryCategory> categories = settings().repositoryCategories(); + foreach (const RepositoryCategory &category, categories) + map.insert(category.displayname(), QVariant::fromValue(category)); + return map; +} + +void PackageManagerCore::enableRepositoryCategory(const QString &repositoryName, bool enable) +{ + QMap<QString, RepositoryCategory> organizedRepositoryCategories = settings().organizedRepositoryCategories(); + + QMap<QString, RepositoryCategory>::iterator i = organizedRepositoryCategories.find(repositoryName); + while (i != organizedRepositoryCategories.end() && i.key() == repositoryName) { + d->enableRepositoryCategory(i.value(), enable); + i++; + } +} + +void PackageManagerCore::runProgram() +{ + const QString program = replaceVariables(value(scRunProgram)); + + const QStringList args = replaceVariables(values(scRunProgramArguments)); + if (program.isEmpty()) + return; + + qCDebug(QInstaller::lcInstallerInstallLog) << "starting" << program << args; + QProcess::startDetached(program, args); +} + + /*! Returns the script engine that prepares and runs the component scripts. @@ -2102,6 +2238,15 @@ Component *PackageManagerCore::componentByName(const QString &name) const } /*! + Searches for a component alias matching \a name and returns it. + If no alias matches the name, \c nullptr is returned. +*/ +ComponentAlias *PackageManagerCore::aliasByName(const QString &name) const +{ + return d->m_componentAliases.value(name); +} + +/*! Searches \a components for a component matching \a name and returns it. \a name can also contain a version requirement. For example, \c org.qt-project.sdk.qt returns any component with that name, whereas \c{org.qt-project.sdk.qt->=4.5} requires @@ -2127,6 +2272,18 @@ Component *PackageManagerCore::componentByName(const QString &name, const QList< } /*! + Returns an array of all components currently available. If the repository + metadata have not been fetched yet, the array will be empty. Optionally, a + \a regexp expression can be used to further filter the listed packages. + + \sa {installer::components}{installer.components} + */ +QList<Component *> PackageManagerCore::components(const QString ®exp) const +{ + return components(PackageManagerCore::ComponentType::All, regexp); +} + +/*! Returns \c true if directory specified by \a path is writable by the current user. */ @@ -2164,8 +2321,26 @@ QList<Component *> PackageManagerCore::componentsMarkedForInstallation() const } /*! - Determines which components to install based on the current run mode, including dependencies - and automatic dependencies. Returns \c true on success, \c false otherwise. + Returns a list of component aliases that are marked for installation. + The list can be empty. +*/ +QList<ComponentAlias *> PackageManagerCore::aliasesMarkedForInstallation() const +{ + if (isUpdater()) // Aliases not supported on update at the moment + return QList<ComponentAlias *>(); + + QList<ComponentAlias *> markedForInstallation; + for (auto *alias : qAsConst(d->m_componentAliases)) { + if (alias && alias->isSelected()) + markedForInstallation.append(alias); + } + + return markedForInstallation; +} + +/*! + Determines which components to install based on the current run mode, including component aliases, + dependencies and automatic dependencies. Returns \c true on success, \c false otherwise. The aboutCalculateComponentsToInstall() signal is emitted before the calculation starts, the finishedCalculateComponentsToInstall() @@ -2179,15 +2354,13 @@ bool PackageManagerCore::calculateComponentsToInstall() const emit aboutCalculateComponentsToInstall(); d->clearInstallerCalculator(); - const QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation(); - const bool componentsToInstallCalculated = - d->installerCalculator()->solve(selectedComponentsToInstall); + const bool calculated = d->installerCalculator()->solve(); d->updateComponentInstallActions(); emit finishedCalculateComponentsToInstall(); - return componentsToInstallCalculated; + return calculated; } /*! @@ -2455,12 +2628,13 @@ ComponentModel *PackageManagerCore::defaultComponentModel() const if (!d->m_defaultModel) { d->m_defaultModel = componentModel(const_cast<PackageManagerCore*> (this), QLatin1String("AllComponentsModel")); + + connect(this, &PackageManagerCore::startAllComponentsReset, [&] { + d->m_defaultModel->reset(); + }); + connect(this, &PackageManagerCore::finishAllComponentsReset, d->m_defaultModel, + &ComponentModel::reset); } - connect(this, &PackageManagerCore::startAllComponentsReset, [&] { - d->m_defaultModel->reset(); - }); - connect(this, &PackageManagerCore::finishAllComponentsReset, d->m_defaultModel, - &ComponentModel::reset); return d->m_defaultModel; } @@ -2473,44 +2647,70 @@ ComponentModel *PackageManagerCore::updaterComponentModel() const if (!d->m_updaterModel) { d->m_updaterModel = componentModel(const_cast<PackageManagerCore*> (this), QLatin1String("UpdaterComponentsModel")); + + connect(this, &PackageManagerCore::startUpdaterComponentsReset, [&] { + d->m_updaterModel->reset(); + }); + connect(this, &PackageManagerCore::finishUpdaterComponentsReset, d->m_updaterModel, + &ComponentModel::reset); } - connect(this, &PackageManagerCore::startUpdaterComponentsReset, [&] { - d->m_updaterModel->reset(); - }); - connect(this, &PackageManagerCore::finishUpdaterComponentsReset, d->m_updaterModel, - &ComponentModel::reset); return d->m_updaterModel; } /*! + Returns the proxy model +*/ + +ComponentSortFilterProxyModel *PackageManagerCore::componentSortFilterProxyModel() +{ + if (!d->m_componentSortFilterProxyModel) { + d->m_componentSortFilterProxyModel = new ComponentSortFilterProxyModel(this); + d->m_componentSortFilterProxyModel->setRecursiveFilteringEnabled(true); + d->m_componentSortFilterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + } + return d->m_componentSortFilterProxyModel; +} + +/*! Lists available packages filtered with \a regexp without GUI. Virtual components are not listed unless set visible. Optionally, a \a filters hash containing package information elements and regular expressions can be used to further filter listed packages. + Returns \c true if matching packages were found, \c false otherwise. + \sa setVirtualComponentsVisible() */ -void PackageManagerCore::listAvailablePackages(const QString ®exp, const QHash<QString, QString> &filters) +bool PackageManagerCore::listAvailablePackages(const QString ®exp, const QHash<QString, QString> &filters) { setPackageViewer(); + d->enableAllCategories(); qCDebug(QInstaller::lcInstallerInstallLog) << "Searching packages with regular expression:" << regexp; ComponentModel *model = defaultComponentModel(); - d->fetchMetaInformationFromRepositories(); + PackagesList packages; + + if (!d->m_updates) { + d->fetchMetaInformationFromRepositories(); + d->addUpdateResourcesFromRepositories(); + + packages = d->remotePackages(); + if (!fetchAllPackages(packages, LocalPackagesMap())) { + qCWarning(QInstaller::lcInstallerInstallLog) + << "There was a problem with loading the package data."; + return false; + } + } else { + // No need to fetch metadata again + packages = d->remotePackages(); + } - d->addUpdateResourcesFromRepositories(); QRegularExpression re(regexp); re.setPatternOptions(QRegularExpression::CaseInsensitiveOption); - const PackagesList &packages = d->remotePackages(); - if (!fetchAllPackages(packages, LocalPackagesMap())) { - qCWarning(QInstaller::lcInstallerInstallLog) - << "There was a problem with loading the package data."; - return; - } PackagesList matchedPackages; - foreach (Package *package, packages) { + foreach (Package *package, qAsConst(packages)) { const QString name = package->data(scName).toString(); Component *component = componentByName(name); if (!component) @@ -2532,10 +2732,56 @@ void PackageManagerCore::listAvailablePackages(const QString ®exp, const QHas matchedPackages.append(package); } } - if (matchedPackages.count() == 0) + if (matchedPackages.count() == 0) { qCDebug(QInstaller::lcInstallerInstallLog) << "No matching packages found."; - else - LoggingHandler::instance().printPackageInformation(matchedPackages, localInstalledPackages()); + return false; + } + + LoggingHandler::instance().printPackageInformation(matchedPackages, localInstalledPackages()); + return true; +} + +/*! + Lists available component aliases filtered with \a regexp without GUI. Virtual + aliases are not listed unless set visible. + + Returns \c true if matching package aliases were found, \c false otherwise. + + \sa setVirtualComponentsVisible() +*/ +bool PackageManagerCore::listAvailableAliases(const QString ®exp) +{ + setPackageViewer(); + d->enableAllCategories(); + qCDebug(QInstaller::lcInstallerInstallLog) + << "Searching aliases with regular expression:" << regexp; + + if (!d->buildComponentAliases()) + return false; + + QRegularExpression re(regexp); + re.setPatternOptions(QRegularExpression::CaseInsensitiveOption); + + QList<ComponentAlias *> matchedAliases; + for (auto *alias : std::as_const(d->m_componentAliases)) { + if (!alias) + continue; + + if (re.match(alias->name()).hasMatch()) { + if (alias->isVirtual() && !virtualComponentsVisible()) + continue; + + matchedAliases.append(alias); + } + } + + if (matchedAliases.isEmpty()) { + qCDebug(QInstaller::lcInstallerInstallLog) << "No matching package aliases found."; + return false; + } + + LoggingHandler::instance().printAliasInformation(matchedAliases); + return true; } bool PackageManagerCore::componentUninstallableFromCommandLine(const QString &componentName) @@ -2549,7 +2795,7 @@ bool PackageManagerCore::componentUninstallableFromCommandLine(const QString &co } ComponentModel *model = defaultComponentModel(); const QModelIndex &idx = model->indexFromComponentName(component->treeName()); - if (model->data(idx, Qt::CheckStateRole) == QVariant::Invalid) { + if (model->data(idx, Qt::CheckStateRole) == QVariant()) { // Component cannot be unselected, check why if (component->forcedInstallation()) { qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace() @@ -2573,25 +2819,50 @@ bool PackageManagerCore::componentUninstallableFromCommandLine(const QString &co /*! \internal - Tries to set \c Qt::CheckStateRole to \c Qt::Checked for given \a components in the - default component model. Returns \c true if \a components contains at least one component + Tries to set \c Qt::CheckStateRole to \c Qt::Checked for given component \a names in the + default component model, and select given aliases in the \c names list. + + Returns \c true if \a names contains at least one component or component alias eligible for installation, otherwise returns \c false. An error message can be retrieved with \a errorMessage. */ -bool PackageManagerCore::checkComponentsForInstallation(const QStringList &components, QString &errorMessage) +bool PackageManagerCore::checkComponentsForInstallation(const QStringList &names, QString &errorMessage, bool &unstableAliasFound, bool fallbackReposFetched) { bool installComponentsFound = false; ComponentModel *model = defaultComponentModel(); - foreach (const QString &name, components) { + foreach (const QString &name, names) { Component *component = componentByName(name); if (!component) { - errorMessage.append(tr("Cannot install %1. Component not found.").arg(name) + QLatin1Char('\n')); + // No such component, check if we have an alias by the name + if (ComponentAlias *alias = aliasByName(name)) { + if (alias->isUnstable()) { + errorMessage.append(tr("Cannot select alias %1. There was a problem loading this alias, " + "so it is marked unstable and cannot be selected.").arg(name) + QLatin1Char('\n')); + unstableAliasFound = true; + setCanceled(); + return false; + } else if (alias->isVirtual()) { + errorMessage.append(tr("Cannot select %1. Alias is marked virtual, meaning it cannot " + "be selected manually.").arg(name) + QLatin1Char('\n')); + continue; + } else if (alias->missingOptionalComponents() && !fallbackReposFetched) { + unstableAliasFound = true; + setCanceled(); + return false; + } + + alias->setSelected(true); + installComponentsFound = true; + } else { + errorMessage.append(tr("Cannot install %1. Component not found.").arg(name) + QLatin1Char('\n')); + } + continue; } const QModelIndex &idx = model->indexFromComponentName(component->treeName()); if (idx.isValid()) { - if ((model->data(idx, Qt::CheckStateRole) == QVariant::Invalid) && !component->forcedInstallation()) { + if ((model->data(idx, Qt::CheckStateRole) == QVariant()) && !component->forcedInstallation()) { // User cannot select the component, check why if (component->autoDependencies().count() > 0) { errorMessage.append(tr("Cannot install component %1. Component is installed only as automatic " @@ -2661,6 +2932,25 @@ void PackageManagerCore::listInstalledPackages(const QString ®exp) LoggingHandler::instance().printLocalPackageInformation(packages); } +PackageManagerCore::Status PackageManagerCore::searchAvailableUpdates() +{ + setUpdater(); + d->enableAllCategories(); + if (!fetchRemotePackagesTree()) { + qCWarning(QInstaller::lcInstallerInstallLog) << error(); + return status(); + } + + const QList<QInstaller::Component *> availableUpdates = + components(QInstaller::PackageManagerCore::ComponentType::Root); + if (availableUpdates.isEmpty()) { + qCWarning(QInstaller::lcInstallerInstallLog) << "There are currently no updates available."; + return status(); + } + QInstaller::LoggingHandler::instance().printUpdateInformation(availableUpdates); + return status(); +} + /*! Updates the selected components \a componentsToUpdate without GUI. If essential components are found, then only those will be updated. @@ -2668,13 +2958,25 @@ void PackageManagerCore::listInstalledPackages(const QString ®exp) */ PackageManagerCore::Status PackageManagerCore::updateComponentsSilently(const QStringList &componentsToUpdate) { - if (d->runningProcessesFound()) - throw Error(tr("Running processes found.")); setUpdater(); ComponentModel *model = updaterComponentModel(); - fetchRemotePackagesTree(); + if (componentsToUpdate.isEmpty()) { + d->enableAllCategories(); + fetchRemotePackagesTree(); + } else { + bool fallbackReposFetched = false; + bool packagesFound = fetchPackagesWithFallbackRepositories(componentsToUpdate, fallbackReposFetched); + + if (!packagesFound) { + qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() + << "No components available for update with the current selection."; + d->setStatus(Canceled); + return status(); + } + } + // List contains components containing update, if essential found contains only essential component const QList<QInstaller::Component*> componentList = componentsMarkedForInstallation(); @@ -2780,6 +3082,20 @@ void PackageManagerCore::addLicenseItem(const QHash<QString, QVariantMap> &licen } } +bool PackageManagerCore::hasLicenses() const +{ + foreach (Component* component, orderedComponentsToInstall()) { + if (isMaintainer() && component->isInstalled()) + continue; // package manager or updater, hide as long as the component is installed + + // The component is about to be installed and provides a license, so the page needs to + // be shown. + if (!component->licenses().isEmpty()) + return true; + } + return false; +} + /*! * Adds \a component local \a dependencies to a hash table for quicker search for * uninstall dependency components. @@ -2804,9 +3120,6 @@ void PackageManagerCore::createAutoDependencyHash(const QString &component, cons */ PackageManagerCore::Status PackageManagerCore::uninstallComponentsSilently(const QStringList& components) { - if (d->runningProcessesFound()) - throw Error(tr("Running processes found.")); - if (components.isEmpty()) { qCDebug(QInstaller::lcInstallerInstallLog) << "No components selected for uninstallation."; return PackageManagerCore::Canceled; @@ -2845,8 +3158,6 @@ PackageManagerCore::Status PackageManagerCore::uninstallComponentsSilently(const PackageManagerCore::Status PackageManagerCore::removeInstallationSilently() { setCompleteUninstallation(true); - if (d->runningProcessesFound()) - throw Error(tr("Running processes found.")); qCDebug(QInstaller::lcInstallerInstallLog) << "Complete uninstallation was chosen."; if (!(d->m_autoConfirmCommand || d->askUserConfirmCommand())) { @@ -2868,23 +3179,7 @@ PackageManagerCore::Status PackageManagerCore::removeInstallationSilently() PackageManagerCore::Status PackageManagerCore::createOfflineInstaller(const QStringList &componentsToAdd) { setOfflineGenerator(); - // init default model before fetching remote packages tree - ComponentModel *model = defaultComponentModel(); - Q_UNUSED(model); - if (!fetchRemotePackagesTree()) - return status(); - - QString errorMessage; - if (checkComponentsForInstallation(componentsToAdd, errorMessage)) { - if (d->calculateComponentsAndRun()) { - qCDebug(QInstaller::lcInstallerInstallLog) - << "Created installer to:" << offlineBinaryName(); - } - } else { - qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage - << "\nNo components available with the current selection."; - } - return status(); + return d->fetchComponentsAndInstall(componentsToAdd); } /*! @@ -2896,9 +3191,6 @@ PackageManagerCore::Status PackageManagerCore::createOfflineInstaller(const QStr PackageManagerCore::Status PackageManagerCore::installSelectedComponentsSilently(const QStringList& components) { if (!isInstaller()) { - // Check if there are processes running in the install if maintenancetool is used. - if (d->runningProcessesFound()) - throw Error(tr("Running processes found.")); setPackageManager(); //Check that packages are not already installed @@ -2911,24 +3203,7 @@ PackageManagerCore::Status PackageManagerCore::installSelectedComponentsSilently return PackageManagerCore::Canceled; } } - - // init default model before fetching remote packages tree - ComponentModel *model = defaultComponentModel(); - Q_UNUSED(model); - if (!fetchRemotePackagesTree()) - return status(); - - QString errorMessage; - if (checkComponentsForInstallation(components, errorMessage)) { - if (!errorMessage.isEmpty()) - qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage; - if (d->calculateComponentsAndRun()) - qCDebug(QInstaller::lcInstallerInstallLog) << "Components installed successfully"; - } else { - qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage - << "\nNo components available for installation with the current selection."; - } - return status(); + return d->fetchComponentsAndInstall(components); } /*! @@ -2994,6 +3269,21 @@ void PackageManagerCore::dropAdminRights() } /*! + Returns \c true if the installer has admin rights. For example, if the installer + was started with a root account, or the internal admin rights elevation is active. + + Returns \c false otherwise. + + \sa {installer::hasAdminRights}{installer.hasAdminRights} + \sa gainAdminRights() + \sa dropAdminRights() +*/ +bool PackageManagerCore::hasAdminRights() const +{ + return AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive(); +} + +/*! Sets checkAvailableSpace based on value of \a check. */ void PackageManagerCore::setCheckAvailableSpace(bool check) @@ -3002,12 +3292,22 @@ void PackageManagerCore::setCheckAvailableSpace(bool check) } /*! - Checks available disk space if the feature is not explicitly disabled. Informative - text about space status can be retrieved by passing \a message parameter. Returns + * Returns informative text about disk space status + */ +QString PackageManagerCore::availableSpaceMessage() const +{ + return m_availableSpaceMessage; +} + +/*! + Checks available disk space if the feature is not explicitly disabled. Returns \c true if there is sufficient free space on installation and temporary volumes. + + \sa availableSpaceMessage() */ -bool PackageManagerCore::checkAvailableSpace(QString &message) const +bool PackageManagerCore::checkAvailableSpace() { + m_availableSpaceMessage.clear(); const quint64 extraSpace = 256 * 1024 * 1024LL; quint64 required(requiredDiskSpace()); quint64 tempRequired(requiredTemporaryDiskSpace()); @@ -3063,21 +3363,21 @@ bool PackageManagerCore::checkAvailableSpace(QString &message) const } if (cacheOnSameVolume && (installVolumeAvailableSize <= (required + tempRequired))) { - message = tr("Not enough disk space to store temporary files and the " + m_availableSpaceMessage = tr("Not enough disk space to store temporary files and the " "installation. %1 are available, while the minimum required is %2.").arg( humanReadableSize(installVolumeAvailableSize), humanReadableSize(required + tempRequired)); return false; } if (installVolumeAvailableSize < required) { - message = tr("Not enough disk space to store all selected components! %1 are " + m_availableSpaceMessage = tr("Not enough disk space to store all selected components! %1 are " "available, while the minimum required is %2.").arg(humanReadableSize(installVolumeAvailableSize), humanReadableSize(required)); return false; } if (cacheVolumeAvailableSize < tempRequired) { - message = tr("Not enough disk space to store temporary files! %1 are available, " + m_availableSpaceMessage = tr("Not enough disk space to store temporary files! %1 are available, " "while the minimum required is %2. You may select another location for the " "temporary files by modifying the local cache path from the installer settings.") .arg(humanReadableSize(cacheVolumeAvailableSize), humanReadableSize(tempRequired)); @@ -3086,22 +3386,25 @@ bool PackageManagerCore::checkAvailableSpace(QString &message) const if (installVolumeAvailableSize - required < 0.01 * targetVolume.size()) { // warn for less than 1% of the volume's space being free - message = tr("The volume you selected for installation seems to have sufficient space for " + m_availableSpaceMessage = tr("The volume you selected for installation seems to have sufficient space for " "installation, but there will be less than 1% of the volume's space available afterwards."); } else if (installVolumeAvailableSize - required < 100 * 1024 * 1024LL) { // warn for less than 100MB being free - message = tr("The volume you selected for installation seems to have sufficient " + m_availableSpaceMessage = tr("The volume you selected for installation seems to have sufficient " "space for installation, but there will be less than 100 MB available afterwards."); } #ifdef Q_OS_WIN if (isOfflineGenerator() && (required > UINT_MAX)) { - message = tr("The estimated installer size %1 would exceed the supported executable " + m_availableSpaceMessage = tr("The estimated installer size %1 would exceed the supported executable " "size limit of %2. The application may not be able to run.") .arg(humanReadableSize(required), humanReadableSize(UINT_MAX)); } #endif } - message = QString::fromLatin1("%1 %2").arg(message, tr("Installation will use %1 of disk space.") + m_availableSpaceMessage = QString::fromLatin1("%1 %2").arg(m_availableSpaceMessage, + (isOfflineGenerator() + ? tr("Created installer will use %1 of disk space.") + : tr("Installation will use %1 of disk space.")) .arg(humanReadableSize(requiredDiskSpace()))).simplified(); return true; @@ -3160,6 +3463,10 @@ bool PackageManagerCore::killProcess(const QString &absoluteFilePath) const } /*! + \deprecated [4.6] Maintenance tool no longer automatically checks for all running processes + in the installation directory for CLI runs. To manually check for a process to stop, use + \l {component::addStopProcessForUpdateRequest}{component.addStopProcessForUpdateRequest} instead. + Sets additional \a processes that can run when updating with the maintenance tool. @@ -3171,6 +3478,10 @@ void PackageManagerCore::setAllowedRunningProcesses(const QStringList &processes } /*! + \deprecated [4.6] Maintenance tool no longer automatically checks for all running processes + in the installation directory for CLI runs. To manually check for a process to stop, use + \l {component::addStopProcessForUpdateRequest}{component.addStopProcessForUpdateRequest} instead. + Returns processes that are allowed to run when updating with the maintenance tool. @@ -3686,6 +3997,14 @@ QString PackageManagerCore::offlineBinaryName() const } /*! + Add new \a source for looking component aliases. +*/ +void PackageManagerCore::addAliasSource(const AliasSource &source) +{ + d->m_aliasSources.insert(source); +} + +/*! \sa {installer::setInstaller}{installer.setInstaller} \sa isInstaller(), setUpdater(), setPackageManager() */ @@ -3787,6 +4106,7 @@ bool PackageManagerCore::isPackageManager() const void PackageManagerCore::setOfflineGenerator() { d->m_magicMarkerSupplement = BinaryContent::OfflineGenerator; + emit installerBinaryMarkerChanged(d->m_magicBinaryMarker); } /*! @@ -3805,6 +4125,7 @@ bool PackageManagerCore::isOfflineGenerator() const void PackageManagerCore::setPackageViewer() { d->m_magicMarkerSupplement = BinaryContent::PackageViewer; + emit installerBinaryMarkerChanged(d->m_magicBinaryMarker); } /*! @@ -3818,6 +4139,17 @@ bool PackageManagerCore::isPackageViewer() const } /*! + Resets the binary marker supplement of the installer to \c Default. + The supplement enables or disables additional features on top of the binary + marker state (\c Installer, \c Updater, \c PackageManager, \c Uninstaller). +*/ +void PackageManagerCore::resetBinaryMarkerSupplement() +{ + d->m_magicMarkerSupplement = BinaryContent::Default; + emit installerBinaryMarkerChanged(d->m_magicBinaryMarker); +} + +/*! Sets the installer magic binary marker based on \a magicMarker and userSetBinaryMarker to \c true. */ @@ -3992,13 +4324,6 @@ bool PackageManagerCore::updateComponentData(struct Data &data, Component *compo component->setUninstalled(); const QString localPath = component->localTempPath(); - if (LoggingHandler::instance().verboseLevel() == LoggingHandler::Detailed) { - static QString lastLocalPath; - if (lastLocalPath != localPath) - qCDebug(QInstaller::lcDeveloperBuild()) << "Url is:" << localPath; - lastLocalPath = localPath; - } - const Repository repo = d->m_metadataJob.repositoryForCacheDirectory(localPath); if (repo.isValid()) { @@ -4130,10 +4455,10 @@ bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const Loc continue; } - QScopedPointer<QInstaller::Component> remoteComponent(new QInstaller::Component(this)); + std::unique_ptr<QInstaller::Component> remoteComponent(new QInstaller::Component(this)); data.package = package; remoteComponent->loadDataFromPackage(*package); - if (updateComponentData(data, remoteComponent.data())) { + if (updateComponentData(data, remoteComponent.get())) { // Create a list where is name and treename. Repo can contain a package with // a different treename of component which is already installed. We don't want // to move already installed local packages. @@ -4141,7 +4466,7 @@ bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const Loc if (!treeName.isEmpty()) remoteTreeNameComponents.insert(remoteComponent->name(), treeName); const QString name = remoteComponent->treeName(); - allComponents.insert(name, remoteComponent.take()); + allComponents.insert(name, remoteComponent.release()); } } // Second pass with leftover packages @@ -4164,7 +4489,7 @@ bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const Loc d->m_localVirtualComponents.append(package.name); } - QScopedPointer<QInstaller::Component> localComponent(new QInstaller::Component(this)); + std::unique_ptr<QInstaller::Component> localComponent(new QInstaller::Component(this)); localComponent->loadDataFromPackage(package); const QString name = localComponent->treeName(); @@ -4219,7 +4544,7 @@ bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const Loc const QString treeName = localComponent->value(scTreeName); if (!treeName.isEmpty()) allTreeNameComponents.insert(localComponent->name(), treeName); - allComponents.insert(name, localComponent.take()); + allComponents.insert(name, localComponent.release()); } // store all components that got a replacement @@ -4233,6 +4558,9 @@ bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const Loc d->commitPendingUnstableComponents(); + if (!d->buildComponentAliases()) + return false; + } catch (const Error &error) { d->clearAllComponentLists(); d->setStatus(PackageManagerCore::Failure, error.message()); @@ -4270,12 +4598,12 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const if (!ProductKeyCheck::instance()->isValidPackage(update->data(scName).toString())) continue; - QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this)); + std::unique_ptr<QInstaller::Component> component(new QInstaller::Component(this)); data.package = update; component->loadDataFromPackage(*update); - if (updateComponentData(data, component.data())) { + if (updateComponentData(data, component.get())) { // Keep a reference so we can resolve dependencies during update. - d->m_updaterComponentsDeps.append(component.take()); + d->m_updaterComponentsDeps.append(component.release()); // const QString isNew = update->data(scNewComponent).toString(); // if (isNew.toLower() != scTrue) diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h index e5236112e..d9da87d85 100644 --- a/src/libs/installer/packagemanagercore.h +++ b/src/libs/installer/packagemanagercore.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2023 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -29,6 +29,7 @@ #define PACKAGEMANAGERCORE_H #include "binaryformat.h" +#include "binarycontent.h" #include "component.h" #include "protocol.h" #include "repository.h" @@ -45,11 +46,14 @@ namespace QInstaller { +struct AliasSource; class ComponentModel; +class ComponentAlias; class ScriptEngine; class PackageManagerCorePrivate; class PackageManagerProxyFactory; class Settings; +class ComponentSortFilterProxyModel; // -- PackageManagerCore @@ -80,7 +84,8 @@ public: Canceled = 3, Unfinished = 4, ForceUpdate = 5, - EssentialUpdated = 6 + EssentialUpdated = 6, + NoPackagesFound = 7 }; Status status() const; QString error() const; @@ -145,8 +150,9 @@ public: void setProxyFactory(PackageManagerProxyFactory *factory); PackagesList remotePackages(); - bool fetchRemotePackagesTree(); + bool fetchRemotePackagesTree(const QStringList& components = QStringList()); bool fetchCompressedPackagesTree(); + bool fetchPackagesWithFallbackRepositories(const QStringList& components, bool &fallBackReposFetched); bool run(); void reset(); @@ -202,9 +208,20 @@ public: void setOfflineBinaryName(const QString &name); QString offlineBinaryName() const; + void addAliasSource(const AliasSource &source); + Q_INVOKABLE void addUserRepositories(const QStringList &repositories); Q_INVOKABLE void setTemporaryRepositories(const QStringList &repositories, bool replace = false, bool compressed = false); + bool addQBspRepositories(const QStringList &repositories); + bool validRepositoriesAvailable() const; + Q_INVOKABLE void setAllowCompressedRepositoryInstall(bool allow); + bool allowCompressedRepositoryInstall() const; + bool showRepositoryCategories() const; + QVariantMap organizedRepositoryCategories() const; + void enableRepositoryCategory(const QString &repositoryName, bool enable); + void runProgram(); + Q_INVOKABLE void autoAcceptMessageBoxes(); Q_INVOKABLE void autoRejectMessageBoxes(); Q_INVOKABLE void setMessageBoxAutomaticAnswer(const QString &identifier, int button); @@ -241,7 +258,10 @@ public: void appendUpdaterComponent(Component *components); QList<Component *> components(ComponentTypes mask, const QString ®exp = QString()) const; - Component *componentByName(const QString &identifier) const; + Q_INVOKABLE QInstaller::Component *componentByName(const QString &identifier) const; + Q_INVOKABLE QList<QInstaller::Component *> components(const QString ®exp = QString()) const; + + ComponentAlias *aliasByName(const QString &name) const; Q_INVOKABLE bool calculateComponentsToInstall() const; QList<Component*> orderedComponentsToInstall() const; @@ -252,6 +272,9 @@ public: Q_INVOKABLE bool calculateComponentsToUninstall() const; QList<Component*> componentsToUninstall() const; + QList<Component *> componentsMarkedForInstallation() const; + QList<ComponentAlias *> aliasesMarkedForInstallation() const; + QString componentsToInstallError() const; QString componentsToUninstallError() const; QString installReason(Component *component) const; @@ -263,9 +286,14 @@ public: ComponentModel *defaultComponentModel() const; ComponentModel *updaterComponentModel() const; + ComponentSortFilterProxyModel *componentSortFilterProxyModel(); + void listInstalledPackages(const QString ®exp = QString()); - void listAvailablePackages(const QString ®exp = QString(), + bool listAvailablePackages(const QString ®exp = QString(), const QHash<QString, QString> &filters = QHash<QString, QString>()); + bool listAvailableAliases(const QString ®exp = QString()); + + PackageManagerCore::Status searchAvailableUpdates(); PackageManagerCore::Status updateComponentsSilently(const QStringList &componentsToUpdate); PackageManagerCore::Status installSelectedComponentsSilently(const QStringList& components); PackageManagerCore::Status installDefaultComponentsSilently(); @@ -293,6 +321,8 @@ public: void setPackageViewer(); Q_INVOKABLE bool isPackageViewer() const; + void resetBinaryMarkerSupplement(); + void setUserSetBinaryMarker(qint64 magicMarker); Q_INVOKABLE bool isUserSetBinaryMarker() const; @@ -307,9 +337,11 @@ public: Q_INVOKABLE bool gainAdminRights(); Q_INVOKABLE void dropAdminRights(); + Q_INVOKABLE bool hasAdminRights() const; void setCheckAvailableSpace(bool check); - bool checkAvailableSpace(QString &message) const; + bool checkAvailableSpace(); + QString availableSpaceMessage() const; Q_INVOKABLE quint64 requiredDiskSpace() const; Q_INVOKABLE quint64 requiredTemporaryDiskSpace() const; @@ -353,14 +385,20 @@ public: void clearLicenses(); QHash<QString, QMap<QString, QString>> sortedLicenses(); void addLicenseItem(const QHash<QString, QVariantMap> &licenses); + bool hasLicenses() const; void createLocalDependencyHash(const QString &component, const QString &dependencies) const; void createAutoDependencyHash(const QString &component, const QString &oldDependencies, const QString &newDependencies) const; bool resetLocalCache(bool init = false); bool clearLocalCache(QString *error = nullptr); + bool isValidCache() const; + template <typename T> bool loadComponentScripts(const T &components, const bool postScript = false); + void saveGivenArguments(const QStringList &args); + QStringList givenArguments() const; + public Q_SLOTS: bool runInstaller(); bool runUninstaller(); @@ -380,11 +418,9 @@ Q_SIGNALS: void aboutCalculateComponentsToUninstall() const; void finishedCalculateComponentsToUninstall() const; void componentAdded(QInstaller::Component *comp); - void rootComponentsAdded(QList<QInstaller::Component*> components); - void updaterComponentsAdded(QList<QInstaller::Component*> components); void valueChanged(const QString &key, const QString &value); void statusChanged(QInstaller::PackageManagerCore::Status); - void defaultTranslationsLoadedForLanguage(QLocale::Language lang); + void defaultTranslationsLoadedForLanguage(QLocale lang); void currentPageChanged(int page); void finishButtonClicked(); @@ -447,17 +483,18 @@ private: QString findDisplayVersion(const QString &componentName, const QHash<QString, QInstaller::Component*> &components, const QString& versionKey, QHash<QString, bool> &visited); ComponentModel *componentModel(PackageManagerCore *core, const QString &objectName) const; - QList<Component *> componentsMarkedForInstallation() const; bool fetchPackagesTree(const PackagesList &packages, const LocalPackagesMap installedPackages); bool componentUninstallableFromCommandLine(const QString &componentName); - bool checkComponentsForInstallation(const QStringList &components, QString &errorMessage); + bool checkComponentsForInstallation(const QStringList &names, QString &errorMessage, bool &unstableAliasFound, bool fallbackReposFetched); private: PackageManagerCorePrivate *const d; friend class PackageManagerCorePrivate; QHash<QString, QString> m_fileDialogAutomaticAnswers; QHash<QString, QStringList> m_localVirtualWithDependants; + QString m_availableSpaceMessage; + QStringList m_arguments; private: // remove once we deprecate isSelected, setSelected etc... diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index 259aafeb6..0ed4bf6d8 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2023 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -45,6 +45,7 @@ #include "qsettingswrapper.h" #include "installercalculator.h" #include "uninstallercalculator.h" +#include "componentalias.h" #include "componentchecker.h" #include "globals.h" #include "binarycreator.h" @@ -56,11 +57,13 @@ #include "selfrestarter.h" #include "filedownloaderfactory.h" #include "updateoperationfactory.h" +#include "constants.h" #include <productkeycheck.h> #include <QSettings> #include <QtConcurrentRun> +#include <QtConcurrent> #include <QtCore/QCoreApplication> #include <QtCore/QDir> #include <QtCore/QDirIterator> @@ -120,56 +123,67 @@ static QStringList checkRunningProcessesFromList(const QStringList &processList) return stillRunningProcesses; } -static void deferredRename(const QString &oldName, const QString &newName, bool restart = false) +static bool filterMissingAliasesToInstall(const QString& component, const QList<ComponentAlias *> packages) { -#ifdef Q_OS_WIN - const QString currentExecutable = QCoreApplication::applicationFilePath(); - const QString tmpExecutable = generateTemporaryFileName(currentExecutable); - - QFile::rename(currentExecutable, tmpExecutable); - QFile::rename(oldName, newName); - - QStringList arguments; - if (restart) { - // Restart with same command line arguments as first executable - arguments = QCoreApplication::arguments(); - arguments.removeFirst(); // Remove program name - arguments.prepend(tmpExecutable); - arguments.prepend(QLatin1String("--") - + CommandLineOptions::scCleanupUpdate); - } else { - arguments.append(QLatin1String("--") - + CommandLineOptions::scCleanupUpdateOnly); - arguments.append(tmpExecutable); + bool packageFound = false; + for (qsizetype i = 0; i < packages.size(); ++i) { + packageFound = (packages.at(i)->name() == component); + if (packageFound) + break; } - QProcessWrapper::startDetached2(newName, arguments); + return !packageFound; +} -#else - QFile::remove(newName); - QFile::rename(oldName, newName); - SelfRestarter::setRestartOnQuit(restart); -#endif +static bool filterMissingPackagesToInstall(const QString& component, const PackagesList& packages) +{ + bool packageFound = false; + for (qsizetype i = 0; i < packages.size(); ++i) { + packageFound = (packages.at(i)->data(scName).toString() == component); + if (packageFound) + break; + } + return !packageFound; } +static QString getAppBundlePath() { + QString appDirPath = QCoreApplication::applicationDirPath(); + QDir dir(appDirPath); + while (!dir.isRoot()) { + if (dir.dirName().endsWith(QLatin1String(".app"))) + return dir.absolutePath(); + dir.cdUp(); + } + return QString(); +} // -- PackageManagerCorePrivate PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) : m_updateFinder(nullptr) + , m_aliasFinder(nullptr) , m_localPackageHub(std::make_shared<LocalPackageHub>()) , m_status(PackageManagerCore::Unfinished) , m_needsHardRestart(false) , m_testChecksum(false) , m_launchedAsRoot(AdminAuthorization::hasAdminRights()) + , m_commandLineInstance(false) + , m_defaultInstall(false) + , m_userSetBinaryMarker(false) + , m_checkAvailableSpace(true) , m_completeUninstall(false) , m_needToWriteMaintenanceTool(false) , m_dependsOnLocalInstallerBinary(false) + , m_autoAcceptLicenses(false) + , m_disableWriteMaintenanceTool(false) + , m_autoConfirmCommand(false) , m_core(core) , m_updates(false) + , m_aliases(false) , m_repoFetched(false) , m_updateSourcesAdded(false) , m_magicBinaryMarker(0) // initialize with pseudo marker , m_magicMarkerSupplement(BinaryContent::Default) + , m_foundEssentialUpdate(false) , m_componentScriptEngine(nullptr) , m_controlScriptEngine(nullptr) , m_installerCalculator(nullptr) @@ -177,37 +191,46 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) , m_proxyFactory(nullptr) , m_defaultModel(nullptr) , m_updaterModel(nullptr) + , m_componentSortFilterProxyModel(nullptr) , m_guiObject(nullptr) , m_remoteFileEngineHandler(nullptr) - , m_foundEssentialUpdate(false) - , m_commandLineInstance(false) - , m_defaultInstall(false) - , m_userSetBinaryMarker(false) - , m_checkAvailableSpace(true) - , m_autoAcceptLicenses(false) - , m_disableWriteMaintenanceTool(false) - , m_autoConfirmCommand(false) , m_datFileName(QString()) +#ifdef INSTALLCOMPRESSED + , m_allowCompressedRepositoryInstall(true) +#else + , m_allowCompressedRepositoryInstall(false) +#endif + , m_connectedOperations(0) { } PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker, const QList<OperationBlob> &performedOperations, const QString &datFileName) : m_updateFinder(nullptr) + , m_aliasFinder(nullptr) , m_localPackageHub(std::make_shared<LocalPackageHub>()) , m_status(PackageManagerCore::Unfinished) , m_needsHardRestart(false) , m_testChecksum(false) , m_launchedAsRoot(AdminAuthorization::hasAdminRights()) + , m_commandLineInstance(false) + , m_defaultInstall(false) + , m_userSetBinaryMarker(false) + , m_checkAvailableSpace(true) , m_completeUninstall(false) , m_needToWriteMaintenanceTool(false) , m_dependsOnLocalInstallerBinary(false) + , m_autoAcceptLicenses(false) + , m_disableWriteMaintenanceTool(false) + , m_autoConfirmCommand(false) , m_core(core) , m_updates(false) + , m_aliases(false) , m_repoFetched(false) , m_updateSourcesAdded(false) , m_magicBinaryMarker(magicInstallerMaker) , m_magicMarkerSupplement(BinaryContent::Default) + , m_foundEssentialUpdate(false) , m_componentScriptEngine(nullptr) , m_controlScriptEngine(nullptr) , m_installerCalculator(nullptr) @@ -215,22 +238,21 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q , m_proxyFactory(nullptr) , m_defaultModel(nullptr) , m_updaterModel(nullptr) + , m_componentSortFilterProxyModel(nullptr) , m_guiObject(nullptr) , m_remoteFileEngineHandler(new RemoteFileEngineHandler) - , m_foundEssentialUpdate(false) - , m_commandLineInstance(false) - , m_defaultInstall(false) - , m_userSetBinaryMarker(false) - , m_checkAvailableSpace(true) - , m_autoAcceptLicenses(false) - , m_disableWriteMaintenanceTool(false) - , m_autoConfirmCommand(false) , m_datFileName(datFileName) +#ifdef INSTALLCOMPRESSED + , m_allowCompressedRepositoryInstall(true) +#else + , m_allowCompressedRepositoryInstall(false) +#endif + , m_connectedOperations(0) { foreach (const OperationBlob &operation, performedOperations) { - QScopedPointer<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance() + std::unique_ptr<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance() .create(operation.name, core)); - if (op.isNull()) { + if (!op) { qCWarning(QInstaller::lcInstallerInstallLog) << "Failed to load unknown operation" << operation.name; continue; @@ -241,7 +263,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q << operation.name; continue; } - m_performedOperationsOld.append(op.take()); + m_performedOperationsOld.append(op.release()); } connect(this, &PackageManagerCorePrivate::installationStarted, @@ -270,6 +292,7 @@ PackageManagerCorePrivate::~PackageManagerCorePrivate() qDeleteAll(m_performedOperationsCurrentSession); delete m_updateFinder; + delete m_aliasFinder; delete m_proxyFactory; delete m_defaultModel; @@ -430,6 +453,124 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c return true; } +bool PackageManagerCorePrivate::buildComponentAliases() +{ + // For now, aliases are only used for command line runs + if (!m_core->isCommandLineInstance()) + return true; + + { + const QList<ComponentAlias *> aliasList = componentAliases(); + if (aliasList.isEmpty()) + return true; + + for (const auto *alias : aliasList) { + // Create a new alias object for package manager core to take ownership of + ComponentAlias *newAlias = new ComponentAlias(m_core); + for (const QString &key : alias->keys()) + newAlias->setValue(key, alias->value(key)); + + m_componentAliases.insert(alias->name(), newAlias); + } + } + + if (m_core->isPackageViewer()) + return true; + // After aliases are loaded, perform sanity checks: + + // 1. Component check state is changed by alias selection, so store the initial state + storeCheckState(); + + QStringList aliasNamesSelectedForInstall; + + // 2. Get a list of alias names to be installed, dependency aliases needs to be listed first + // to get proper unstable state for parents + std::function<void(QStringList)> fetchAliases = [&](QStringList aliases) { + for (const QString &aliasName : aliases) { + ComponentAlias *alias = m_componentAliases.value(aliasName); + if (!alias || aliasNamesSelectedForInstall.contains(aliasName)) + continue; + if (!aliasNamesSelectedForInstall.contains(aliasName)) + aliasNamesSelectedForInstall.prepend(aliasName); + fetchAliases(QStringList() << QInstaller::splitStringWithComma(alias->value(scRequiredAliases)) + << QInstaller::splitStringWithComma(alias->value(scOptionalAliases))); + } + }; + for (const QString &installComponent : m_componentsToBeInstalled) { + ComponentAlias *alias = m_componentAliases.value(installComponent); + if (!alias) + continue; + if (!aliasNamesSelectedForInstall.contains(installComponent)) + aliasNamesSelectedForInstall.prepend(installComponent); + fetchAliases(QStringList() << QInstaller::splitStringWithComma(alias->value(scRequiredAliases)) + << QInstaller::splitStringWithComma(alias->value(scOptionalAliases))); + } + + Graph<QString> aliasGraph; + QList<ComponentAlias *> aliasesSelectedForInstall; + for (auto &aliasName : std::as_const(aliasNamesSelectedForInstall)) { + ComponentAlias *alias = m_componentAliases.value(aliasName); + if (!alias) + continue; + aliasGraph.addNode(alias->name()); + aliasGraph.addEdges(alias->name(), + QInstaller::splitStringWithComma(alias->value(scRequiredAliases)) << + QInstaller::splitStringWithComma(alias->value(scOptionalAliases))); + + if (!m_core->componentByName(alias->name())) { + // Name ok, select for sanity check calculation + alias->setSelected(true); + } else { + alias->setUnstable(ComponentAlias::ComponentNameConfict, + tr("Alias declares name that conflicts with an existing component \"%1\"") + .arg(alias->name())); + } + if (!aliasesSelectedForInstall.contains(alias)) + aliasesSelectedForInstall.append(alias); + } + + const QList<QString> sortedAliases = aliasGraph.sort(); + // 3. Check for cyclic dependency errors + if (aliasGraph.hasCycle()) { + setStatus(PackageManagerCore::Failure, installerCalculator()->error()); + MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"), + tr("Unresolved component aliases"), + tr("Cyclic dependency between aliases \"%1\" and \"%2\" detected.") + .arg(aliasGraph.cycle().first, aliasGraph.cycle().second)); + + return false; + } + + // 4. Test for required aliases and components, this triggers setting the + // alias unstable in case of a broken reference. + for (const auto &aliasName : sortedAliases) { + ComponentAlias *alias = m_componentAliases.value(aliasName); + if (!alias) // sortedAliases may contain dependencies that don't exist, we don't know it yet + continue; + + alias->components(); + alias->aliases(); + } + + clearInstallerCalculator(); + // 5. Check for other errors preventing resolving components to install + if (!installerCalculator()->solve(aliasesSelectedForInstall)) { + setStatus(PackageManagerCore::Failure, installerCalculator()->error()); + MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"), + tr("Unresolved component aliases"), installerCalculator()->error()); + + return false; + } + + for (auto *alias : std::as_const(m_componentAliases)) + alias->setSelected(false); + + // 6. Restore original state + restoreCheckState(); + + return true; +} + template <typename T> bool PackageManagerCorePrivate::loadComponentScripts(const T &components, const bool postScript) { @@ -468,6 +609,10 @@ void PackageManagerCorePrivate::cleanUpComponentEnvironment() // so we need to remove the current component script engine delete m_componentScriptEngine; m_componentScriptEngine = nullptr; + + // Calculators become invalid after clearing components + clearInstallerCalculator(); + clearUninstallerCalculator(); } ScriptEngine *PackageManagerCorePrivate::componentScriptEngine() const @@ -486,6 +631,9 @@ ScriptEngine *PackageManagerCorePrivate::controlScriptEngine() const void PackageManagerCorePrivate::clearAllComponentLists() { + qDeleteAll(m_componentAliases); + m_componentAliases.clear(); + QList<QInstaller::Component*> toDelete; toDelete << m_rootComponents << m_deletedReplacedComponents; @@ -645,6 +793,10 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> ¶ms if (isInstaller()) m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 1)); + const QString aliasFilePath = m_core->settings().aliasDefinitionsFile(); + if (!aliasFilePath.isEmpty()) + m_aliasSources.insert(AliasSource(AliasSource::SourceFileFormat::Xml, aliasFilePath, -1)); + m_metadataJob.disconnect(); m_metadataJob.setAutoDelete(false); m_metadataJob.setPackageManagerCore(m_core); @@ -773,7 +925,7 @@ QString PackageManagerCorePrivate::maintenanceToolAliasPath() const if (aliasName.isEmpty()) return QString(); - const bool isRoot = (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive()); + const bool isRoot = m_core->hasAdminRights(); const QString applicationsDir = m_core->value( isRoot ? QLatin1String("ApplicationsDir") : QLatin1String("ApplicationsDirUser") ); @@ -864,7 +1016,7 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles() if (key == scRunProgramDescription || key == scRunProgram || key == scRunProgramArguments) continue; QVariant value = m_data.value(key); - if (value.canConvert(QVariant::String)) + if (value.canConvert<QString>()) value = replacePath(value.toString(), targetDir(), QLatin1String(scRelocatable)); variables.insert(key, value); } @@ -888,8 +1040,8 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles() QFile file(targetDir() + QLatin1Char('/') + QLatin1String("network.xml")); if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - QXmlStreamWriter writer(&file); - writer.setCodec("UTF-8"); + QString outputStr; + QXmlStreamWriter writer(&outputStr); writer.setAutoFormatting(true); writer.writeStartDocument(); @@ -922,6 +1074,8 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles() writer.writeEndElement(); writer.writeTextElement(QLatin1String("LocalCachePath"), m_data.settings().localCachePath()); writer.writeEndElement(); + + file.write(outputStr.toUtf8()); } setDefaultFilePermissions(&file, DefaultFilePermissions::NonExecutable); } @@ -1070,8 +1224,11 @@ void PackageManagerCorePrivate::connectOperationToInstaller(Operation *const ope connect(m_core, SIGNAL(installationInterrupted()), operationObject, SLOT(cancelOperation())); if (mo->indexOfSignal(QMetaObject::normalizedSignature("progressChanged(double)")) > -1) { + // create unique object names for progress information track + operationObject->setObjectName(QLatin1String("operation_%1").arg(QString::number(m_connectedOperations))); ProgressCoordinator::instance()->registerPartProgress(operationObject, SIGNAL(progressChanged(double)), operationPartSize); + m_connectedOperations++; } } } @@ -1296,7 +1453,7 @@ void PackageManagerCorePrivate::writeMaintenanceToolAppBundle(OperationList &per const QString after = QLatin1String("<string>") + QFileInfo(maintenanceToolName()).baseName() + QLatin1String("</string>"); while (!in.atEnd()) - out << in.readLine().replace(before, after) << endl; + out << in.readLine().replace(before, after) << Qt::endl; // copy qt_menu.nib if it exists op = createOwnedOperation(QLatin1String("Mkdir")); @@ -1390,6 +1547,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper bool newBinaryWritten = false; QString mtName = maintenanceToolName(); const QString installerBaseBinary = replaceVariables(m_installerBaseBinaryUnreplaced); + bool macOsMTBundleExtracted = false; if (!installerBaseBinary.isEmpty() && QFileInfo::exists(installerBaseBinary)) { qCDebug(QInstaller::lcInstallerInstallLog) << "Got a replacement installer base binary:" << installerBaseBinary; @@ -1397,25 +1555,9 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper // In macOS the installerbase is a whole app bundle. We do not modify the maintenancetool name in app bundle // so that possible signing and notarization will remain. Therefore, the actual maintenance tool name might // differ from the one defined in the settings. - try { - const QString maintenanceToolRenamedName = installerBaseBinary + QLatin1String(".new"); - qCDebug(QInstaller::lcInstallerInstallLog) << "Writing maintenance tool " << maintenanceToolRenamedName; - QInstaller::copyDirectoryContents(installerBaseBinary, maintenanceToolRenamedName); - - newBinaryWritten = true; - mtName = installerBaseBinary; - } catch (const Error &error) { - qCWarning(QInstaller::lcInstallerInstallLog) << error.message(); - } - try { - QInstaller::removeDirectory(installerBaseBinary); - qCDebug(QInstaller::lcInstallerInstallLog) << "Removed installer base binary" - << installerBaseBinary << "after updating the maintenance tool."; - } catch (const Error &error) { - qCDebug(QInstaller::lcInstallerInstallLog) << "Cannot remove installer base binary" - << installerBaseBinary << "after updating the maintenance tool:" - << error.message(); - } + newBinaryWritten = true; + mtName = installerBaseBinary; + macOsMTBundleExtracted = true; } else { writeMaintenanceToolAppBundle(performedOperations); QFile replacementBinary(installerBaseBinary); @@ -1557,7 +1699,9 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper << (restart ? "true." : "false."); if (newBinaryWritten) { - if (isInstaller()) + if (macOsMTBundleExtracted) + deferredRename(mtName, targetDir() + QDir::separator() + fi.fileName(), restart); + else if (isInstaller()) QFile::rename(mtName + QLatin1String(".new"), mtName); else deferredRename(mtName + QLatin1String(".new"), mtName, restart); @@ -1740,7 +1884,7 @@ bool PackageManagerCorePrivate::runInstaller() throw Error(tr("It is not possible to install from network location")); } - if (!adminRightsGained) { + if (!m_core->hasAdminRights()) { foreach (Component *component, componentsToInstall) { if (component->value(scRequiresAdminRights, scFalse) == scFalse) continue; @@ -1774,7 +1918,7 @@ bool PackageManagerCorePrivate::runInstaller() double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount; // Now install the requested components - unpackAndInstallComponents(componentsToInstall, progressOperationSize, adminRightsGained); + unpackAndInstallComponents(componentsToInstall, progressOperationSize); if (m_core->isOfflineOnly() && PackageManagerCore::createLocalRepositoryFromBinary()) { emit m_core->titleMessageChanged(tr("Creating local repository")); @@ -1972,7 +2116,7 @@ bool PackageManagerCorePrivate::runPackageUpdater() } // we did not request admin rights till we found out that a component/ undo needs admin rights - if (updateAdminRights && !adminRightsGained) { + if (updateAdminRights && !m_core->hasAdminRights()) { m_core->gainAdminRights(); m_core->dropAdminRights(); } @@ -1993,7 +2137,7 @@ bool PackageManagerCorePrivate::runPackageUpdater() if (undoOperations.count() > 0) { ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Removing deselected components...")); - runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, true); + runUndoOperations(undoOperations, undoOperationProgressSize, true); } m_performedOperationsOld = nonRevertedOperations; // these are all operations left: those not reverted @@ -2001,7 +2145,7 @@ bool PackageManagerCorePrivate::runPackageUpdater() const double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount; // Now install the requested new components - unpackAndInstallComponents(componentsToInstall, progressOperationSize, adminRightsGained); + unpackAndInstallComponents(componentsToInstall, progressOperationSize); emit m_core->titleMessageChanged(tr("Creating Maintenance Tool")); @@ -2070,7 +2214,7 @@ bool PackageManagerCorePrivate::runUninstaller() } // We did not yet request elevated permissions but they are required. - if (updateAdminRights && !adminRightsGained) { + if (updateAdminRights && !m_core->hasAdminRights()) { m_core->gainAdminRights(); m_core->dropAdminRights(); } @@ -2078,7 +2222,7 @@ bool PackageManagerCorePrivate::runUninstaller() const int uninstallOperationCount = countProgressOperations(undoOperations); const double undoOperationProgressSize = double(1) / double(uninstallOperationCount); - runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, false); + runUndoOperations(undoOperations, undoOperationProgressSize, false); // No operation delete here, as all old undo operations are deleted in the destructor. deleteMaintenanceTool(); // this will also delete the TargetDir on Windows @@ -2087,7 +2231,7 @@ bool PackageManagerCorePrivate::runUninstaller() // If not on Windows, we need to remove TargetDir manually. #ifndef Q_OS_WIN if (QVariant(m_core->value(scRemoveTargetDir)).toBool() && !targetDir().isEmpty()) { - if (updateAdminRights && !adminRightsGained) + if (updateAdminRights && !m_core->hasAdminRights()) adminRightsGained = m_core->gainAdminRights(); removeDirectoryThreaded(targetDir(), true); qCDebug(QInstaller::lcInstallerInstallLog) << "Complete uninstallation was chosen."; @@ -2249,7 +2393,7 @@ bool PackageManagerCorePrivate::runOfflineGenerator() } void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &components, - double progressOperationSize, bool adminRightsGained) + double progressOperationSize) { OperationList unpackOperations; bool becameAdmin = false; @@ -2273,7 +2417,7 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo // There's currently no way to control this on a per-operation basis, so // any op requesting execution as admin means all extracts are done as admin. - if (!adminRightsGained && !becameAdmin && op->value(QLatin1String("admin")).toBool()) + if (!m_core->hasAdminRights() && op->value(QLatin1String("admin")).toBool()) becameAdmin = m_core->gainAdminRights(); } } @@ -2321,7 +2465,7 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo continue; } // Backup may request performing operation as admin - if (!adminRightsGained && !becameAdmin && operation->value(QLatin1String("admin")).toBool()) + if (!m_core->hasAdminRights() && operation->value(QLatin1String("admin")).toBool()) becameAdmin = m_core->gainAdminRights(); } @@ -2390,8 +2534,7 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo ProgressCoordinator::instance()->emitDetailTextChanged(tr("Done")); } -void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize, - bool adminRightsGained) +void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize) { OperationList operations = component->operations(Operation::Install); if (!component->operationsCreatedSuccessfully()) @@ -2412,7 +2555,7 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr // maybe this operations wants us to be admin... bool becameAdmin = false; - if (!adminRightsGained && operation->value(QLatin1String("admin")).toBool()) { + if (!m_core->hasAdminRights() && operation->value(QLatin1String("admin")).toBool()) { becameAdmin = m_core->gainAdminRights(); qCDebug(QInstaller::lcInstallerInstallLog) << operation->name() << "as admin:" << becameAdmin; } @@ -2501,23 +2644,67 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr ProgressCoordinator::instance()->emitDetailTextChanged(tr("Done")); } -bool PackageManagerCorePrivate::runningProcessesFound() +PackageManagerCore::Status PackageManagerCorePrivate::fetchComponentsAndInstall(const QStringList& components) { - //Check if there are processes running in the install - QStringList excludeFiles = m_allowedRunningProcesses; - excludeFiles.append(maintenanceToolName()); + // init default model before fetching remote packages tree + ComponentModel *model = m_core->defaultComponentModel(); + Q_UNUSED(model); - const QString performModeWarning = m_completeUninstall - ? QLatin1String("Unable to remove components.") - : QLatin1String("Unable to update components."); + bool fallbackReposFetched = false; + auto fetchComponents = [&]() { + bool packagesFound = m_core->fetchPackagesWithFallbackRepositories(components, fallbackReposFetched); - QStringList runningProcesses = runningInstallerProcesses(excludeFiles); - if (!runningProcesses.isEmpty()) { - qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace() << performModeWarning - << " Please stop these processes: " << runningProcesses << " and try again."; + if (!packagesFound) { + qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() + << "No components available with the current selection."; + setStatus(PackageManagerCore::Canceled); + return false; + } + QString errorMessage; + bool unstableAliasFound = false; + if (m_core->checkComponentsForInstallation(components, errorMessage, unstableAliasFound, fallbackReposFetched)) { + if (!errorMessage.isEmpty()) + qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage; + if (calculateComponentsAndRun()) { + if (m_core->isOfflineGenerator()) + qCDebug(QInstaller::lcInstallerInstallLog) << "Created installer to:" << offlineBinaryName(); + else + qCDebug(QInstaller::lcInstallerInstallLog) << "Components installed successfully"; + } + } else { + // We found unstable alias and all repos were not fetched. Alias might have dependency to component + // which exists in non-default repository. Fetch all repositories now. + if (unstableAliasFound && !fallbackReposFetched) { + return false; + } else { + for (const QString &possibleAliasName : components) { + if (ComponentAlias *alias = m_core->aliasByName(possibleAliasName)) { + if (alias->componentErrorMessage().isEmpty()) + continue; + qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace() << alias->componentErrorMessage(); + } + } + qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage + << "No components available with the current selection."; + } + } return true; + }; + + if (!fetchComponents() && !fallbackReposFetched) { + fallbackReposFetched = true; + setStatus(PackageManagerCore::Running); + qCDebug(QInstaller::lcInstallerInstallLog).noquote() + << "Components not found with the current selection." + << "Searching from additional repositories"; + if (!ProductKeyCheck::instance()->securityWarning().isEmpty()) { + qCWarning(QInstaller::lcInstallerInstallLog) << ProductKeyCheck::instance()->securityWarning(); + } + enableAllCategories(); + fetchComponents(); } - return false; + + return m_core->status(); } void PackageManagerCorePrivate::setComponentSelection(const QString &id, Qt::CheckState state) @@ -2637,9 +2824,12 @@ void PackageManagerCorePrivate::registerMaintenanceTool() settings.setValue(QLatin1String("Comments"), m_data.value(scTitle)); settings.setValue(QLatin1String("InstallDate"), QDateTime::currentDateTime().toString()); settings.setValue(QLatin1String("InstallLocation"), QDir::toNativeSeparators(targetDir())); - settings.setValue(QLatin1String("UninstallString"), quoted(maintenanceTool)); - settings.setValue(QLatin1String("ModifyPath"), QString(quoted(maintenanceTool) - + QLatin1String(" --manage-packages"))); + settings.setValue(QLatin1String("UninstallString"), QString(quoted(maintenanceTool) + + QLatin1String(" --") + CommandLineOptions::scStartUninstallerLong)); + if (!isOfflineOnly()) { + settings.setValue(QLatin1String("ModifyPath"), QString(quoted(maintenanceTool) + + QLatin1String(" --") + CommandLineOptions::scStartPackageManagerLong)); + } // required disk space of the installed components quint64 estimatedSizeKB = m_core->requiredDiskSpace() / 1024; // add required space for the maintenance tool @@ -2676,8 +2866,8 @@ void PackageManagerCorePrivate::unregisterMaintenanceTool() #endif } -void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOperations, double progressSize, - bool adminRightsGained, bool deleteOperation) +void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOperations, + double progressSize, bool deleteOperation) { try { const int operationsCount = undoOperations.size(); @@ -2688,7 +2878,7 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera throw Error(tr("Installation canceled by user")); bool becameAdmin = false; - if (!adminRightsGained && undoOperation->value(QLatin1String("admin")).toBool()) + if (!m_core->hasAdminRights() && undoOperation->value(QLatin1String("admin")).toBool()) becameAdmin = m_core->gainAdminRights(); connectOperationToInstaller(undoOperation, progressSize); @@ -2796,6 +2986,25 @@ LocalPackagesMap PackageManagerCorePrivate::localInstalledPackages() return m_localPackageHub->localPackages(); } +QList<ComponentAlias *> PackageManagerCorePrivate::componentAliases() +{ + if (m_aliases && m_aliasFinder) + return m_aliasFinder->aliases(); + + m_aliases = false; + delete m_aliasFinder; + + m_aliasFinder = new AliasFinder(m_core); + m_aliasFinder->setAliasSources(m_aliasSources); + if (!m_aliasFinder->run()) { + qCDebug(lcDeveloperBuild) << "No component aliases found." << Qt::endl; + return QList<ComponentAlias *>(); + } + + m_aliases = true; + return m_aliasFinder->aliases(); +} + bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories(DownloadType type) { m_updates = false; @@ -2858,9 +3067,9 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool compress continue; if (data->repository().isCompressed()) - m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 2)); + m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 2, data->repository().postLoadComponentScript())); else - m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 0)); + m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 0, data->repository().postLoadComponentScript())); ProductKeyCheck::instance()->addPackagesFromXml(data->path() + QLatin1String("/Updates.xml")); } @@ -2913,6 +3122,125 @@ void PackageManagerCorePrivate::updateComponentInstallActions() component->setInstallAction(ComponentModelHelper::Install); } +bool PackageManagerCorePrivate::enableAllCategories() +{ + QSet<RepositoryCategory> repoCategories = m_data.settings().repositoryCategories(); + bool additionalRepositoriesEnabled = false; + for (const auto &category : repoCategories) { + if (!category.isEnabled()) { + additionalRepositoriesEnabled = true; + enableRepositoryCategory(category, true); + } + } + return additionalRepositoriesEnabled; +} + +void PackageManagerCorePrivate::enableRepositoryCategory(const RepositoryCategory &repoCategory, const bool enable) +{ + RepositoryCategory replacement = repoCategory; + replacement.setEnabled(enable); + QSet<RepositoryCategory> tmpRepoCategories = m_data.settings().repositoryCategories(); + if (tmpRepoCategories.contains(repoCategory)) { + tmpRepoCategories.remove(repoCategory); + tmpRepoCategories.insert(replacement); + m_data.settings().addRepositoryCategories(tmpRepoCategories); + } +} + +bool PackageManagerCorePrivate::installablePackagesFound(const QStringList& components) +{ + if (components.isEmpty()) + return true; + + PackagesList remotes = remotePackages(); + + auto componentsNotFoundForInstall = QtConcurrent::blockingFiltered( + components, + [remotes](const QString& installerPackage) { + return filterMissingPackagesToInstall(installerPackage, remotes); + } + ); + + if (componentsNotFoundForInstall.count() > 0) { + QList<ComponentAlias *> aliasList = componentAliases(); + auto aliasesNotFoundForInstall = QtConcurrent::blockingFiltered( + componentsNotFoundForInstall, + [aliasList](const QString& installerPackage) { + return filterMissingAliasesToInstall(installerPackage, aliasList); + } + ); + + if (aliasesNotFoundForInstall.count() > 0) { + qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << "Cannot select " << aliasesNotFoundForInstall.join(QLatin1String(", ")) << ". Component(s) not found."; + setStatus(PackageManagerCore::NoPackagesFound); + return false; + } + } + return true; +} + +void PackageManagerCorePrivate::deferredRename(const QString &oldName, const QString &newName, bool restart) +{ + +#ifdef Q_OS_WINDOWS + const QString currentExecutable = QCoreApplication::applicationFilePath(); + QString tmpExecutable = generateTemporaryFileName(currentExecutable); + QFile::rename(currentExecutable, tmpExecutable); + QFile::rename(oldName, newName); + + QStringList arguments; + if (restart) { + // Restart with same command line arguments as first executable + arguments = QCoreApplication::arguments(); + arguments.removeFirst(); // Remove program name + arguments.prepend(tmpExecutable); + arguments.prepend(QLatin1String("--") + + CommandLineOptions::scCleanupUpdate); + } else { + arguments.append(QLatin1String("--") + + CommandLineOptions::scCleanupUpdateOnly); + arguments.append(tmpExecutable); + } + QProcessWrapper::startDetached2(newName, arguments); +#elif defined Q_OS_MACOS + // In macos oldName is the name of the maintenancetool we got from repository + // It might be extracted to a folder to avoid overlapping with running maintenancetool + // Here, ditto renames it to newName (and possibly moves from the subfolder). + if (oldName != newName) { + //1. Rename/move maintenancetool + QProcessWrapper process; + process.start(QLatin1String("ditto"), QStringList() << oldName << newName); + if (!process.waitForFinished()) { + qCDebug(QInstaller::lcInstallerInstallLog) << "Failed to rename maintenancetool from :" << oldName << " to "<<newName; + } + //2. Remove subfolder + QDir subDirectory(oldName); + subDirectory.cdUp(); + QString subDirectoryPath = QDir::cleanPath(subDirectory.absolutePath()); + QString targetDirectoryPath = QDir::cleanPath(targetDir()); + + //Make sure there is subdirectory in the targetdir so we don't delete the installation + if (subDirectoryPath.startsWith(targetDirectoryPath) && subDirectoryPath != targetDirectoryPath) + subDirectory.removeRecursively(); + } + + //3. If new maintenancetool name differs from original, remove the original maintenance tool + if (!isInstaller()) { + QString currentAppBundlePath = getAppBundlePath(); + if (currentAppBundlePath != newName) { + QDir oldBundlePath(currentAppBundlePath); + oldBundlePath.removeRecursively(); + } + } + SelfRestarter::setRestartOnQuit(restart); + +#elif defined Q_OS_LINUX + QFile::remove(newName); + QFile::rename(oldName, newName); + SelfRestarter::setRestartOnQuit(restart); +#endif +} + void PackageManagerCorePrivate::connectOperationCallMethodRequest(Operation *const operation) { QObject *const operationObject = dynamic_cast<QObject *> (operation); @@ -2973,16 +3301,16 @@ void PackageManagerCorePrivate::addPathForDeletion(const QString &path) } void PackageManagerCorePrivate::unpackAndInstallComponents(const QList<Component *> &components, - const double progressOperationSize, const bool adminRightsGained) + const double progressOperationSize) { // Perform extract operations - unpackComponents(components, progressOperationSize, adminRightsGained); + unpackComponents(components, progressOperationSize); // Perform rest of the operations and mark component as installed const int componentsToInstallCount = components.size(); int installedComponents = 0; foreach (Component *component, components) { - installComponent(component, progressOperationSize, adminRightsGained); + installComponent(component, progressOperationSize); ++installedComponents; ProgressCoordinator::instance()->emitAdditionalProgressStatus(tr("%1 of %2 components installed.") @@ -3008,24 +3336,6 @@ void PackageManagerCorePrivate::processFilesForDelayedDeletion() } } -void PackageManagerCorePrivate::findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result) -{ - QDirIterator it(path, QDir::NoDotAndDotDot | QDir::Executable | QDir::Files | QDir::System, QDirIterator::Subdirectories ); - - while (it.hasNext()) - result->append(QDir::toNativeSeparators(it.next().toLower())); - - foreach (const QString &process, excludeFiles) - result->removeAll(QDir::toNativeSeparators(process.toLower())); -} - -QStringList PackageManagerCorePrivate::runningInstallerProcesses(const QStringList &excludeFiles) -{ - QStringList resultFiles; - findExecutablesRecursive(QCoreApplication::applicationDirPath(), excludeFiles, &resultFiles); - return checkRunningProcessesFromList(resultFiles); -} - bool PackageManagerCorePrivate::calculateComponentsAndRun() { bool componentsOk = m_core->recalculateAllComponents(); @@ -3042,9 +3352,8 @@ bool PackageManagerCorePrivate::calculateComponentsAndRun() qCDebug(QInstaller::lcInstallerInstallLog).noquote() << htmlToString(m_core->componentResolveReasons()); - QString spaceInfo; - const bool spaceOk = m_core->checkAvailableSpace(spaceInfo); - qCDebug(QInstaller::lcInstallerInstallLog) << spaceInfo; + const bool spaceOk = m_core->checkAvailableSpace(); + qCDebug(QInstaller::lcInstallerInstallLog) << m_core->availableSpaceMessage(); if (!spaceOk || !(m_autoConfirmCommand || askUserConfirmCommand())) { qCDebug(QInstaller::lcInstallerInstallLog) << "Installation aborted."; @@ -3071,6 +3380,13 @@ bool PackageManagerCorePrivate::acceptLicenseAgreements() const m_core->addLicenseItem(component->licenses()); } + const QString acceptanceText = ProductKeyCheck::instance()->licenseAcceptanceText(); + if (!acceptanceText.isEmpty()) { + qCDebug(QInstaller::lcInstallerInstallLog).noquote() << acceptanceText; + if (!m_autoAcceptLicenses && !acceptRejectCliQuery()) + return false; + } + QHash<QString, QMap<QString, QString>> priorityHash = m_core->sortedLicenses(); QStringList priorities = priorityHash.keys(); priorities.sort(); @@ -3119,6 +3435,23 @@ bool PackageManagerCorePrivate::askUserAcceptLicense(const QString &name, const } } +bool PackageManagerCorePrivate::acceptRejectCliQuery() const +{ + forever { + const QString input = m_core->readConsoleLine(QLatin1String("Accept|Reject")); + + if (QString::compare(input, QLatin1String("Accept"), Qt::CaseInsensitive) == 0 + || QString::compare(input, QLatin1String("A"), Qt::CaseInsensitive) == 0) { + return true; + } else if (QString::compare(input, QLatin1String("Reject"), Qt::CaseInsensitive) == 0 + || QString::compare(input, QLatin1String("R"), Qt::CaseInsensitive) == 0) { + return false; + } else { + qCDebug(QInstaller::lcInstallerInstallLog) << "Unknown answer:" << input; + } + } +} + bool PackageManagerCorePrivate::askUserConfirmCommand() const { qCDebug(QInstaller::lcInstallerInstallLog) << "Do you want to continue?"; diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h index 74cb0667c..c1b50e615 100644 --- a/src/libs/installer/packagemanagercore_p.h +++ b/src/libs/installer/packagemanagercore_p.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2023 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -54,11 +54,15 @@ using namespace KDUpdater; namespace QInstaller { struct BinaryLayout; +struct AliasSource; +class AliasFinder; class ScriptEngine; class ComponentModel; +class ComponentAlias; class InstallerCalculator; class UninstallerCalculator; class RemoteFileEngineHandler; +class ComponentSortFilterProxyModel; class PackageManagerCorePrivate : public QObject { @@ -106,6 +110,7 @@ public: QString configurationFileName() const; bool buildComponentTree(QHash<QString, Component*> &components, bool loadScript); + bool buildComponentAliases(); template <typename T> bool loadComponentScripts(const T &components, const bool postScript = false); @@ -169,13 +174,11 @@ public: m_performedOperationsCurrentSession.clear(); } - void unpackComponents(const QList<Component *> &components, double progressOperationSize, - bool adminRightsGained = false); + void unpackComponents(const QList<Component *> &components, double progressOperationSize); - void installComponent(Component *component, double progressOperationSize, - bool adminRightsGained = false); + void installComponent(Component *component, double progressOperationSize); + PackageManagerCore::Status fetchComponentsAndInstall(const QStringList& components); - bool runningProcessesFound(); void setComponentSelection(const QString &id, Qt::CheckState state); signals: @@ -188,8 +191,10 @@ signals: public: UpdateFinder *m_updateFinder; + AliasFinder *m_aliasFinder; QSet<PackageSource> m_packageSources; QSet<PackageSource> m_compressedPackageSources; + QSet<AliasSource> m_aliasSources; std::shared_ptr<LocalPackageHub> m_localPackageHub; QStringList m_filesForDelayedDeletion; @@ -217,6 +222,8 @@ public: QList<QInstaller::Component*> m_updaterComponentsDeps; QList<QInstaller::Component*> m_updaterDependencyReplacements; + QHash<QString, QInstaller::ComponentAlias *> m_componentAliases; + OperationList m_ownedOperations; OperationList m_performedOperationsOld; OperationList m_performedOperationsCurrentSession; @@ -244,7 +251,7 @@ private slots: private: void unpackAndInstallComponents(const QList<Component *> &components, - const double progressOperationSize, const bool adminRightsGained); + const double progressOperationSize); void deleteMaintenanceTool(); void deleteMaintenanceToolAlias(); @@ -257,18 +264,19 @@ private: void writeMaintenanceToolAppBundle(OperationList &performedOperations); void runUndoOperations(const OperationList &undoOperations, double undoOperationProgressSize, - bool adminRightsGained, bool deleteOperation); + bool deleteOperation); PackagesList remotePackages(); LocalPackagesMap localInstalledPackages(); + QList<ComponentAlias *> componentAliases(); + bool fetchMetaInformationFromRepositories(DownloadType type = DownloadType::All); bool addUpdateResourcesFromRepositories(bool compressedRepository = false); void processFilesForDelayedDeletion(); - void findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result); - QStringList runningInstallerProcesses(const QStringList &exludeFiles); bool calculateComponentsAndRun(); bool acceptLicenseAgreements() const; bool askUserAcceptLicense(const QString &name, const QString &content) const; + bool acceptRejectCliQuery() const; bool askUserConfirmCommand() const; bool packageNeedsUpdate(const LocalPackage &localPackage, const Package *update) const; void commitPendingUnstableComponents(); @@ -276,6 +284,13 @@ private: void createLocalDependencyHash(const QString &componentName, const QString &dependencies); void updateComponentInstallActions(); + bool enableAllCategories(); + void enableRepositoryCategory(const RepositoryCategory &repoCategory, const bool enable); + + bool installablePackagesFound(const QStringList& components); + + void deferredRename(const QString &oldName, const QString &newName, bool restart = false); + // remove once we deprecate isSelected, setSelected etc... void restoreCheckState(); void storeCheckState(); @@ -286,6 +301,7 @@ private: TempPathDeleter m_tmpPathDeleter; bool m_updates; + bool m_aliases; bool m_repoFetched; bool m_updateSourcesAdded; qint64 m_magicBinaryMarker; @@ -308,6 +324,7 @@ private: ComponentModel *m_defaultModel; ComponentModel *m_updaterModel; + ComponentSortFilterProxyModel *m_componentSortFilterProxyModel; QObject *m_guiObject; QScopedPointer<RemoteFileEngineHandler> m_remoteFileEngineHandler; @@ -325,6 +342,9 @@ private: QHash<QString, QStringList > m_componentReplaces; QString m_datFileName; + bool m_allowCompressedRepositoryInstall; + int m_connectedOperations; + QStringList m_componentsToBeInstalled; }; } // namespace QInstaller diff --git a/src/libs/installer/packagemanagercoredata.cpp b/src/libs/installer/packagemanagercoredata.cpp index 1d5b9f713..1113908bd 100644 --- a/src/libs/installer/packagemanagercoredata.cpp +++ b/src/libs/installer/packagemanagercoredata.cpp @@ -36,6 +36,7 @@ #include <QDir> #include <QRegularExpression> #include <QSettings> +#include <QStandardPaths> #ifdef Q_OS_WIN # include <windows.h> @@ -95,7 +96,7 @@ PackageManagerCoreData::PackageManagerCoreData(const QHash<QString, QString> &va if (isInstaller) { addNewVariable(scTargetDir, replaceVariables(m_settings.targetDir())); addNewVariable(scTargetConfigurationFile, m_settings.configurationFileName()); - addNewVariable(scStartMenuDir, m_settings.startMenuDir()); + addNewVariable(scStartMenuDir, replaceVariables(m_settings.startMenuDir())); } else { #ifdef Q_OS_MACOS addNewVariable(scTargetDir, QFileInfo(QCoreApplication::applicationDirPath() + QLatin1String("/../../..")).absoluteFilePath()); @@ -257,6 +258,8 @@ QVariant PackageManagerCoreData::value(const QString &key, const QVariant &_defa if (!filename.isEmpty() && !regKey.isEmpty() && registry.contains(regKey)) return registry.value(regKey).toString(); } +#else + Q_UNUSED(format) #endif if (m_variables.contains(key)) diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp index c3642e4ee..1f0462eea 100644 --- a/src/libs/installer/packagemanagergui.cpp +++ b/src/libs/installer/packagemanagergui.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2023 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -412,10 +412,13 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent) connect(this, &QDialog::rejected, m_core, &PackageManagerCore::setCanceled); connect(this, &PackageManagerGui::interrupted, m_core, &PackageManagerCore::interrupt); - // both queued to show the finished page once everything is done + // all queued to show the finished page once everything is done connect(m_core, &PackageManagerCore::installationFinished, this, &PackageManagerGui::showFinishedPage, Qt::QueuedConnection); + connect(m_core, &PackageManagerCore::offlineGenerationFinished, + this, &PackageManagerGui::showFinishedPage, + Qt::QueuedConnection); connect(m_core, &PackageManagerCore::uninstallationFinished, this, &PackageManagerGui::showFinishedPage, Qt::QueuedConnection); @@ -689,6 +692,22 @@ bool PackageManagerGui::isButtonEnabled(int wb) } /*! + Sets \a buttonText for button specified by \a buttonId to a installer page \a pageId. + + \note In some pages, installer will change the button text when entering + the page. In that case, you need to connect to \c entered() -signal of the + page to change the \a buttonText. + + \sa {gui::setWizardPageButtonText}{gui.setWizardPageButtonText} +*/ +void PackageManagerGui::setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText) +{ + PackageManagerPage *const p = qobject_cast<PackageManagerPage*> (page(pageId)); + if (p) + p->setButtonText(static_cast<QWizard::WizardButton>(buttonId), buttonText); +} + +/*! Sets a validator for the custom page specified by \a name and \a callbackName requested by \a component. */ @@ -1245,10 +1264,10 @@ void PackageManagerGui::currentPageChanged(int newId) PackageManagerPage::PackageManagerPage(PackageManagerCore *core) : m_complete(true) , m_titleColor(QString()) + , m_showOnPageList(true) , m_needsSettingsButton(false) , m_core(core) , validatorComponent(nullptr) - , m_showOnPageList(true) { if (!m_core->settings().titleColor().isEmpty()) m_titleColor = m_core->settings().titleColor(); @@ -1452,19 +1471,12 @@ int PackageManagerPage::nextId() const if (next == PackageManagerCore::LicenseCheck) { // calculate the page after the license page const int nextNextId = gui()->pageIds().value(gui()->pageIds().indexOf(next) + 1, -1); - const PackageManagerCore *const core = packageManagerCore(); + PackageManagerCore *const core = packageManagerCore(); if (core->isUninstaller()) return nextNextId; // forcibly hide the license page if we run as uninstaller - - foreach (Component* component, core->orderedComponentsToInstall()) { - if (core->isMaintainer() && component->isInstalled()) - continue; // package manager or updater, hide as long as the component is installed - - // The component is about to be installed and provides a license, so the page needs to - // be shown. - if (!component->licenses().isEmpty()) - return next; - } + core->recalculateAllComponents(); + if (core->hasLicenses()) + return next; return nextNextId; // no component with a license or all components with license installed } return next; // default, show the next page @@ -1503,7 +1515,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core) , m_removeAllComponents(nullptr) { setObjectName(QLatin1String("IntroductionPage")); - setColoredTitle(tr("Setup - %1").arg(productName())); + setColoredTitle(tr("Welcome")); QVBoxLayout *layout = new QVBoxLayout(this); setLayout(layout); @@ -1551,6 +1563,8 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core) m_errorLabel = new QLabel(this); m_errorLabel->setWordWrap(true); + m_errorLabel->setTextFormat(Qt::RichText); + m_errorLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); boxLayout->addWidget(m_errorLabel); m_errorLabel->setObjectName(QLatin1String("ErrorLabel")); @@ -1607,7 +1621,7 @@ bool IntroductionPage::validatePage() bool isOfflineOnlyInstaller = core->isInstaller() && core->isOfflineOnly(); // If not offline only installer, at least one valid repository needs to be available - if (!isOfflineOnlyInstaller && !validRepositoriesAvailable()) { + if (!isOfflineOnlyInstaller && !core->validRepositoriesAvailable()) { setErrorMessage(QLatin1String("<font color=\"red\">") + tr("At least one valid and enabled " "repository required for this action to succeed.") + QLatin1String("</font>")); return isComplete(); @@ -1654,21 +1668,12 @@ bool IntroductionPage::validatePage() // fetch common packages if (core->isInstaller() || core->isPackageManager()) { - bool localPackagesTreeFetched = false; if (!m_allPackagesFetched) { // first try to fetch the server side packages tree m_allPackagesFetched = core->fetchRemotePackagesTree(); if (!m_allPackagesFetched) { QString error = core->error(); - if (core->isPackageManager() && core->status() != PackageManagerCore::ForceUpdate) { - // if that fails and we're in maintenance mode, try to fetch local installed tree - localPackagesTreeFetched = core->fetchLocalPackagesTree(); - if (localPackagesTreeFetched) { - // if that succeeded, adjust error message - error = QLatin1String("<font color=\"red\">") + error + tr(" Only local package " - "management available.") + QLatin1String("</font>"); - } - } else if (core->status() == PackageManagerCore::ForceUpdate) { + if (core->status() == PackageManagerCore::ForceUpdate) { // replaces the error string from packagemanagercore error = tr("There is an important update available. Please select '%1' first") .arg(m_updateComponents->text().remove(QLatin1Char('&'))); @@ -1686,7 +1691,7 @@ bool IntroductionPage::validatePage() } } - if (m_allPackagesFetched || localPackagesTreeFetched) + if (m_allPackagesFetched) setComplete(true); } @@ -1833,22 +1838,6 @@ void IntroductionPage::setErrorMessage(const QString &error) #endif } -/*! - Returns \c true if at least one valid and enabled repository is available. -*/ -bool IntroductionPage::validRepositoriesAvailable() const -{ - const PackageManagerCore *const core = packageManagerCore(); - bool valid = false; - - foreach (const Repository &repo, core->settings().repositories()) { - if (repo.isEnabled() && repo.isValid()) { - valid = true; - break; - } - } - return valid; -} // -- private slots @@ -1892,9 +1881,9 @@ void IntroductionPage::setPackageManager(bool value) */ void IntroductionPage::initializePage() { - const bool repositoriesAvailable = validRepositoriesAvailable(); - PackageManagerCore *core = packageManagerCore(); + const bool repositoriesAvailable = core->validRepositoriesAvailable(); + if (core->isPackageManager()) { m_packageManager->setChecked(true); } else if (core->isUpdater()) { @@ -1927,7 +1916,7 @@ void IntroductionPage::onCoreNetworkSettingsChanged() PackageManagerCore *core = packageManagerCore(); if (core->isUninstaller() || core->isMaintainer()) { - m_offlineMaintenanceTool = !validRepositoriesAvailable(); + m_offlineMaintenanceTool = !core->validRepositoriesAvailable(); setMaintainerToolsEnabled(!m_offlineMaintenanceTool); m_removeAllComponents->setChecked(m_offlineMaintenanceTool); @@ -2117,7 +2106,7 @@ void LicenseAgreementPage::entering() */ bool LicenseAgreementPage::isComplete() const { - return m_acceptCheckBox->isChecked(); + return m_acceptCheckBox->isChecked() && ProductKeyCheck::instance()->hasAcceptedAllLicenses(); } void LicenseAgreementPage::openLicenseUrl(const QUrl &url) @@ -2210,7 +2199,7 @@ void ComponentSelectionPage::entering() QT_TR_NOOP("Please select the components you want to update."), QT_TR_NOOP("Please select the components you want to install."), QT_TR_NOOP("Please select the components you want to uninstall."), - QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated."), + QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them.<br>Any components already installed will not be updated."), QT_TR_NOOP("Mandatory components need to be updated first before you can select other components to update.") }; @@ -2228,14 +2217,13 @@ void ComponentSelectionPage::entering() d->onModelStateChanged(d->m_currentModel->checkedState()); setModified(isComplete()); - if (core->settings().repositoryCategories().count() > 0 && !core->isOfflineOnly() - && !core->isUpdater()) { - d->showCategoryLayout(true); - core->settings().setAllowUnstableComponents(true); - } else { - d->showCategoryLayout(false); - } + d->showCategoryLayout(core->showRepositoryCategories()); d->showCompressedRepositoryButton(); + d->showCreateOfflineInstallerButton(true); + + // Reset to default supplement state. The page may set it to OfflineGenerator + // which needs to be reset after navigating back to the page. + core->resetBinaryMarkerSupplement(); } /*! @@ -2245,6 +2233,7 @@ void ComponentSelectionPage::entering() void ComponentSelectionPage::leaving() { d->hideCompressedRepositoryButton(); + d->showCreateOfflineInstallerButton(false); } /*! @@ -2328,16 +2317,6 @@ void ComponentSelectionPage::deselectComponent(const QString &id) } /*! - Adds the possibility to install a compressed repository on component selection - page. A new button which opens a file browser is added for compressed - repository selection. -*/ -void ComponentSelectionPage::allowCompressedRepositoryInstall() -{ - d->allowCompressedRepositoryInstall(); -} - -/*! Adds an additional virtual component with the \a name to be installed. Returns \c true if the virtual component is found and not installed. @@ -2357,6 +2336,11 @@ bool ComponentSelectionPage::addVirtualComponentToUninstall(const QString &name) return false; } +void ComponentSelectionPage::setAllowCreateOfflineInstaller(bool allow) +{ + d->setAllowCreateOfflineInstaller(allow); +} + void ComponentSelectionPage::setModified(bool modified) { setComplete(modified); @@ -2370,18 +2354,7 @@ bool ComponentSelectionPage::isComplete() const if (!d->componentsResolved()) return false; - if (packageManagerCore()->isInstaller() || packageManagerCore()->isUpdater()) - return d->m_currentModel->checked().count(); - - if (d->m_currentModel->checkedState().testFlag(ComponentModel::DefaultChecked) == false) - return true; - - const QSet<Component *> uncheckable = d->m_currentModel->uncheckable(); - for (auto &component : uncheckable) { - if (component->forcedInstallation() && !component->isInstalled()) - return true; // allow installation for new forced components - } - return false; + return d->m_currentModel->componentsSelected(); } @@ -2719,6 +2692,10 @@ void ReadyForInstallationPage::entering() setButtonText(QWizard::CommitButton, tr("U&pdate")); setColoredTitle(tr("Ready to Update Packages")); m_msgLabel->setText(tr("All required information is now available to begin updating your installation.")); + } else if (packageManagerCore()->isOfflineGenerator()) { + setButtonText(QWizard::CommitButton, tr("Create Offline Installer")); + setColoredTitle(tr("Ready to Create Offline Installer")); + m_msgLabel->setText(tr("All required information is now available to create an offline installer for selected components.")); } else { Q_ASSERT(packageManagerCore()->isInstaller()); setButtonText(QWizard::CommitButton, tr("&Install")); @@ -2735,11 +2712,10 @@ void ReadyForInstallationPage::entering() m_taskDetailsBrowser->setVisible(!componentsOk || LoggingHandler::instance().isVerbose()); setComplete(componentsOk); - QString spaceInfo; - if (packageManagerCore()->checkAvailableSpace(spaceInfo)) { - m_msgLabel->setText(QString::fromLatin1("%1 %2").arg(m_msgLabel->text(), spaceInfo)); + if (packageManagerCore()->checkAvailableSpace()) { + m_msgLabel->setText(QString::fromLatin1("%1 %2").arg(m_msgLabel->text(), packageManagerCore()->availableSpaceMessage())); } else { - m_msgLabel->setText(spaceInfo); + m_msgLabel->setText(packageManagerCore()->availableSpaceMessage()); setComplete(false); } } @@ -2759,7 +2735,9 @@ void ReadyForInstallationPage::leaving() void ReadyForInstallationPage::updatePageListTitle() { PackageManagerCore *core = packageManagerCore(); - if (core->isInstaller()) + if (core->isOfflineGenerator()) + setPageListTitle(tr("Ready to Create Offline Installer")); + else if (core->isInstaller()) setPageListTitle(tr("Ready to Install")); else if (core->isMaintainer()) setPageListTitle(tr("Ready to Update")); @@ -2817,6 +2795,11 @@ PerformInstallationPage::PerformInstallationPage(PackageManagerCore *core) connect(core, &PackageManagerCore::installationFinished, this, &PerformInstallationPage::installationFinished); + connect(core, &PackageManagerCore::offlineGenerationStarted, + this, &PerformInstallationPage::installationStarted); + connect(core, &PackageManagerCore::offlineGenerationFinished, + this, &PerformInstallationPage::installationFinished); + connect(core, &PackageManagerCore::uninstallationStarted, this, &PerformInstallationPage::uninstallationStarted); connect(core, &PackageManagerCore::uninstallationFinished, @@ -2886,6 +2869,11 @@ void PerformInstallationPage::entering() setColoredTitle(tr("Updating components of %1").arg(productName())); QTimer::singleShot(30, packageManagerCore(), SLOT(runPackageUpdater())); + } else if (packageManagerCore()->isOfflineGenerator()) { + setButtonText(QWizard::CommitButton, tr("&Create Offline Installer")); + setColoredTitle(tr("Creating Offline Installer for %1").arg(productName())); + + QTimer::singleShot(30, packageManagerCore(), SLOT(runOfflineGenerator())); } else { setButtonText(QWizard::CommitButton, tr("&Install")); setColoredTitle(tr("Installing %1").arg(productName())); @@ -2910,7 +2898,9 @@ void PerformInstallationPage::leaving() void PerformInstallationPage::updatePageListTitle() { PackageManagerCore *core = packageManagerCore(); - if (core->isInstaller()) + if (core->isOfflineGenerator()) + setPageListTitle(tr("Creating Offline Installer")); + else if (core->isInstaller()) setPageListTitle(tr("Installing")); else if (core->isMaintainer()) setPageListTitle(tr("Updating")); @@ -3014,7 +3004,7 @@ FinishedPage::FinishedPage(PackageManagerCore *core) , m_commitButton(nullptr) { setObjectName(QLatin1String("FinishedPage")); - setColoredTitle(tr("Completing the %1 Setup").arg(productName())); + setColoredTitle(tr("Finished the %1 Setup").arg(productName())); setPageListTitle(tr("Finished")); m_msgLabel = new QLabel(this); @@ -3039,7 +3029,7 @@ FinishedPage::FinishedPage(PackageManagerCore *core) */ void FinishedPage::entering() { - m_msgLabel->setText(tr("Click %1 to exit the %2 Wizard.") + m_msgLabel->setText(tr("Click %1 to exit the %2 Setup.") .arg(gui()->defaultButtonText(QWizard::FinishButton).remove(QLatin1Char('&'))) .arg(productName())); @@ -3132,16 +3122,8 @@ void FinishedPage::leaving() */ void FinishedPage::handleFinishClicked() { - const QString program = - packageManagerCore()->replaceVariables(packageManagerCore()->value(scRunProgram)); - - const QStringList args = packageManagerCore()->replaceVariables(packageManagerCore() - ->values(scRunProgramArguments)); - if (!m_runItCheckBox->isChecked() || program.isEmpty()) - return; - - qCDebug(QInstaller::lcInstallerInstallLog) << "starting" << program << args; - QProcess::startDetached(program, args); + if (m_runItCheckBox->isChecked()) + packageManagerCore()->runProgram(); } /*! @@ -3186,7 +3168,7 @@ RestartPage::RestartPage(PackageManagerCore *core) : PackageManagerPage(core) { setObjectName(QLatin1String("RestartPage")); - setColoredTitle(tr("Completing the %1 Setup").arg(productName())); + setColoredTitle(tr("Finished the %1 Setup").arg(productName())); // Never show this page on the page list setShowOnPageList(false); diff --git a/src/libs/installer/packagemanagergui.h b/src/libs/installer/packagemanagergui.h index ba27c4af6..d83643005 100644 --- a/src/libs/installer/packagemanagergui.h +++ b/src/libs/installer/packagemanagergui.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -82,6 +82,7 @@ public: void clickButton(int wizardButton, int delayInMs = 0); void clickButton(const QString &objectName, int delayInMs = 0) const; bool isButtonEnabled(int wizardButton); + void setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText); void showSettingsButton(bool show); void requestSettingsButtonByInstaller(bool request); @@ -261,7 +262,6 @@ private: void leaving() override; void showWidgets(bool show); - bool validRepositoriesAvailable() const; private: bool m_updatesFetched; @@ -330,9 +330,10 @@ public: Q_INVOKABLE void selectDefault(); Q_INVOKABLE void selectComponent(const QString &id); Q_INVOKABLE void deselectComponent(const QString &id); - Q_INVOKABLE void allowCompressedRepositoryInstall(); Q_INVOKABLE bool addVirtualComponentToUninstall(const QString &name); + void setAllowCreateOfflineInstaller(bool allow); + protected: void entering() override; void leaving() override; diff --git a/src/libs/installer/packagesource.cpp b/src/libs/installer/packagesource.cpp index 3b9fbe813..0f87e0def 100644 --- a/src/libs/installer/packagesource.cpp +++ b/src/libs/installer/packagesource.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -50,7 +50,7 @@ namespace QInstaller { */ /*! - \fn QInstaller::PackageSource::PackageSource(const QUrl &u, int p) + \fn QInstaller::PackageSource::PackageSource(const QUrl &u, int p, bool pl) Constructs a package source info object. The object's url is set to \a u, while the priority is set to \a p. @@ -69,7 +69,7 @@ namespace QInstaller { /*! Returns the hash value for the \a key, using \a seed to seed the calculation. */ -uint qHash(const PackageSource &key, uint seed) +hashValue qHash(const PackageSource &key, hashValue seed) { return qHash(key.url, seed) ^ key.priority; } diff --git a/src/libs/installer/packagesource.h b/src/libs/installer/packagesource.h index 1193c1f76..f63b53cd8 100644 --- a/src/libs/installer/packagesource.h +++ b/src/libs/installer/packagesource.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -40,16 +40,18 @@ struct INSTALLER_EXPORT PackageSource PackageSource() : priority(-1) {} - PackageSource(const QUrl &u, int p) + PackageSource(const QUrl &u, int p, bool pl = false) : url(u) , priority(p) + , postLoadComponentScript(pl) {} QUrl url; int priority; + bool postLoadComponentScript; }; -INSTALLER_EXPORT uint qHash(const PackageSource &key, uint seed); +INSTALLER_EXPORT hashValue qHash(const PackageSource &key, hashValue seed); INSTALLER_EXPORT bool operator==(const PackageSource &lhs, const PackageSource &rhs); } // namespace QInstaller diff --git a/src/libs/installer/permissionsettings.cpp b/src/libs/installer/permissionsettings.cpp index 68e4ab427..d70cf5625 100644 --- a/src/libs/installer/permissionsettings.cpp +++ b/src/libs/installer/permissionsettings.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -37,38 +37,6 @@ using namespace QInstaller; \internal */ -PermissionSettings::PermissionSettings(const QString &organization, const QString &application, QObject *parent) - : QSettings(organization, application, parent) -{ -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - setIniCodec("UTF-8"); // to workaround QTBUG-102334 -#endif -} - -PermissionSettings::PermissionSettings(Scope scope, const QString &organization, const QString &application, QObject *parent) - : QSettings(scope, organization, application, parent) -{ -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - setIniCodec("UTF-8"); // QTBUG-102334 -#endif -} - -PermissionSettings::PermissionSettings(Format format, Scope scope, const QString &organization, const QString &application, QObject *parent) - : QSettings(format, scope, organization, application, parent) -{ -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - setIniCodec("UTF-8"); // QTBUG-102334 -#endif -} - -PermissionSettings::PermissionSettings(const QString &fileName, Format format, QObject *parent) - : QSettings(fileName, format, parent) -{ -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - setIniCodec("UTF-8"); // QTBUG-102334 -#endif -} - PermissionSettings::~PermissionSettings() { if (!fileName().isEmpty()) { diff --git a/src/libs/installer/permissionsettings.h b/src/libs/installer/permissionsettings.h index 2621624c2..c950d9c17 100644 --- a/src/libs/installer/permissionsettings.h +++ b/src/libs/installer/permissionsettings.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -37,12 +37,19 @@ class PermissionSettings : public QSettings { public: explicit PermissionSettings(const QString &organization, - const QString &application = QString(), QObject *parent = 0); + const QString &application = QString(), QObject *parent = 0) + : QSettings(organization, application, parent) {} + PermissionSettings(Scope scope, const QString &organization, - const QString &application = QString(), QObject *parent = 0); + const QString &application = QString(), QObject *parent = 0) + : QSettings(scope, organization, application, parent) {} + PermissionSettings(Format format, Scope scope, const QString &organization, - const QString &application = QString(), QObject *parent = 0); - PermissionSettings(const QString &fileName, Format format, QObject *parent = 0); + const QString &application = QString(), QObject *parent = 0) + : QSettings(format, scope, organization, application, parent) {} + PermissionSettings(const QString &fileName, Format format, QObject *parent = 0) + : QSettings(fileName, format, parent) {} + ~PermissionSettings(); }; diff --git a/src/libs/installer/productkeycheck.cpp b/src/libs/installer/productkeycheck.cpp index c1dfe83d6..ed128fa61 100644 --- a/src/libs/installer/productkeycheck.cpp +++ b/src/libs/installer/productkeycheck.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -29,6 +29,8 @@ #include "productkeycheck.h" #include "packagemanagercore.h" +#include <QtUiTools/QUiLoader> + class ProductKeyCheckPrivate { }; @@ -49,6 +51,12 @@ ProductKeyCheck *ProductKeyCheck::instance() return &instance; } +QUiLoader *ProductKeyCheck::uiLoader() +{ + static QUiLoader loader; + return &loader; +} + void ProductKeyCheck::init(QInstaller::PackageManagerCore *core) { Q_UNUSED(core) @@ -106,3 +114,22 @@ bool ProductKeyCheck::hasValidLicense() const { return true; } + +bool ProductKeyCheck::hasAcceptedAllLicenses() const +{ + return true; +} + +QString ProductKeyCheck::licenseAcceptanceText() const +{ + return QString(); +} +QString ProductKeyCheck::securityWarning() const +{ + return QString(); +} + +QString ProductKeyCheck::additionalMetaDownloadWarning() const +{ + return QString(); +} diff --git a/src/libs/installer/productkeycheck.h b/src/libs/installer/productkeycheck.h index b7e8c6d52..8e7d6724f 100644 --- a/src/libs/installer/productkeycheck.h +++ b/src/libs/installer/productkeycheck.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -32,6 +32,7 @@ #include "installer_global.h" #include <QString> +#include <QUiLoader> namespace QInstaller { @@ -49,6 +50,8 @@ public: static ProductKeyCheck *instance(); void init(QInstaller::PackageManagerCore *core); + static QUiLoader *uiLoader(); + // was validLicense bool hasValidKey(); QString lastErrorString(); @@ -67,6 +70,10 @@ public: QList<int> registeredPages() const; bool hasValidLicense() const; + bool hasAcceptedAllLicenses() const; + QString licenseAcceptanceText() const; + QString securityWarning() const; + QString additionalMetaDownloadWarning() const; private: ProductKeyCheck(); diff --git a/src/libs/installer/progresscoordinator.cpp b/src/libs/installer/progresscoordinator.cpp index 8b02711b1..6413efe28 100644 --- a/src/libs/installer/progresscoordinator.cpp +++ b/src/libs/installer/progresscoordinator.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -40,7 +40,7 @@ using namespace QInstaller; QT_BEGIN_NAMESPACE -uint qHash(QPointer<QObject> key) +hashValue qHash(QPointer<QObject> key) { return qHash(key.data()); } @@ -76,7 +76,8 @@ ProgressCoordinator *ProgressCoordinator::instance() void ProgressCoordinator::reset() { - disconnectAllSenders(); + m_senderPartProgressSizeHash.clear(); + m_senderPendingCalculatedPercentageHash.clear(); m_installationLabelText.clear(); m_currentCompletePercentage = 0; m_currentBasePercentage = 0; @@ -90,10 +91,11 @@ void ProgressCoordinator::reset() void ProgressCoordinator::registerPartProgress(QObject *sender, const char *signal, double partProgressSize) { Q_ASSERT(sender); + Q_ASSERT(!sender->objectName().isEmpty()); Q_ASSERT(QString::fromLatin1(signal).contains(QLatin1String("(double)"))); Q_ASSERT(partProgressSize <= 1); - m_senderPartProgressSizeHash.insert(sender, partProgressSize); + m_senderPartProgressSizeHash.insert(sender->objectName(), partProgressSize); bool isConnected = connect(sender, signal, this, SLOT(partProgressChanged(double))); Q_UNUSED(isConnected); Q_ASSERT(isConnected); @@ -116,16 +118,17 @@ void ProgressCoordinator::partProgressChanged(double fraction) } // no fraction no change - if (fraction == 0) + if (fraction == 0 || !sender()) return; + QString senderObjectName = sender()->objectName(); // ignore senders sending 100% multiple times - if (fraction == 1 && m_senderPendingCalculatedPercentageHash.contains(sender()) - && m_senderPendingCalculatedPercentageHash.value(sender()) == 0) { + if (fraction == 1 && m_senderPendingCalculatedPercentageHash.contains(senderObjectName) + && m_senderPendingCalculatedPercentageHash.value(senderObjectName) == 0) { return; } - double partProgressSize = m_senderPartProgressSizeHash.value(sender(), 0); + double partProgressSize = m_senderPartProgressSizeHash.value(senderObjectName, 0); if (partProgressSize == 0) { qCWarning(QInstaller::lcInstallerInstallLog) << "It seems that this sender was not registered " "in the right way:" << sender(); @@ -138,7 +141,7 @@ void ProgressCoordinator::partProgressChanged(double fraction) // allPendingCalculatedPartPercentages has negative values double newCurrentCompletePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage - + allPendingCalculatedPartPercentages(sender()); + + allPendingCalculatedPartPercentages(senderObjectName); //we can't check this here, because some round issues can make it little bit under 0 or over 100 //Q_ASSERT(newCurrentCompletePercentage >= 0); @@ -163,9 +166,9 @@ void ProgressCoordinator::partProgressChanged(double fraction) m_currentCompletePercentage = newCurrentCompletePercentage; if (fraction == 1) { m_currentBasePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage; - m_senderPendingCalculatedPercentageHash.insert(sender(), 0); + m_senderPendingCalculatedPercentageHash.insert(senderObjectName, 0); } else { - m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage); + m_senderPendingCalculatedPercentageHash.insert(senderObjectName, pendingCalculatedPartPercentage); } } else { //if (m_undoMode) @@ -174,7 +177,7 @@ void ProgressCoordinator::partProgressChanged(double fraction) //double checkValue = allPendingCalculatedPartPercentages(sender()); double newCurrentCompletePercentage = m_manualAddedPercentage + m_currentBasePercentage - + pendingCalculatedPartPercentage + allPendingCalculatedPartPercentages(sender()); + + pendingCalculatedPartPercentage + allPendingCalculatedPartPercentages(senderObjectName); //we can't check this here, because some round issues can make it little bit under 0 or over 100 //Q_ASSERT(newCurrentCompletePercentage >= 0); @@ -199,9 +202,9 @@ void ProgressCoordinator::partProgressChanged(double fraction) if (fraction == 1) { m_currentBasePercentage = m_currentBasePercentage + pendingCalculatedPartPercentage; - m_senderPendingCalculatedPercentageHash.insert(sender(), 0); + m_senderPendingCalculatedPercentageHash.insert(senderObjectName, 0); } else { - m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage); + m_senderPendingCalculatedPercentageHash.insert(senderObjectName, pendingCalculatedPartPercentage); } } //if (m_undoMode) printProgressPercentage(progressInPercentage()); @@ -219,25 +222,13 @@ int ProgressCoordinator::progressInPercentage() const return currentValue; } -void ProgressCoordinator::disconnectAllSenders() -{ - foreach (QPointer<QObject> sender, m_senderPartProgressSizeHash.keys()) { - if (!sender.isNull()) { - bool isDisconnected = sender->disconnect(this); - Q_UNUSED(isDisconnected); - Q_ASSERT(isDisconnected); - } - } - m_senderPartProgressSizeHash.clear(); - m_senderPendingCalculatedPercentageHash.clear(); -} - void ProgressCoordinator::setUndoMode() { Q_ASSERT(!m_undoMode); m_undoMode = true; - disconnectAllSenders(); + m_senderPartProgressSizeHash.clear(); + m_senderPendingCalculatedPercentageHash.clear(); m_reachedPercentageBeforeUndo = progressInPercentage(); m_currentBasePercentage = m_reachedPercentageBeforeUndo; } @@ -294,10 +285,10 @@ void ProgressCoordinator::emitLabelAndDetailTextChanged(const QString &text) qApp->processEvents(); //makes the result available in the ui } -double ProgressCoordinator::allPendingCalculatedPartPercentages(QObject *excludeKeyObject) +double ProgressCoordinator::allPendingCalculatedPartPercentages(const QString &excludeKeyObject) { double result = 0; - QHash<QPointer<QObject>, double>::iterator it = m_senderPendingCalculatedPercentageHash.begin(); + QHash<QString, double>::iterator it = m_senderPendingCalculatedPercentageHash.begin(); while (it != m_senderPendingCalculatedPercentageHash.end()) { if (it.key() != excludeKeyObject) result += it.value(); diff --git a/src/libs/installer/progresscoordinator.h b/src/libs/installer/progresscoordinator.h index 3540b5d16..75d5a5d30 100644 --- a/src/libs/installer/progresscoordinator.h +++ b/src/libs/installer/progresscoordinator.h @@ -85,12 +85,12 @@ protected: explicit ProgressCoordinator(QObject *parent); private: - double allPendingCalculatedPartPercentages(QObject *excludeKeyObject = 0); + double allPendingCalculatedPartPercentages(const QString &excludeKeyObject = QString()); void disconnectAllSenders(); private: - QHash<QPointer<QObject>, double> m_senderPendingCalculatedPercentageHash; - QHash<QPointer<QObject>, double> m_senderPartProgressSizeHash; + QHash<QString, double> m_senderPendingCalculatedPercentageHash; + QHash<QString, double> m_senderPartProgressSizeHash; ProgressSpinner *m_progressSpinner; QString m_installationLabelText; double m_currentCompletePercentage; diff --git a/src/libs/installer/protocol.h b/src/libs/installer/protocol.h index 2dd1a5b1c..8b2288a89 100644 --- a/src/libs/installer/protocol.h +++ b/src/libs/installer/protocol.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -90,7 +90,7 @@ const char GetQProcessSignals[] = "GetQProcessSignals"; const char QProcessSignalBytesWritten[] = "QProcess::bytesWritten"; const char QProcessSignalAboutToClose[] = "QProcess::aboutToClose"; const char QProcessSignalReadChannelFinished[] = "QProcess::readChannelFinished"; -const char QProcessSignalError[] = "QProcess::error"; +const char QProcessSignalError[] = "QProcess::errorOccurred"; const char QProcessSignalReadyReadStandardOutput[] = "QProcess::readyReadStandardOutput"; const char QProcessSignalReadyReadStandardError[] = "QProcess::readyReadStandardError"; const char QProcessSignalStarted[] = "QProcess::started"; diff --git a/src/libs/installer/qprocesswrapper.cpp b/src/libs/installer/qprocesswrapper.cpp index 6f34e36da..44117eefb 100644 --- a/src/libs/installer/qprocesswrapper.cpp +++ b/src/libs/installer/qprocesswrapper.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -54,7 +54,7 @@ QProcessWrapper::QProcessWrapper(QObject *parent) connect(&process, &QIODevice::bytesWritten, this, &QProcessWrapper::bytesWritten); connect(&process, &QIODevice::aboutToClose, this, &QProcessWrapper::aboutToClose); connect(&process, &QIODevice::readChannelFinished, this, &QProcessWrapper::readChannelFinished); - connect(&process, SIGNAL(error(QProcess::ProcessError)), SIGNAL(error(QProcess::ProcessError))); + connect(&process, SIGNAL(errorOccurred(QProcess::ProcessError)), SIGNAL(errorOccurred(QProcess::ProcessError))); connect(&process, &QProcess::readyReadStandardOutput, this, &QProcessWrapper::readyReadStandardOutput); connect(&process, &QProcess::readyReadStandardError, this, &QProcessWrapper::readyReadStandardError); connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), SIGNAL(finished(int,QProcess::ExitStatus))); @@ -88,7 +88,7 @@ void QProcessWrapper::processSignals() } else if (name == QLatin1String(Protocol::QProcessSignalReadChannelFinished)) { emit readChannelFinished(); } else if (name == QLatin1String(Protocol::QProcessSignalError)) { - emit error(static_cast<QProcess::ProcessError> (receivedSignals.takeFirst().toInt())); + emit errorOccurred(static_cast<QProcess::ProcessError> (receivedSignals.takeFirst().toInt())); } else if (name == QLatin1String(Protocol::QProcessSignalReadyReadStandardOutput)) { emit readyReadStandardOutput(); } else if (name == QLatin1String(Protocol::QProcessSignalReadyReadStandardError)) { @@ -191,8 +191,8 @@ void QProcessWrapper::setProcessChannelMode(QProcessWrapper::ProcessChannelMode { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::QProcessSetProcessChannelMode), - static_cast<QProcess::ProcessChannelMode>(mode), dummy); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetProcessChannelMode), + static_cast<QProcess::ProcessChannelMode>(mode)); m_lock.unlock(); } else { process.setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(mode)); @@ -216,8 +216,8 @@ void QProcessWrapper::setReadChannel(QProcessWrapper::ProcessChannel chan) { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::QProcessSetReadChannel), - static_cast<QProcess::ProcessChannel>(chan), dummy); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetReadChannel), + static_cast<QProcess::ProcessChannel>(chan)); m_lock.unlock(); } else { process.setReadChannel(static_cast<QProcess::ProcessChannel>(chan)); @@ -263,7 +263,7 @@ void QProcessWrapper::closeWriteChannel() { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::QProcessCloseWriteChannel)); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessCloseWriteChannel)); m_lock.unlock(); } else { process.closeWriteChannel(); @@ -296,7 +296,7 @@ void QProcessWrapper::kill() { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::QProcessKill)); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessKill)); m_lock.unlock(); } else { process.kill(); @@ -343,7 +343,7 @@ void QProcessWrapper::start(const QString ¶m1, const QStringList ¶m2, { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::QProcessStart3Arg), param1, param2, param3); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessStart3Arg), param1, param2, param3); m_lock.unlock(); } else { process.start(param1, param2, param3); @@ -354,7 +354,7 @@ void QProcessWrapper::start(const QString ¶m1, QIODevice::OpenMode param2) { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::QProcessStart2Arg), param1, param2); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessStart2Arg), param1, param2); m_lock.unlock(); } else { process.start(param1, {}, param2); @@ -376,7 +376,7 @@ void QProcessWrapper::terminate() { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::QProcessTerminate)); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessTerminate)); m_lock.unlock(); } else { process.terminate(); @@ -443,7 +443,7 @@ void QProcessWrapper::setEnvironment(const QStringList ¶m1) { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::QProcessSetEnvironment), param1, dummy); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetEnvironment), param1); m_lock.unlock(); } else { process.setEnvironment(param1); @@ -455,7 +455,7 @@ void QProcessWrapper::setNativeArguments(const QString ¶m1) { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::QProcessSetNativeArguments), param1, dummy); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetNativeArguments), param1); m_lock.unlock(); } else { process.setNativeArguments(param1); @@ -467,7 +467,7 @@ void QProcessWrapper::setWorkingDirectory(const QString ¶m1) { if (connectToServer()) { m_lock.lockForWrite(); - callRemoteMethod(QLatin1String(Protocol::QProcessSetWorkingDirectory), param1, dummy); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetWorkingDirectory), param1); m_lock.unlock(); } else { process.setWorkingDirectory(param1); diff --git a/src/libs/installer/qprocesswrapper.h b/src/libs/installer/qprocesswrapper.h index 1a68bbf92..b34d6f82c 100644 --- a/src/libs/installer/qprocesswrapper.h +++ b/src/libs/installer/qprocesswrapper.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -119,7 +119,7 @@ Q_SIGNALS: void bytesWritten(qint64); void aboutToClose(); void readChannelFinished(); - void error(QProcess::ProcessError); + void errorOccurred(QProcess::ProcessError); void readyReadStandardOutput(); void readyReadStandardError(); void finished(int exitCode, QProcess::ExitStatus exitStatus); diff --git a/src/libs/installer/qsettingswrapper.cpp b/src/libs/installer/qsettingswrapper.cpp index f2dd53767..d322728f5 100644 --- a/src/libs/installer/qsettingswrapper.cpp +++ b/src/libs/installer/qsettingswrapper.cpp @@ -141,27 +141,39 @@ QString QSettingsWrapper::applicationName() const return d->settings.applicationName(); } -void QSettingsWrapper::beginGroup(const QString ¶m1) +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) + void QSettingsWrapper::beginGroup(const QString &prefix) +#else + void QSettingsWrapper::beginGroup(QAnyStringView prefix) +#endif { if (createSocket()) - callRemoteMethod(QLatin1String(Protocol::QSettingsBeginGroup), param1, dummy); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsBeginGroup), prefix); else - d->settings.beginGroup(param1); + d->settings.beginGroup(prefix); } -int QSettingsWrapper::beginReadArray(const QString ¶m1) +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) + int QSettingsWrapper::beginReadArray(const QString &prefix) +#else + int QSettingsWrapper::beginReadArray(QAnyStringView prefix) +#endif { if (createSocket()) - return callRemoteMethod<qint32>(QLatin1String(Protocol::QSettingsBeginReadArray), param1); - return d->settings.beginReadArray(param1); + return callRemoteMethod<qint32>(QLatin1String(Protocol::QSettingsBeginReadArray), prefix); + return d->settings.beginReadArray(prefix); } -void QSettingsWrapper::beginWriteArray(const QString ¶m1, int param2) +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) + void QSettingsWrapper::beginWriteArray(const QString &prefix, int size) +#else + void QSettingsWrapper::beginWriteArray(QAnyStringView prefix, int size) +#endif { if (createSocket()) - callRemoteMethod(QLatin1String(Protocol::QSettingsBeginWriteArray), param1, qint32(param2)); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsBeginWriteArray), prefix, qint32(size)); else - d->settings.beginWriteArray(param1, param2); + d->settings.beginWriteArray(prefix, size); } QStringList QSettingsWrapper::childGroups() const @@ -181,21 +193,25 @@ QStringList QSettingsWrapper::childKeys() const void QSettingsWrapper::clear() { if (createSocket()) - callRemoteMethod(QLatin1String(Protocol::QSettingsClear)); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsClear)); else d->settings.clear(); } -bool QSettingsWrapper::contains(const QString ¶m1) const +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) + bool QSettingsWrapper::contains(const QString &key) const +#else + bool QSettingsWrapper::contains(QAnyStringView key) const +#endif { if (createSocket()) - return callRemoteMethod<bool>(QLatin1String(Protocol::QSettingsContains), param1); - return d->settings.contains(param1); + return callRemoteMethod<bool>(QLatin1String(Protocol::QSettingsContains), key); + return d->settings.contains(key); } void QSettingsWrapper::endArray() { if (createSocket()) - callRemoteMethod(QLatin1String(Protocol::QSettingsEndArray)); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsEndArray)); else d->settings.endArray(); } @@ -203,7 +219,7 @@ void QSettingsWrapper::endArray() void QSettingsWrapper::endGroup() { if (createSocket()) - callRemoteMethod(QLatin1String(Protocol::QSettingsEndGroup)); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsEndGroup)); else d->settings.endGroup(); } @@ -249,12 +265,16 @@ QString QSettingsWrapper::organizationName() const return d->settings.organizationName(); } -void QSettingsWrapper::remove(const QString ¶m1) +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) + void QSettingsWrapper::remove(const QString &key) +#else + void QSettingsWrapper::remove(QAnyStringView key) +#endif { if (createSocket()) - callRemoteMethod(QLatin1String(Protocol::QSettingsRemove), param1, dummy); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsRemove), key); else - d->settings.remove(param1); + d->settings.remove(key); } QSettingsWrapper::Scope QSettingsWrapper::scope() const @@ -266,7 +286,7 @@ QSettingsWrapper::Scope QSettingsWrapper::scope() const void QSettingsWrapper::setArrayIndex(int param1) { if (createSocket()) - callRemoteMethod(QLatin1String(Protocol::QSettingsSetArrayIndex), qint32(param1), dummy); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSetArrayIndex), qint32(param1)); else d->settings.setArrayIndex(param1); } @@ -274,17 +294,20 @@ void QSettingsWrapper::setArrayIndex(int param1) void QSettingsWrapper::setFallbacksEnabled(bool param1) { if (createSocket()) - callRemoteMethod(QLatin1String(Protocol::QSettingsSetFallbacksEnabled), param1, dummy); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSetFallbacksEnabled), param1); else d->settings.setFallbacksEnabled(param1); } - -void QSettingsWrapper::setValue(const QString ¶m1, const QVariant ¶m2) +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) +void QSettingsWrapper::setValue(const QString &key, const QVariant &value) +#else +void QSettingsWrapper::setValue(QAnyStringView key, const QVariant &value) +#endif { if (createSocket()) - callRemoteMethod(QLatin1String(Protocol::QSettingsSetValue), param1, param2); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSetValue), key, value); else - d->settings.setValue(param1, param2); + d->settings.setValue(key, value); } QSettingsWrapper::Status QSettingsWrapper::status() const @@ -299,17 +322,30 @@ QSettingsWrapper::Status QSettingsWrapper::status() const void QSettingsWrapper::sync() { if (createSocket()) - callRemoteMethod(QLatin1String(Protocol::QSettingsSync)); + callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSync)); else d->settings.sync(); } -QVariant QSettingsWrapper::value(const QString ¶m1, const QVariant ¶m2) const +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) +QVariant QSettingsWrapper::value(const QString &key, const QVariant &value) const +#else +QVariant QSettingsWrapper::value(QAnyStringView key, const QVariant &value) const +#endif +{ + if (createSocket()) + return callRemoteMethod<QVariant>(QLatin1String(Protocol::QSettingsValue), key, value); + return d->settings.value(key, value); +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) +QVariant QSettingsWrapper::value(QAnyStringView key) const { if (createSocket()) - return callRemoteMethod<QVariant>(QLatin1String(Protocol::QSettingsValue), param1, param2); - return d->settings.value(param1, param2); + return callRemoteMethod<QVariant>(QLatin1String(Protocol::QSettingsValue), key); + return d->settings.value(key); } +#endif // -- private diff --git a/src/libs/installer/qsettingswrapper.h b/src/libs/installer/qsettingswrapper.h index 92221c117..f5d428b1e 100644 --- a/src/libs/installer/qsettingswrapper.h +++ b/src/libs/installer/qsettingswrapper.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -36,6 +36,8 @@ #include <QSettings> +QT_FORWARD_DECLARE_CLASS(QTextCodec) + namespace QInstaller { class INSTALLER_EXPORT QSettingsWrapper : public RemoteObject @@ -68,12 +70,21 @@ public: void sync(); Status status() const; +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) void beginGroup(const QString &prefix); +#else + void beginGroup(QAnyStringView prefix); +#endif void endGroup(); QString group() const; +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) int beginReadArray(const QString &prefix); void beginWriteArray(const QString &prefix, int size = -1); +#else + int beginReadArray(QAnyStringView prefix); + void beginWriteArray(QAnyStringView prefix, int size = -1); +#endif void endArray(); void setArrayIndex(int i); @@ -82,12 +93,22 @@ public: QStringList childGroups() const; bool isWritable() const; +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) void setValue(const QString &key, const QVariant &value); QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; +#else + void setValue(QAnyStringView key, const QVariant &value); + QVariant value(QAnyStringView key, const QVariant &defaultValue) const; + QVariant value(QAnyStringView key) const; +#endif +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) void remove(const QString &key); bool contains(const QString &key) const; - +#else + void remove(QAnyStringView key); + bool contains(QAnyStringView key) const; +#endif void setFallbacksEnabled(bool b); bool fallbacksEnabled() const; @@ -105,14 +126,13 @@ private: // we cannot support the following functionality : RemoteObject(QLatin1String(Protocol::QSettings), parent) {} +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) void setIniCodec(QTextCodec * /*codec*/); void setIniCodec(const char * /*codecName*/); QTextCodec *iniCodec() const { return 0; } - +#endif static void setDefaultFormat(QSettings::Format /*format*/); static QSettings::Format defaultFormat() { return QSettings::NativeFormat; } - static void setSystemIniPath(const QString & /*dir*/); - static void setUserIniPath(const QString & /*dir*/); static void setPath(QSettings::Format /*format*/, Scope /*scope*/, const QString & /*path*/); typedef QMap<QString, QVariant> SettingsMap; diff --git a/src/libs/installer/registerfiletypeoperation.cpp b/src/libs/installer/registerfiletypeoperation.cpp index 1754b664d..852714dfb 100644 --- a/src/libs/installer/registerfiletypeoperation.cpp +++ b/src/libs/installer/registerfiletypeoperation.cpp @@ -153,7 +153,7 @@ bool RegisterFileTypeOperation::undoOperation() { #ifdef Q_OS_WIN ensureOptionalArgumentsRead(); - if (parseUndoOperationArguments().count() > 0) + if (skipUndoOperation()) return true; QStringList args = arguments(); diff --git a/src/libs/installer/remotefileengine.cpp b/src/libs/installer/remotefileengine.cpp index b600da618..2ead83861 100644 --- a/src/libs/installer/remotefileengine.cpp +++ b/src/libs/installer/remotefileengine.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -55,10 +55,10 @@ QAbstractFileEngine* RemoteFileEngineHandler::create(const QString &fileName) co if (fileName.isEmpty() || fileName.startsWith(QLatin1String(":"))) return 0; // empty filename or Qt resource - QScopedPointer<RemoteFileEngine> client(new RemoteFileEngine()); + std::unique_ptr<RemoteFileEngine> client(new RemoteFileEngine()); client->setFileName(fileName); if (client->isConnectedToServer()) - return client.take(); + return client.release(); return 0; } @@ -313,6 +313,7 @@ bool RemoteFileEngine::link(const QString &newName) /*! \reimp */ +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) { @@ -320,18 +321,38 @@ bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectorie dirName, createParentDirectories); } return m_fileEngine.mkdir(dirName, createParentDirectories); + +} +#else +bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectories, + std::optional<QFile::Permissions> permissions) const +{ + if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) { + return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineMkdir), + dirName, createParentDirectories); + } + return m_fileEngine.mkdir(dirName, createParentDirectories, permissions); } +#endif /*! \reimp */ +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) bool RemoteFileEngine::open(QIODevice::OpenMode mode) +#else +bool RemoteFileEngine::open(QIODevice::OpenMode mode, std::optional<QFile::Permissions> permissions) +#endif { if (connectToServer()) { return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineOpen), static_cast<qint32>(mode | QIODevice::Unbuffered)); } +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) return m_fileEngine.open(mode | QIODevice::Unbuffered); +#else + return m_fileEngine.open(mode | QIODevice::Unbuffered, permissions); +#endif } /*! @@ -418,8 +439,7 @@ bool RemoteFileEngine::seek(qint64 offset) void RemoteFileEngine::setFileName(const QString &fileName) { if (connectToServer()) { - callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineSetFileName), fileName, - dummy); + callRemoteMethodDefaultReply(QString::fromLatin1(Protocol::QAbstractFileEngineSetFileName), fileName); } m_fileEngine.setFileName(fileName); } @@ -534,7 +554,11 @@ bool RemoteFileEngine::renameOverwrite(const QString &newName) return m_fileEngine.renameOverwrite(newName); } +#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0) QDateTime RemoteFileEngine::fileTime(FileTime time) const +#else +QDateTime RemoteFileEngine::fileTime(QFile::FileTime time) const +#endif { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) { return callRemoteMethod<QDateTime> diff --git a/src/libs/installer/remotefileengine.h b/src/libs/installer/remotefileengine.h index 35ebf7742..c46e861ca 100644 --- a/src/libs/installer/remotefileengine.h +++ b/src/libs/installer/remotefileengine.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -53,7 +53,12 @@ public: RemoteFileEngine(); ~RemoteFileEngine(); +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) bool open(QIODevice::OpenMode mode) override; +#else + bool open(QIODevice::OpenMode mode, + std::optional<QFile::Permissions> permissions = std::nullopt) override; +#endif bool close() override; bool flush() override; bool syncToDisk() override; @@ -66,7 +71,12 @@ public: bool rename(const QString &newName) override; bool renameOverwrite(const QString &newName) override; bool link(const QString &newName) override; +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) bool mkdir(const QString &dirName, bool createParentDirectories) const override; +#else + bool mkdir(const QString &dirName, bool createParentDirectories, + std::optional<QFile::Permissions> permissions = std::nullopt) const override; +#endif bool rmdir(const QString &dirName, bool recurseParentDirectories) const override; bool setSize(qint64 size) override; bool caseSensitive() const override; @@ -77,7 +87,11 @@ public: QString fileName(FileName file = DefaultName) const override; uint ownerId(FileOwner owner) const override; QString owner(FileOwner owner) const override; +#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0) QDateTime fileTime(FileTime time) const override; +#else + QDateTime fileTime(QFile::FileTime time) const override; +#endif void setFileName(const QString &fileName) override; int handle() const override; bool atEnd() const; diff --git a/src/libs/installer/remoteobject.cpp b/src/libs/installer/remoteobject.cpp index 70ab48af3..b4dd0cbb7 100644 --- a/src/libs/installer/remoteobject.cpp +++ b/src/libs/installer/remoteobject.cpp @@ -46,7 +46,6 @@ namespace QInstaller { RemoteObject::RemoteObject(const QString &wrappedType, QObject *parent) : QObject(parent) - , dummy(nullptr) , m_type(wrappedType) , m_socket(nullptr) { @@ -140,10 +139,4 @@ bool RemoteObject::isConnectedToServer() const return false; } -void RemoteObject::callRemoteMethod(const QString &name) -{ - const QString reply = sendReceivePacket<QString>(name, dummy, dummy, dummy); - Q_ASSERT(reply == QLatin1String(Protocol::DefaultReply)); -} - } // namespace QInstaller diff --git a/src/libs/installer/remoteobject.h b/src/libs/installer/remoteobject.h index c423de943..ddd512588 100644 --- a/src/libs/installer/remoteobject.h +++ b/src/libs/installer/remoteobject.h @@ -52,93 +52,58 @@ public: virtual ~RemoteObject() = 0; bool isConnectedToServer() const; - void callRemoteMethod(const QString &name); - template<typename T1, typename T2> - void callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2) + template<typename... Args> + void callRemoteMethodDefaultReply(const QString &name, const Args&... args) { - const QString reply = sendReceivePacket<QString>(name, arg, arg2, dummy); + const QString reply = sendReceivePacket<QString>(name, args...); Q_ASSERT(reply == QLatin1String(Protocol::DefaultReply)); } - template<typename T1, typename T2, typename T3> - void callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2, const T3 & arg3) + template<typename T, typename... Args> + T callRemoteMethod(const QString &name, const Args&... args) const { - const QString reply = sendReceivePacket<QString>(name, arg, arg2, arg3); - Q_ASSERT(reply == QLatin1String(Protocol::DefaultReply)); - } - - template<typename T> - T callRemoteMethod(const QString &name) const - { - return sendReceivePacket<T>(name, dummy, dummy, dummy); - } - - template<typename T, typename T1> - T callRemoteMethod(const QString &name, const T1 &arg) const - { - return sendReceivePacket<T>(name, arg, dummy, dummy); - } - - template<typename T, typename T1, typename T2> - T callRemoteMethod(const QString &name, const T1 & arg, const T2 &arg2) const - { - return sendReceivePacket<T>(name, arg, arg2, dummy); - } - - template<typename T, typename T1, typename T2, typename T3> - T callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const - { - return sendReceivePacket<T>(name, arg, arg2, arg3); + return sendReceivePacket<T>(name, args...); } protected: bool authorize(); bool connectToServer(const QVariantList &arguments = QVariantList()); - // Use this structure to allow derived classes to manipulate the template - // function signature of the callRemoteMethod templates, since most of the - // generated functions will differ in return type rather given arguments. - struct Dummy {}; Dummy *dummy; - private: - template<typename T> bool isValueType(T) const - { - return true; - } - template<typename T> bool isValueType(T *dummy) const + template<typename T, typename... Args> + T sendReceivePacket(const QString &name, const Args&... args) const { - // Force compiler error while passing anything different then Dummy* to the function. - // It really doesn't make sense to send any pointer over to the server, so bail early. - Q_UNUSED(static_cast<Dummy*> (dummy)) - return false; - } - - template<typename T, typename T1, typename T2, typename T3> - T sendReceivePacket(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const - { - writeData(name, arg, arg2, arg3); + writeData(name, args...); while (m_socket->bytesToWrite()) m_socket->waitForBytesWritten(); return readData<T>(name); } + template <class T> int writeObject(QDataStream& out, const T& t) const + { + static_assert(!std::is_pointer<T>::value, "Pointer passed to remote server"); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + out << t; +#else + if constexpr (std::is_same<T, QAnyStringView>::value) + out << t.toString(); + else + out << t; +#endif + + return 0; + } - template<typename T1, typename T2, typename T3> - void writeData(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const + template<typename... Args> + void writeData(const QString &name, const Args&... args) const { QByteArray data; QDataStream out(&data, QIODevice::WriteOnly); - if (isValueType(arg)) - out << arg; - if (isValueType(arg2)) - out << arg2; - if (isValueType(arg3)) - out << arg3; - + (void)std::initializer_list<int>{writeObject(out, args)...}; sendPacket(m_socket, name.toLatin1(), data); m_socket->flush(); } diff --git a/src/libs/installer/remoteserverconnection.cpp b/src/libs/installer/remoteserverconnection.cpp index ed3d343fe..55c4f48d9 100644 --- a/src/libs/installer/remoteserverconnection.cpp +++ b/src/libs/installer/remoteserverconnection.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -429,7 +429,8 @@ void RemoteServerConnection::handleQSettings(RemoteServerReply *reply, const QSt QString key; QVariant defaultValue; data >> key; - data >> defaultValue; + if (!data.atEnd()) + data >> defaultValue; reply->send(settings->value(key, defaultValue)); } else if (command == QLatin1String(Protocol::QSettingsOrganizationName)) { reply->send(settings->organizationName()); @@ -497,11 +498,20 @@ void RemoteServerConnection::handleQFSFileEngine(RemoteServerReply *reply, const bool createParentDirectories; data >>dirName; data >>createParentDirectories; +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) reply->send(m_engine->mkdir(dirName, createParentDirectories)); +#else + reply->send(m_engine->mkdir(dirName, createParentDirectories, std::nullopt)); +#endif + } else if (command == QLatin1String(Protocol::QAbstractFileEngineOpen)) { qint32 openMode; data >>openMode; +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) reply->send(m_engine->open(static_cast<QIODevice::OpenMode> (openMode))); +#else + reply->send(m_engine->open(static_cast<QIODevice::OpenMode> (openMode), std::nullopt)); +#endif } else if (command == QLatin1String(Protocol::QAbstractFileEngineOwner)) { qint32 owner; data >>owner; @@ -570,7 +580,11 @@ void RemoteServerConnection::handleQFSFileEngine(RemoteServerReply *reply, const } else if (command == QLatin1String(Protocol::QAbstractFileEngineFileTime)) { qint32 filetime; data >> filetime; +#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0) reply->send(m_engine->fileTime(static_cast<QAbstractFileEngine::FileTime> (filetime))); +#else + reply->send(m_engine->fileTime(static_cast<QFile::FileTime> (filetime))); +#endif } else if (!command.isEmpty()) { qCDebug(QInstaller::lcServer) << "Unknown QAbstractFileEngine command:" << command; } diff --git a/src/libs/installer/remoteserverconnection_p.h b/src/libs/installer/remoteserverconnection_p.h index 977a64711..09e6de7d7 100644 --- a/src/libs/installer/remoteserverconnection_p.h +++ b/src/libs/installer/remoteserverconnection_p.h @@ -52,7 +52,7 @@ private: connect(process, &QIODevice::bytesWritten, this, &QProcessSignalReceiver::onBytesWritten); connect(process, &QIODevice::aboutToClose, this, &QProcessSignalReceiver::onAboutToClose); connect(process, &QIODevice::readChannelFinished, this, &QProcessSignalReceiver::onReadChannelFinished); - connect(process, SIGNAL(error(QProcess::ProcessError)), + connect(process, SIGNAL(errorOccurred(QProcess::ProcessError)), SLOT(onError(QProcess::ProcessError))); connect(process, &QProcess::readyReadStandardOutput, this, &QProcessSignalReceiver::onReadyReadStandardOutput); diff --git a/src/libs/installer/repository.cpp b/src/libs/installer/repository.cpp index 4ef8f9f25..f7035e732 100644 --- a/src/libs/installer/repository.cpp +++ b/src/libs/installer/repository.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -35,7 +35,7 @@ #include <QDir> /*! - \fn inline uint QInstaller::qHash(const Repository &repository) + \fn inline hashValue QInstaller::qHash(const Repository &repository) Returns a hash of the \a repository. */ @@ -49,6 +49,7 @@ Repository::Repository() : m_default(false) , m_enabled(false) , m_compressed(false) + , m_postLoadComponentScript(false) { } @@ -62,9 +63,10 @@ Repository::Repository(const Repository &other) , m_username(other.m_username) , m_password(other.m_password) , m_displayname(other.m_displayname) - , m_compressed(other.m_compressed) , m_categoryname(other.m_categoryname) + , m_compressed(other.m_compressed) , m_xmlChecksum(other.m_xmlChecksum) + , m_postLoadComponentScript(other.m_postLoadComponentScript) { } @@ -77,6 +79,7 @@ Repository::Repository(const QUrl &url, bool isDefault, bool compressed) , m_default(isDefault) , m_enabled(true) , m_compressed(compressed) + , m_postLoadComponentScript(false) { } @@ -252,6 +255,22 @@ bool Repository::isCompressed() const } /*! + \internal +*/ +bool Repository::postLoadComponentScript() const +{ + return m_postLoadComponentScript; +} + +/*! + \internal +*/ +void Repository::setPostLoadComponentScript(const bool postLoad) +{ + m_postLoadComponentScript = postLoad; +} + +/*! Compares the values of this repository to \a other and returns true if they are equal (same server, default state, enabled state as well as username and password). \sa operator!=() */ @@ -259,7 +278,8 @@ bool Repository::operator==(const Repository &other) const { return m_url == other.m_url && m_default == other.m_default && m_enabled == other.m_enabled && m_username == other.m_username && m_password == other.m_password - && m_displayname == other.m_displayname && m_xmlChecksum == other.m_xmlChecksum; + && m_displayname == other.m_displayname && m_xmlChecksum == other.m_xmlChecksum + && m_postLoadComponentScript == other.m_postLoadComponentScript; } /*! @@ -288,6 +308,7 @@ const Repository &Repository::operator=(const Repository &other) m_compressed = other.m_compressed; m_categoryname = other.m_categoryname; m_xmlChecksum = other.m_xmlChecksum; + m_postLoadComponentScript = other.m_postLoadComponentScript; return *this; } @@ -307,7 +328,7 @@ QDataStream &operator>>(QDataStream &istream, Repository &repository) { QByteArray url, username, password, displayname, compressed; istream >> url >> repository.m_default >> repository.m_enabled >> username >> password - >> displayname >> repository.m_categoryname >> repository.m_xmlChecksum; + >> displayname >> repository.m_categoryname >> repository.m_xmlChecksum >> repository.m_postLoadComponentScript; repository.setUrl(QUrl::fromEncoded(QByteArray::fromBase64(url))); repository.setUsername(QString::fromUtf8(QByteArray::fromBase64(username))); repository.setPassword(QString::fromUtf8(QByteArray::fromBase64(password))); @@ -323,7 +344,7 @@ QDataStream &operator<<(QDataStream &ostream, const Repository &repository) return ostream << repository.m_url.toEncoded().toBase64() << repository.m_default << repository.m_enabled << repository.m_username.toUtf8().toBase64() << repository.m_password.toUtf8().toBase64() << repository.m_displayname.toUtf8().toBase64() << repository.m_categoryname.toUtf8().toBase64() - << repository.m_xmlChecksum.toBase64(); + << repository.m_xmlChecksum.toBase64() << repository.m_postLoadComponentScript; } } diff --git a/src/libs/installer/repository.h b/src/libs/installer/repository.h index 1edb83449..0a589a43e 100644 --- a/src/libs/installer/repository.h +++ b/src/libs/installer/repository.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -71,10 +71,13 @@ public: void setXmlChecksum(const QByteArray &checksum); bool isCompressed() const; + bool postLoadComponentScript() const; + void setPostLoadComponentScript(const bool postLoad); + bool operator==(const Repository &other) const; bool operator!=(const Repository &other) const; - uint qHash(const Repository &repository); + hashValue qHash(const Repository &repository); const Repository &operator=(const Repository &other); friend INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, Repository &repository); @@ -90,9 +93,10 @@ private: QString m_categoryname; bool m_compressed; QByteArray m_xmlChecksum; + bool m_postLoadComponentScript; }; -inline uint qHash(const Repository &repository) +inline hashValue qHash(const Repository &repository) { return qHash(repository.url()); } diff --git a/src/libs/installer/repositorycategory.cpp b/src/libs/installer/repositorycategory.cpp index 6be292330..c9bee6e3a 100644 --- a/src/libs/installer/repositorycategory.cpp +++ b/src/libs/installer/repositorycategory.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -35,7 +35,7 @@ #include <QStringList> /*! - \fn inline uint QInstaller::qHash(const RepositoryCategory &repository) + \fn inline hashValue QInstaller::qHash(const RepositoryCategory &repository) Returns a hash of the repository category \a repository. */ @@ -65,8 +65,8 @@ RepositoryCategory::RepositoryCategory() Constructs a new category by using all fields of the given category \a other. */ RepositoryCategory::RepositoryCategory(const RepositoryCategory &other) - : m_displayname(other.m_displayname), m_data(other.m_data), m_enabled(other.m_enabled), - m_tooltip(other.m_tooltip) + : m_data(other.m_data), m_displayname(other.m_displayname), m_tooltip(other.m_tooltip), + m_enabled(other.m_enabled) { registerMetaType(); } diff --git a/src/libs/installer/repositorycategory.h b/src/libs/installer/repositorycategory.h index dc45527eb..993ae78aa 100644 --- a/src/libs/installer/repositorycategory.h +++ b/src/libs/installer/repositorycategory.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -31,6 +31,7 @@ #include "installer_global.h" #include "repository.h" +#include "qinstallerglobal.h" #include <QtCore/QMetaType> #include <QtCore/QUrl> @@ -63,7 +64,7 @@ public: bool operator==(const RepositoryCategory &other) const; bool operator!=(const RepositoryCategory &other) const; - uint qHash(const RepositoryCategory &repository); + hashValue qHash(const RepositoryCategory &repository); friend INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, RepositoryCategory &repository); friend INSTALLER_EXPORT QDataStream &operator<<(QDataStream &ostream, const RepositoryCategory &repository); @@ -75,7 +76,7 @@ private: bool m_enabled; }; -inline uint qHash(const RepositoryCategory &repository) +inline hashValue qHash(const RepositoryCategory &repository) { return qHash(repository.displayname()); } diff --git a/src/libs/installer/resources/installer.qrc b/src/libs/installer/resources/installer.qrc index 48a7c65bd..a3855b5c4 100644 --- a/src/libs/installer/resources/installer.qrc +++ b/src/libs/installer/resources/installer.qrc @@ -7,5 +7,6 @@ <file>uninstall.png</file> <file>keepinstalled.png</file> <file>keepuninstalled.png</file> + <file>qt/etc/qt.conf</file> </qresource> </RCC> diff --git a/src/libs/installer/resources/qt/etc/qt.conf b/src/libs/installer/resources/qt/etc/qt.conf new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/libs/installer/resources/qt/etc/qt.conf diff --git a/src/libs/installer/scriptengine.cpp b/src/libs/installer/scriptengine.cpp index baf348868..7e3e69eb2 100644 --- a/src/libs/installer/scriptengine.cpp +++ b/src/libs/installer/scriptengine.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2023 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -56,18 +56,6 @@ namespace QInstaller { /*! \inmodule QtInstallerFramework - \class QInstaller::ConsoleProxy - \internal -*/ - -/*! - \inmodule QtInstallerFramework - \class QInstaller::InstallerProxy - \internal -*/ - -/*! - \inmodule QtInstallerFramework \class QInstaller::QDesktopServicesProxy \internal */ @@ -78,28 +66,6 @@ namespace QInstaller { \internal */ -QJSValue InstallerProxy::components(const QString ®exp) const -{ - if (m_core) { - const QList<Component*> all = m_core->components(PackageManagerCore::ComponentType::All, regexp); - QJSValue scriptComponentsObject = m_engine->newArray(all.count()); - for (int i = 0; i < all.count(); ++i) { - Component *const component = all.at(i); - QQmlEngine::setObjectOwnership(component, QQmlEngine::CppOwnership); - scriptComponentsObject.setProperty(i, m_engine->newQObject(component)); - } - return scriptComponentsObject; - } - return m_engine->newArray(); -} - -QJSValue InstallerProxy::componentByName(const QString &componentName) -{ - if (m_core) - return m_engine->newQObject(m_core->componentByName(componentName), false); - return QJSValue(); -} - QJSValue QDesktopServicesProxy::findFiles(const QString &path, const QString &pattern) { QStringList result; @@ -223,6 +189,12 @@ bool GuiProxy::isButtonEnabled(int wizardButton) return m_gui->isButtonEnabled(wizardButton); } +void GuiProxy::setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText) +{ + if (m_gui) + m_gui->setWizardPageButtonText(pageId, buttonId, buttonText); +} + void GuiProxy::showSettingsButton(bool show) { if (m_gui) @@ -382,14 +354,10 @@ ScriptEngine::ScriptEngine(PackageManagerCore *core) : QObject(core) , m_guiProxy(new GuiProxy(this, this)) , m_core(core) { - m_engine.installExtensions(QJSEngine::TranslationExtension); + m_engine.installExtensions(QJSEngine::TranslationExtension | QJSEngine::ConsoleExtension); QJSValue global = m_engine.globalObject(); - global.setProperty(QLatin1String("console"), m_engine.newQObject(new ConsoleProxy)); + global.setProperty(QLatin1String("QFileDialog"), m_engine.newQObject(new QFileDialogProxy(core))); - const QJSValue proxy = m_engine.newQObject(new InstallerProxy(this, core)); - global.setProperty(QLatin1String("InstallerProxy"), proxy); - global.setProperty(QLatin1String("print"), m_engine.newQObject(new ConsoleProxy) - .property(QLatin1String("log"))); global.setProperty(QLatin1String("systemInfo"), m_engine.newQObject(new SystemInfo)); global.setProperty(QLatin1String("QInstaller"), generateQInstallerObject()); @@ -409,11 +377,6 @@ ScriptEngine::ScriptEngine(PackageManagerCore *core) : QObject(core) global.setProperty(QLatin1String("installer"), m_engine.newQObject(new QObject)); } global.setProperty(QLatin1String("gui"), m_engine.newQObject(m_guiProxy)); - - global.property(QLatin1String("installer")).setProperty(QLatin1String("components"), - proxy.property(QLatin1String("components"))); - global.property(QLatin1String("installer")).setProperty(QLatin1String("componentByName"), - proxy.property(QLatin1String("componentByName"))); } /*! @@ -661,14 +624,19 @@ QJSValue ScriptEngine::generateDesktopServicesObject() SETPROPERTY(desktopServices, PicturesLocation, QStandardPaths) SETPROPERTY(desktopServices, TempLocation, QStandardPaths) SETPROPERTY(desktopServices, HomeLocation, QStandardPaths) - SETPROPERTY(desktopServices, AppDataLocation, QStandardPaths) + SETPROPERTY(desktopServices, AppLocalDataLocation, QStandardPaths) SETPROPERTY(desktopServices, CacheLocation, QStandardPaths) + SETPROPERTY(desktopServices, GenericCacheLocation, QStandardPaths) SETPROPERTY(desktopServices, GenericDataLocation, QStandardPaths) SETPROPERTY(desktopServices, RuntimeLocation, QStandardPaths) SETPROPERTY(desktopServices, ConfigLocation, QStandardPaths) SETPROPERTY(desktopServices, DownloadLocation, QStandardPaths) SETPROPERTY(desktopServices, GenericCacheLocation, QStandardPaths) SETPROPERTY(desktopServices, GenericConfigLocation, QStandardPaths) + SETPROPERTY(desktopServices, AppDataLocation, QStandardPaths) + SETPROPERTY(desktopServices, AppConfigLocation, QStandardPaths) + SETPROPERTY(desktopServices, PublicShareLocation, QStandardPaths) + SETPROPERTY(desktopServices, TemplatesLocation, QStandardPaths) QJSValue object = m_engine.newQObject(new QDesktopServicesProxy(this)); object.setPrototype(desktopServices); // attach the properties diff --git a/src/libs/installer/scriptengine_p.h b/src/libs/installer/scriptengine_p.h index a0936fe75..101d4f303 100644 --- a/src/libs/installer/scriptengine_p.h +++ b/src/libs/installer/scriptengine_p.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -43,36 +43,6 @@ class PackageManagerCore; class PackageManagerGui; class ScriptEngine; -class ConsoleProxy : public QObject -{ - Q_OBJECT - Q_DISABLE_COPY(ConsoleProxy) - -public: - ConsoleProxy() {} - -public slots : - void log(const QString &log) { qCDebug(QInstaller::lcInstallerInstallLog).noquote() << log; } -}; - -class InstallerProxy : public QObject -{ - Q_OBJECT - Q_DISABLE_COPY(InstallerProxy) - -public: - InstallerProxy(ScriptEngine *engine, PackageManagerCore *core) - : m_engine(engine), m_core(core) {} - -public slots: - QJSValue components(const QString ®exp = QString()) const; - QJSValue componentByName(const QString &componentName); - -private: - ScriptEngine *m_engine; - PackageManagerCore *m_core; -}; - class QFileDialogProxy : public QObject { Q_OBJECT @@ -146,6 +116,7 @@ public: Q_INVOKABLE void clickButton(int wizardButton, int delayInMs = 0); Q_INVOKABLE void clickButton(const QString &objectName, int delayInMs = 0) const; Q_INVOKABLE bool isButtonEnabled(int wizardButton); + Q_INVOKABLE void setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText); Q_INVOKABLE void showSettingsButton(bool show); Q_INVOKABLE void setSettingsButtonEnabled(bool enable); @@ -178,8 +149,6 @@ private: } // namespace QInstaller -Q_DECLARE_METATYPE(QInstaller::ConsoleProxy*) -Q_DECLARE_METATYPE(QInstaller::InstallerProxy*) Q_DECLARE_METATYPE(QInstaller::QFileDialogProxy*) Q_DECLARE_METATYPE(QInstaller::QDesktopServicesProxy*) diff --git a/src/libs/installer/settings.cpp b/src/libs/installer/settings.cpp index 85ab0dece..e18f63689 100644 --- a/src/libs/installer/settings.cpp +++ b/src/libs/installer/settings.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -318,13 +318,13 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix, elementList << scName << scVersion << scTitle << scPublisher << scProductUrl << scTargetDir << scAdminTargetDir << scInstallerApplicationIcon << scInstallerWindowIcon - << scLogo << scWatermark << scBanner << scBackground << scPageListPixmap + << scLogo << scWatermark << scBanner << scBackground << scPageListPixmap << scAliasDefinitionsFile << scStartMenuDir << scMaintenanceToolName << scMaintenanceToolIniFile << scMaintenanceToolAlias << scRemoveTargetDir << scLocalCacheDir << scPersistentLocalCache << scRunProgram << scRunProgramArguments << scRunProgramDescription << scDependsOnLocalInstallerBinary - << scAllowSpaceInPath << scAllowNonAsciiCharacters << scDisableAuthorizationFallback - << scDisableCommandLineInterface + << scAllowSpaceInPath << scAllowNonAsciiCharacters << scAllowRepositoriesForOfflineInstaller + << scDisableAuthorizationFallback << scDisableCommandLineInterface << scWizardStyle << scStyleSheet << scTitleColor << scWizardDefaultWidth << scWizardDefaultHeight << scWizardMinimumWidth << scWizardMinimumHeight << scWizardShowPageList << scProductImages @@ -485,11 +485,11 @@ static int lengthToInt(const QVariant &variant) QString length = variant.toString().trimmed(); if (length.endsWith(QLatin1String("em"), Qt::CaseInsensitive)) { length.chop(2); - return qRound(length.toDouble() * QApplication::fontMetrics().height()); + return qRound(length.toDouble() * QFontMetricsF(QApplication::font()).height()); } if (length.endsWith(QLatin1String("ex"), Qt::CaseInsensitive)) { length.chop(2); - return qRound(length.toDouble() * QApplication::fontMetrics().xHeight()); + return qRound(length.toDouble() * QFontMetricsF(QApplication::font()).xHeight()); } if (length.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) { length.chop(2); @@ -539,6 +539,11 @@ void Settings::setProductImages(const QMap<QString, QVariant> &images) d->m_data.insert(scProductImages, QVariant::fromValue(images)); } +QString Settings::aliasDefinitionsFile() const +{ + return d->absolutePathFromKey(scAliasDefinitionsFile); +} + QString Settings::installerApplicationIcon() const { return d->absolutePathFromKey(scInstallerApplicationIcon, systemIconSuffix()); @@ -644,6 +649,11 @@ bool Settings::allowNonAsciiCharacters() const return d->m_data.value(scAllowNonAsciiCharacters, false).toBool(); } +bool Settings::allowRepositoriesForOfflineInstaller() const +{ + return d->m_data.value(scAllowRepositoriesForOfflineInstaller, true).toBool(); +} + bool Settings::disableAuthorizationFallback() const { return d->m_data.value(scDisableAuthorizationFallback, false).toBool(); diff --git a/src/libs/installer/settings.h b/src/libs/installer/settings.h index f98319110..85b59869c 100644 --- a/src/libs/installer/settings.h +++ b/src/libs/installer/settings.h @@ -95,6 +95,8 @@ public: QMap<QString, QVariant> productImages() const; void setProductImages(const QMap<QString, QVariant> &images); + QString aliasDefinitionsFile() const; + QString applicationName() const; QString version() const; @@ -143,6 +145,8 @@ public: bool allowSpaceInPath() const; bool allowNonAsciiCharacters() const; + bool allowRepositoriesForOfflineInstaller() const; + bool disableAuthorizationFallback() const; bool disableCommandLineInterface() const; diff --git a/src/libs/installer/simplemovefileoperation.cpp b/src/libs/installer/simplemovefileoperation.cpp index 5f3000be0..5bbbdabb4 100644 --- a/src/libs/installer/simplemovefileoperation.cpp +++ b/src/libs/installer/simplemovefileoperation.cpp @@ -93,7 +93,7 @@ bool SimpleMoveFileOperation::performOperation() bool SimpleMoveFileOperation::undoOperation() { - if (parseUndoOperationArguments().count() > 0) + if (skipUndoOperation()) return true; const QString source = arguments().at(0); const QString target = arguments().at(1); diff --git a/src/libs/installer/sysinfo_win.cpp b/src/libs/installer/sysinfo_win.cpp index e74eb1d4e..e5df8e35f 100644 --- a/src/libs/installer/sysinfo_win.cpp +++ b/src/libs/installer/sysinfo_win.cpp @@ -64,7 +64,7 @@ VolumeInfo updateVolumeSizeInformation(const VolumeInfo &info) return update; } -/*! +/* Returns a list of volume info objects that are mounted as network drive shares. */ QList<VolumeInfo> networkVolumeInfosFromMountPoints() @@ -95,7 +95,7 @@ QList<VolumeInfo> networkVolumeInfosFromMountPoints() return volumes; } -/*! +/* Returns a list of volume info objects based on the given \a volumeGUID. The function also solves mounted volume folder paths. It does not return any network drive shares. */ diff --git a/src/libs/installer/systeminfo.cpp b/src/libs/installer/systeminfo.cpp index 6a1976c4a..aed02c569 100644 --- a/src/libs/installer/systeminfo.cpp +++ b/src/libs/installer/systeminfo.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -55,6 +55,7 @@ SystemInfo::SystemInfo(QObject *parent) : QObject(parent) \list \li "i386" \li "x86_64" + \li "arm64" \endlist \note This function depends on what the OS will report and may not detect the actual CPU @@ -62,7 +63,7 @@ SystemInfo::SystemInfo(QObject *parent) : QObject(parent) OS running on a 64-bit CPU is usually unable to determine whether the CPU is actually capable of running 64-bit programs. - \sa QSysInfo::currentCpuArchitecture() + \sa QSysInfo::currentCpuArchitecture() buildCpuArchitecture() */ QString SystemInfo::currentCpuArchitecture() const { @@ -70,6 +71,29 @@ QString SystemInfo::currentCpuArchitecture() const } /*! + \property SystemInfo::buildCpuArchitecture + + The architecture of the CPU that the application was compiled for, in text format. + + Possible values include: + \list + \li "i386" + \li "x86_64" + \li "arm64" + \endlist + + \note Note that this may not match the actual CPU that the application is running on if + there's an emulation layer or if the CPU supports multiple architectures (like x86-64 + processors supporting i386 applications). To detect that, use \c installer.currentCpuArchitecture() + + \sa QSysInfo::buildCpuArchitecture() currentCpuArchitecture() +*/ +QString SystemInfo::buildCpuArchitecture() const +{ + return QSysInfo::buildCpuArchitecture(); +} + +/*! \property SystemInfo::kernelType The type of the operating system kernel the installer was compiled for. It is also the @@ -124,7 +148,7 @@ QString SystemInfo::kernelVersion() const \list \li "windows" \li "opensuse" (for the Linux openSUSE distribution) - \li "osx" + \li "macos" \endlist \sa QSysInfo::productType() diff --git a/src/libs/installer/systeminfo.h b/src/libs/installer/systeminfo.h index c5451605e..a1393397a 100644 --- a/src/libs/installer/systeminfo.h +++ b/src/libs/installer/systeminfo.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -39,6 +39,7 @@ class SystemInfo : public QObject Q_DISABLE_COPY(SystemInfo) Q_PROPERTY(QString currentCpuArchitecture READ currentCpuArchitecture CONSTANT) + Q_PROPERTY(QString buildCpuArchitecture READ buildCpuArchitecture CONSTANT) Q_PROPERTY(QString kernelType READ kernelType CONSTANT) Q_PROPERTY(QString kernelVersion READ kernelVersion CONSTANT) Q_PROPERTY(QString productType READ productType CONSTANT) @@ -49,7 +50,7 @@ public: explicit SystemInfo(QObject *parent = 0); QString currentCpuArchitecture() const; - + QString buildCpuArchitecture() const; QString kernelType() const; QString kernelVersion() const; QString productType() const; diff --git a/src/libs/installer/utils.cpp b/src/libs/installer/utils.cpp index 154a66553..9b64ade37 100644 --- a/src/libs/installer/utils.cpp +++ b/src/libs/installer/utils.cpp @@ -171,7 +171,7 @@ QByteArray QInstaller::calculateHash(QIODevice *device, QCryptographicHash::Algo const qint64 numRead = device->read(buffer.data(), buffer.size()); if (numRead <= 0) return hash.result(); - hash.addData(buffer.constData(), numRead); + hash.addData(buffer.left(numRead)); } return QByteArray(); // never reached } diff --git a/src/libs/kdtools/filedownloader.cpp b/src/libs/kdtools/filedownloader.cpp index 6152811aa..c99383892 100644 --- a/src/libs/kdtools/filedownloader.cpp +++ b/src/libs/kdtools/filedownloader.cpp @@ -650,7 +650,7 @@ void KDUpdater::FileDownloader::emitEstimatedDownloadTime() } /*! - \overload addCheckSumData() + Adds checksum \a data. */ void KDUpdater::FileDownloader::addCheckSumData(const QByteArray &data) { @@ -658,14 +658,6 @@ void KDUpdater::FileDownloader::addCheckSumData(const QByteArray &data) } /*! - Adds the \a length of characters of \a data to the cryptographic hash of the downloaded file. -*/ -void KDUpdater::FileDownloader::addCheckSumData(const char *data, int length) -{ - d->m_hash.addData(data, length); -} - -/*! Resets SHA-1 checksum data of the downloaded file. */ void KDUpdater::FileDownloader::resetCheckSumData() @@ -921,7 +913,7 @@ void KDUpdater::LocalFileDownloader::timerEvent(QTimerEvent *event) toWrite -= numWritten; } addSample(numRead); - addCheckSumData(buffer.data(), numRead); + addCheckSumData(buffer.left(numRead)); if (numRead > 0) { setProgress(d->source->pos(), d->source->size()); emit downloadProgress(calcProgress(d->source->pos(), d->source->size())); @@ -1113,7 +1105,7 @@ void KDUpdater::ResourceFileDownloader::timerEvent(QTimerEvent *event) const qint64 numRead = d->destFile.read(buffer.data(), buffer.size()); addSample(numRead); - addCheckSumData(buffer.data(), numRead); + addCheckSumData(buffer.left(numRead)); if (numRead > 0) { setProgress(d->destFile.pos(), d->destFile.size()); @@ -1192,9 +1184,8 @@ struct KDUpdater::HttpDownloader::Private disconnect(http, &QNetworkReply::finished, q, &HttpDownloader::httpReqFinished); disconnect(http, &QNetworkReply::downloadProgress, q, &HttpDownloader::httpReadProgress); - void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error; - disconnect(http, errorSignal, q, &HttpDownloader::httpError); + disconnect(http, &QNetworkReply::errorOccurred, q, &HttpDownloader::httpError); http->deleteLater(); } http = 0; @@ -1220,8 +1211,14 @@ KDUpdater::HttpDownloader::HttpDownloader(QObject *parent) #endif connect(&d->manager, &QNetworkAccessManager::authenticationRequired, this, &HttpDownloader::onAuthenticationRequired); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) connect(&d->manager, &QNetworkAccessManager::networkAccessibleChanged, this, &HttpDownloader::onNetworkAccessibleChanged); +#else + auto netInfo = QNetworkInformation::instance(); + connect(netInfo, &QNetworkInformation::reachabilityChanged, + this, &HttpDownloader::onReachabilityChanged); +#endif } @@ -1314,7 +1311,7 @@ void KDUpdater::HttpDownloader::httpReadyRead() written += numWritten; } addSample(written); - addCheckSumData(buffer.data(), read); + addCheckSumData(buffer.left(read)); updateBytesDownloadedBeforeResume(written); } } @@ -1476,13 +1473,17 @@ void KDUpdater::HttpDownloader::startDownload(const QUrl &url) d->m_authenticationCount = 0; d->manager.setProxyFactory(proxyFactory()); clearBytesDownloadedBeforeResume(); - d->http = d->manager.get(QNetworkRequest(url)); + + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy); + request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false); + + d->http = d->manager.get(request); connect(d->http, &QIODevice::readyRead, this, &HttpDownloader::httpReadyRead); connect(d->http, &QNetworkReply::downloadProgress, this, &HttpDownloader::httpReadProgress); connect(d->http, &QNetworkReply::finished, this, &HttpDownloader::httpReqFinished); - void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error; - connect(d->http, errorSignal, this, &HttpDownloader::httpError); + connect(d->http, &QNetworkReply::errorOccurred, this, &HttpDownloader::httpError); bool fileOpened = false; if (d->destFileName.isEmpty()) { @@ -1509,9 +1510,9 @@ void KDUpdater::HttpDownloader::startDownload(const QUrl &url) qCWarning(QInstaller::lcInstallerInstallLog) << "File exists but installer is unable to open it."; else qCWarning(QInstaller::lcInstallerInstallLog) << "File does not exist."; - d->shutDown(); setDownloadAborted(tr("Cannot download %1. Cannot create file \"%2\": %3").arg( url.toString(), d->destination->fileName(), d->destination->errorString())); + d->shutDown(); } } } @@ -1532,8 +1533,7 @@ void KDUpdater::HttpDownloader::resumeDownload() connect(d->http, &QNetworkReply::downloadProgress, this, &HttpDownloader::httpReadProgress); connect(d->http, &QNetworkReply::finished, this, &HttpDownloader::httpReqFinished); - void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error; - connect(d->http, errorSignal, this, &HttpDownloader::httpError); + connect(d->http, &QNetworkReply::errorOccurred, this, &HttpDownloader::httpError); runDownloadSpeedTimer(); runDownloadDeadlineTimer(); } @@ -1575,6 +1575,7 @@ void KDUpdater::HttpDownloader::onAuthenticationRequired(QNetworkReply *reply, Q } } +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) void KDUpdater::HttpDownloader::onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible) { if (accessible == QNetworkAccessManager::NotAccessible) { @@ -1589,6 +1590,22 @@ void KDUpdater::HttpDownloader::onNetworkAccessibleChanged(QNetworkAccessManager } } } +#else +void KDUpdater::HttpDownloader::onReachabilityChanged(QNetworkInformation::Reachability newReachability) +{ + if (newReachability == QNetworkInformation::Reachability::Online) { + if (isDownloadPaused()) { + setDownloadPaused(false); + resumeDownload(); + } + } else { + d->shutDown(false); + setDownloadPaused(true); + setDownloadResumed(false); + stopDownloadDeadlineTimer(); + } +} +#endif #ifndef QT_NO_SSL @@ -1626,7 +1643,7 @@ void KDUpdater::HttpDownloader::onSslErrors(QNetworkReply* reply, const QList<QS "the error may be temporary and you can try again."))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - msgBox.setButtonText(QMessageBox::Yes, tr("Try again")); + msgBox.addButton(tr("Try again"), QMessageBox::YesRole); msgBox.setDefaultButton(QMessageBox::Cancel); if (msgBox.exec() == QMessageBox::Cancel) { diff --git a/src/libs/kdtools/filedownloader.h b/src/libs/kdtools/filedownloader.h index 3990d849a..e71f7d62f 100644 --- a/src/libs/kdtools/filedownloader.h +++ b/src/libs/kdtools/filedownloader.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -142,7 +143,6 @@ protected: void emitEstimatedDownloadTime(); void addCheckSumData(const QByteArray &data); - void addCheckSumData(const char *data, int length); void resetCheckSumData(); private Q_SLOTS: diff --git a/src/libs/kdtools/filedownloader_p.h b/src/libs/kdtools/filedownloader_p.h index ec47e31ca..23eff08d7 100644 --- a/src/libs/kdtools/filedownloader_p.h +++ b/src/libs/kdtools/filedownloader_p.h @@ -35,6 +35,10 @@ #include <QtNetwork/QNetworkReply> #include <QNetworkAccessManager> +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include <QNetworkInformation> +#endif + // these classes are not a part of the public API namespace KDUpdater { @@ -130,7 +134,11 @@ private Q_SLOTS: void httpDone(bool error); void httpReqFinished(); void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) void onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible); +#else + void onReachabilityChanged(QNetworkInformation::Reachability newReachability); +#endif #ifndef QT_NO_SSL void onSslErrors(QNetworkReply* reply, const QList<QSslError> &errors); #endif diff --git a/src/libs/kdtools/updatefinder.cpp b/src/libs/kdtools/updatefinder.cpp index 120dcb952..dbe7825df 100644 --- a/src/libs/kdtools/updatefinder.cpp +++ b/src/libs/kdtools/updatefinder.cpp @@ -301,9 +301,9 @@ bool UpdateFinder::downloadUpdateXMLFiles() connect(downloader, SIGNAL(downloadCanceled()), this, SLOT(slotDownloadDone())); connect(downloader, SIGNAL(downloadCompleted()), this, SLOT(slotDownloadDone())); connect(downloader, SIGNAL(downloadAborted(QString)), this, SLOT(slotDownloadDone())); - m_updatesInfoList.insert(new UpdatesInfo, Data(info, downloader)); + m_updatesInfoList.insert(new UpdatesInfo(info.postLoadComponentScript), Data(info, downloader)); } else { - UpdatesInfo *updatesInfo = new UpdatesInfo; + UpdatesInfo *updatesInfo = new UpdatesInfo(info.postLoadComponentScript); updatesInfo->setFileName(QInstaller::pathFromUrl(url)); m_updatesInfoList.insert(updatesInfo, Data(info)); } diff --git a/src/libs/kdtools/updateoperation.cpp b/src/libs/kdtools/updateoperation.cpp index 6a7e62f2b..af89382a8 100644 --- a/src/libs/kdtools/updateoperation.cpp +++ b/src/libs/kdtools/updateoperation.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -292,23 +292,18 @@ QStringList UpdateOperation::parsePerformOperationArguments() } /*! - Returns undo operation argument list. If the installation is - cancelled or failed, returns an empty list so that full undo - operation can be performed. + Returns \c true if operation undo should not be performed. + Returns \c false if the installation is cancelled or failed, or + \c UNDOOPERATION is not set in operation call. */ -QStringList UpdateOperation::parseUndoOperationArguments() +bool UpdateOperation::skipUndoOperation() { //Install has failed, allow a normal undo if (m_core && (m_core->status() == QInstaller::PackageManagerCore::Canceled || m_core->status() == QInstaller::PackageManagerCore::Failure)) { - return QStringList(); - } - int index = arguments().indexOf(QLatin1String("UNDOOPERATION")); - QStringList args; - if ((index != -1) && (arguments().length() > index + 1)) { - args = arguments().mid(index + 1); + return false; } - return args; + return arguments().contains(QLatin1String("UNDOOPERATION")); } /*! @@ -577,14 +572,21 @@ QDomDocument UpdateOperation::toXml() const value.setAttribute(QLatin1String("name"), it.key()); value.setAttribute(QLatin1String("type"), QLatin1String(variant.typeName())); - if (variant.type() != QVariant::List && variant.type() != QVariant::StringList - && variant.canConvert(QVariant::String)) { - // it can convert to string? great! - value.appendChild(doc.createTextNode(QInstaller::replacePath(variant.toString(), - target, QLatin1String(QInstaller::scRelocatable)))); + int variantType; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + variantType = variant.typeId(); +#else + variantType = variant.type(); +#endif + + if (variantType != QMetaType::QStringList + && variant.canConvert<QString>()) { + // it can convert to string? great! + value.appendChild(doc.createTextNode(QInstaller::replacePath(variant.toString(), + target, QLatin1String(QInstaller::scRelocatable)))); } else { // no? then we have to go the hard way... - if (variant.type() == QVariant::StringList) { + if (variantType == QMetaType::QStringList) { QStringList list = variant.toStringList(); for (int i = 0; i < list.count(); ++i) { list[i] = QInstaller::replacePath(list.at(i), target, @@ -651,25 +653,31 @@ bool UpdateOperation::fromXml(const QDomDocument &doc) const QString type = v.attribute(QLatin1String("type")); const QString value = v.text(); + int variantType; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + const QMetaType t = QMetaType::fromName(type.toLatin1().data()); + variantType = t.id(); +#else const QVariant::Type t = QVariant::nameToType(type.toLatin1().data()); + variantType = t; +#endif QVariant var = QVariant::fromValue(value); - if (t == QVariant::List || t == QVariant::StringList || !var.convert(t)) { - QDataStream stream(QByteArray::fromBase64( value.toLatin1())); + if (variantType == QMetaType::QStringList || !var.canConvert(t)) { + QDataStream stream(QByteArray::fromBase64(value.toLatin1())); stream >> var; - if (t == QVariant::StringList) { + if (variantType == QMetaType::QStringList) { QStringList list = var.toStringList(); for (int i = 0; i < list.count(); ++i) { list[i] = QInstaller::replacePath(list.at(i), - relocatable, target); + relocatable, target); } var = QVariant::fromValue(list); } - } else if (t == QVariant::String) { - const QString str = QInstaller::replacePath(value, - relocatable, target); - var = QVariant::fromValue(str); + } else if (variantType == QMetaType::QString) { + const QString str = QInstaller::replacePath(value, + relocatable, target); + var = QVariant::fromValue(str); } - m_values[name] = var; } diff --git a/src/libs/kdtools/updateoperation.h b/src/libs/kdtools/updateoperation.h index 4a431f107..e25846cd3 100644 --- a/src/libs/kdtools/updateoperation.h +++ b/src/libs/kdtools/updateoperation.h @@ -112,7 +112,7 @@ protected: bool checkArgumentCount(int minArgCount, int maxArgCount, const QString &argDescription = QString()); bool checkArgumentCount(int argCount); QStringList parsePerformOperationArguments(); - QStringList parseUndoOperationArguments(); + bool skipUndoOperation(); void setRequiresUnreplacedVariables(bool isRequired); bool variableReplacement(QString *variableValue); diff --git a/src/libs/kdtools/updateoperations.cpp b/src/libs/kdtools/updateoperations.cpp index 9301d4f13..5f6135103 100644 --- a/src/libs/kdtools/updateoperations.cpp +++ b/src/libs/kdtools/updateoperations.cpp @@ -127,7 +127,7 @@ QString CopyOperation::sourcePath() QString CopyOperation::destinationPath() { - QString destination = arguments().last(); + QString destination = arguments().at(1); // if the target is a directory use the source filename to complete the destination path if (QFileInfo(destination).isDir()) @@ -135,7 +135,6 @@ QString CopyOperation::destinationPath() return destination; } - void CopyOperation::backup() { QString destination = destinationPath(); @@ -156,8 +155,8 @@ void CopyOperation::backup() bool CopyOperation::performOperation() { // We need two args to complete the copy operation. First arg provides the complete file name of source - // Second arg provides the complete file name of dest - if (!checkArgumentCount(2)) + // Second arg provides the complete file name of dest. Optionally UNDOOPERATION can be added as well + if (!checkArgumentCount(2, 4, QLatin1String("<source filename> <destination filename> [UNDOOPERATION, \"\"]"))) return false; QString source = sourcePath(); @@ -193,6 +192,8 @@ bool CopyOperation::performOperation() bool CopyOperation::undoOperation() { + if (skipUndoOperation()) + return true; QString source = sourcePath(); QString destination = destinationPath(); @@ -270,7 +271,7 @@ MoveOperation::~MoveOperation() void MoveOperation::backup() { - const QString dest = arguments().last(); + const QString dest = arguments().at(1); if (!QFile::exists(dest)) { clearValue(QLatin1String("backupOfExistingDestination")); return; @@ -286,9 +287,10 @@ void MoveOperation::backup() bool MoveOperation::performOperation() { - // We need two args to complete the copy operation. // First arg provides the complete file name of - // source, second arg provides the complete file name of dest - if (!checkArgumentCount(2)) + // We need two args to complete the copy operation. First arg provides the complete file name of + // source, second arg provides the complete file name of dest. Optionally UNDOOPERATION can be added as well + if (!checkArgumentCount(2, 4, QLatin1String("<complete source file name> <complete destination " + "file name> [UNDOOPERATION, \"\"]"))) return false; const QStringList args = arguments(); @@ -318,8 +320,10 @@ bool MoveOperation::performOperation() bool MoveOperation::undoOperation() { + if (skipUndoOperation()) + return true; const QStringList args = arguments(); - const QString dest = args.last(); + const QString dest = args.at(1); // first: copy back the destination to source QFile destF(dest); if (!destF.copy(args.first())) { @@ -391,7 +395,8 @@ void DeleteOperation::backup() bool DeleteOperation::performOperation() { // Requires only one parameter. That is the name of the file to remove. - if (!checkArgumentCount(1)) + // Optionally UNDOOPERATION can be added as well + if (!checkArgumentCount(1, 3, QLatin1String("<file to remove> [UNDOOPERATION, \"\"]"))) return false; return deleteFileNowOrLater(arguments().at(0)); @@ -399,7 +404,7 @@ bool DeleteOperation::performOperation() bool DeleteOperation::undoOperation() { - if (!hasValue(QLatin1String("backupOfExistingFile"))) + if (skipUndoOperation()) return true; const QString fileName = arguments().first(); @@ -478,7 +483,8 @@ void MkdirOperation::backup() bool MkdirOperation::performOperation() { // Requires only one parameter. That is the path which should be created - if (!checkArgumentCount(1)) + // Optionally UNDOOPERATION can be added as well + if (!checkArgumentCount(1, 3, QLatin1String("<file to remove> [UNDOOPERATION, \"\"]"))) return false; const QString dirName = arguments().at(0); @@ -493,7 +499,8 @@ bool MkdirOperation::performOperation() bool MkdirOperation::undoOperation() { - Q_ASSERT(arguments().count() == 1); + if (skipUndoOperation()) + return true; QString createdDirValue = value(QLatin1String("createddir")).toString(); if (packageManager()) { @@ -572,7 +579,8 @@ void RmdirOperation::backup() bool RmdirOperation::performOperation() { // Requires only one parameter. That is the name of the file to remove. - if (!checkArgumentCount(1)) + // Optionally UNDOOPERATION can be added as well + if (!checkArgumentCount(1, 3, QLatin1String("<file to remove> [UNDOOPERATION, \"\"]"))) return false; const QString firstArg = arguments().at(0); @@ -597,7 +605,7 @@ bool RmdirOperation::performOperation() bool RmdirOperation::undoOperation() { - if (!value(QLatin1String("removed")).toBool()) + if (!value(QLatin1String("removed")).toBool() || skipUndoOperation()) return true; errno = 0; @@ -633,6 +641,12 @@ AppendFileOperation::AppendFileOperation(QInstaller::PackageManagerCore *core) setName(QLatin1String("AppendFile")); } +AppendFileOperation::~AppendFileOperation() +{ + if (skipUndoOperation()) + deleteFileNowOrLater(value(QLatin1String("backupOfFile")).toString()); +} + void AppendFileOperation::backup() { const QString filename = arguments().first(); @@ -653,10 +667,10 @@ bool AppendFileOperation::performOperation() { // This operation takes two arguments. First argument is the name of the file into which a text has to be // appended. Second argument is the text to append. - if (!checkArgumentCount(2)) + if (!checkArgumentCount(2, 4, QLatin1String("<filename> <text to apply> [UNDOOPERATION, \"\"]"))) return false; - const QStringList args = this->arguments(); + const QStringList args = arguments(); const QString fName = args.at(0); QFile file(fName); if (!file.open(QFile::Append)) { @@ -695,6 +709,9 @@ bool AppendFileOperation::performOperation() bool AppendFileOperation::undoOperation() { + if (skipUndoOperation()) + return true; + // backupOfFile being empty -> file didn't exist before -> no error const QString filename = arguments().first(); const QString backupOfFile = value(QLatin1String("backupOfFile")).toString(); @@ -746,6 +763,12 @@ PrependFileOperation::PrependFileOperation(QInstaller::PackageManagerCore *core) setName(QLatin1String("PrependFile")); } +PrependFileOperation::~PrependFileOperation() +{ + if (skipUndoOperation()) + deleteFileNowOrLater(value(QLatin1String("backupOfFile")).toString()); +} + void PrependFileOperation::backup() { const QString filename = arguments().first(); @@ -767,10 +790,10 @@ bool PrependFileOperation::performOperation() // This operation takes two arguments. First argument is the name // of the file into which a text has to be appended. Second argument // is the text to append. - if (!checkArgumentCount(2)) + if (!checkArgumentCount(2, 4, QLatin1String("<filename> <text to prepend> [UNDOOPERATION, \"\"]"))) return false; - const QStringList args = this->arguments(); + const QStringList args = arguments(); const QString fName = args.at(0); // Load the file first. QFile file(fName); @@ -810,7 +833,9 @@ bool PrependFileOperation::performOperation() bool PrependFileOperation::undoOperation() { - // bockupOfFile being empty -> file didn't exist before -> no error + if (skipUndoOperation()) + return true; + const QString filename = arguments().first(); const QString backupOfFile = value(QLatin1String("backupOfFile")).toString(); if (!backupOfFile.isEmpty() && !QFile::exists(backupOfFile)) { diff --git a/src/libs/kdtools/updateoperations.h b/src/libs/kdtools/updateoperations.h index c789975e2..adbfc7de1 100644 --- a/src/libs/kdtools/updateoperations.h +++ b/src/libs/kdtools/updateoperations.h @@ -109,6 +109,7 @@ class KDTOOLS_EXPORT AppendFileOperation : public UpdateOperation Q_DECLARE_TR_FUNCTIONS(KDUpdater::AppendFileOperation) public: explicit AppendFileOperation(QInstaller::PackageManagerCore *core = 0); + ~AppendFileOperation(); void backup() override; bool performOperation() override; @@ -121,6 +122,7 @@ class KDTOOLS_EXPORT PrependFileOperation : public UpdateOperation Q_DECLARE_TR_FUNCTIONS(KDUpdater::PrependFileOperation) public: explicit PrependFileOperation(QInstaller::PackageManagerCore *core = 0); + ~PrependFileOperation(); void backup() override; bool performOperation() override; diff --git a/src/libs/kdtools/updatesinfo.cpp b/src/libs/kdtools/updatesinfo.cpp index 707daf11f..e82c1c1fb 100644 --- a/src/libs/kdtools/updatesinfo.cpp +++ b/src/libs/kdtools/updatesinfo.cpp @@ -40,8 +40,9 @@ using namespace KDUpdater; -UpdatesInfoData::UpdatesInfoData() +UpdatesInfoData::UpdatesInfoData(const bool postLoadComponentScript) : error(UpdatesInfo::NotYetReadError) + , m_postLoadComponentScript(postLoadComponentScript) { } @@ -135,7 +136,14 @@ bool UpdatesInfoData::parsePackageUpdateElement(QXmlStreamReader &reader, const parseOperations(reader, info.data); } else if (elementName == QLatin1String("Script")) { const QXmlStreamAttributes attr = reader.attributes(); - const bool postLoad = attr.value(QLatin1String("postLoad")).toString().toLower() == QInstaller::scTrue ? true : false; + bool postLoad = false; + // postLoad can be set either to individual components or to whole repositories. + // If individual components has the postLoad attribute, it overwrites the repository value. + if (attr.hasAttribute(QLatin1String("postLoad"))) + postLoad = attr.value(QLatin1String("postLoad")).toString().toLower() == QInstaller::scTrue ? true : false; + else if (m_postLoadComponentScript) + postLoad = true; + if (postLoad) scriptHash.insert(QLatin1String("postLoadScript"), reader.readElementText()); else @@ -238,8 +246,8 @@ void UpdatesInfoData::parseLicenses(QXmlStreamReader &reader, QHash<QString, QVa // // UpdatesInfo // -UpdatesInfo::UpdatesInfo() - : d(new UpdatesInfoData) +UpdatesInfo::UpdatesInfo(const bool postLoadComponentScript) + : d(new UpdatesInfoData(postLoadComponentScript)) { } diff --git a/src/libs/kdtools/updatesinfo_p.h b/src/libs/kdtools/updatesinfo_p.h index bd9885327..a3768232a 100644 --- a/src/libs/kdtools/updatesinfo_p.h +++ b/src/libs/kdtools/updatesinfo_p.h @@ -59,7 +59,7 @@ public: InvalidContentError }; - UpdatesInfo(); + UpdatesInfo(const bool postLoadComponentScript = false); ~UpdatesInfo(); bool isValid() const; diff --git a/src/libs/kdtools/updatesinfodata_p.h b/src/libs/kdtools/updatesinfodata_p.h index 9229f8577..c71a85193 100644 --- a/src/libs/kdtools/updatesinfodata_p.h +++ b/src/libs/kdtools/updatesinfodata_p.h @@ -43,7 +43,7 @@ struct UpdatesInfoData : public QSharedData Q_DECLARE_TR_FUNCTIONS(KDUpdater::UpdatesInfoData) public: - UpdatesInfoData(); + UpdatesInfoData(const bool postLoadComponentScript); ~UpdatesInfoData(); int error; @@ -53,6 +53,7 @@ public: QString applicationVersion; QString checkSha1CheckSum; QList<UpdateInfo> updateInfoList; + bool m_postLoadComponentScript; void parseFile(const QString &updateXmlFile); bool parsePackageUpdateElement(QXmlStreamReader &reader, const QString &checkSha1CheckSum); |