diff --git a/Extensions/FileImportShareExtension/ViewModel/ShareViewModel.swift b/Extensions/FileImportShareExtension/ViewModel/ShareViewModel.swift index 07322c02..4239a0eb 100644 --- a/Extensions/FileImportShareExtension/ViewModel/ShareViewModel.swift +++ b/Extensions/FileImportShareExtension/ViewModel/ShareViewModel.swift @@ -42,7 +42,7 @@ class ShareViewModel: ShareViewModelProtocol, Loggable { @discardableResult func importFiles(_ items: [ImportedFileItem]) async -> Bool { - ShareViewModel.logger().debug("Importing files...") + ShareViewModel.logger().info("Importing files...") guard !items.isEmpty else { await MainActor.run { [weak self] in self?.status = .failed @@ -57,7 +57,7 @@ class ShareViewModel: ShareViewModelProtocol, Loggable { ) if isImported { - ShareViewModel.logger().debug("Files imported successfully") + ShareViewModel.logger().info("Files imported successfully") } else { ShareViewModel.logger().error("Could not import files") } @@ -178,7 +178,7 @@ class ShareViewModel: ShareViewModelProtocol, Loggable { } func downloadFileFromUrl(_ itemUrl: URL) async -> Bool { - ShareViewModel.logger().debug("Downloading file from \(itemUrl.absoluteString)") + ShareViewModel.logger().info("Downloading file from \(itemUrl.absoluteString)") do { let destinationURL = try Directories.getTempDirectory( @@ -198,7 +198,7 @@ class ShareViewModel: ShareViewModelProtocol, Loggable { for await progress in request.downloadProgress() { let fileName = itemUrl.lastPathComponent let downloadProgress = progress.fractionCompleted * 100 - ShareViewModel.logger().debug( + ShareViewModel.logger().info( "\(String(format: "Download progress for file '%@': %.2f%%", fileName, downloadProgress))" ) } diff --git a/Modules/CommonsLib/Sources/CommonsLib/Constants.swift b/Modules/CommonsLib/Sources/CommonsLib/Constants.swift index a520687b..bc4f8d4d 100644 --- a/Modules/CommonsLib/Sources/CommonsLib/Constants.swift +++ b/Modules/CommonsLib/Sources/CommonsLib/Constants.swift @@ -98,6 +98,7 @@ public struct Constants { } public struct File { + public static let LibDigidocLog = "libdigidocpp.log" public static let LDAPCertsPem = "ldapCerts.pem" } diff --git a/Modules/ConfigLib/Sources/ConfigLib/Configuration/Loader/ConfigurationLoader.swift b/Modules/ConfigLib/Sources/ConfigLib/Configuration/Loader/ConfigurationLoader.swift index 5e3e3923..4766c49f 100644 --- a/Modules/ConfigLib/Sources/ConfigLib/Configuration/Loader/ConfigurationLoader.swift +++ b/Modules/ConfigLib/Sources/ConfigLib/Configuration/Loader/ConfigurationLoader.swift @@ -62,7 +62,7 @@ public actor ConfigurationLoader: ConfigurationLoaderProtocol, Loggable { proxyInfo: ProxyInfo, userAgent: String ) async throws { - ConfigurationLoader.logger().debug("Initializing configuration") + ConfigurationLoader.logger().info("Initializing configuration") if !fileManager.fileExists(atPath: cacheDir.resolvedPath) { try fileManager.createDirectory( @@ -74,7 +74,7 @@ public actor ConfigurationLoader: ConfigurationLoaderProtocol, Loggable { try await loadConfigurationProperty() if try await shouldCheckForUpdates() { - ConfigurationLoader.logger().debug("Checking for configuration updates...") + ConfigurationLoader.logger().info("Checking for configuration updates...") try await loadCentralConfiguration( cacheDir: cacheDir, proxyInfo: proxyInfo, @@ -82,7 +82,7 @@ public actor ConfigurationLoader: ConfigurationLoaderProtocol, Loggable { ) } - ConfigurationLoader.logger().debug("Finished initializing configuration") + ConfigurationLoader.logger().info("Finished initializing configuration") finishConfigurationUpdate() } @@ -132,7 +132,7 @@ public actor ConfigurationLoader: ConfigurationLoaderProtocol, Loggable { fileManager.fileExists(atPath: signatureFile.resolvedPath) if configFilesExist { - ConfigurationLoader.logger().debug("Initializing cached configuration") + ConfigurationLoader.logger().info("Initializing cached configuration") let confFileContents = try String(contentsOf: confFile, encoding: .utf8) let publicKeyContents = try String(contentsOf: publicKeyFile, encoding: .utf8) @@ -149,7 +149,7 @@ public actor ConfigurationLoader: ConfigurationLoaderProtocol, Loggable { from: Data(contentsOf: confFile) ) - ConfigurationLoader.logger().debug( + ConfigurationLoader.logger().info( "Using cached configuration version \(configurationProvider.metaInf.serial)" ) @@ -187,7 +187,7 @@ public actor ConfigurationLoader: ConfigurationLoaderProtocol, Loggable { updateConfiguration(configurationProvider) } } else { - ConfigurationLoader.logger().debug( + ConfigurationLoader.logger().info( "Cached configuration not found. Initializing default configuration") try await loadDefaultConfiguration(cacheDir: configDir) } @@ -247,7 +247,7 @@ public actor ConfigurationLoader: ConfigurationLoaderProtocol, Loggable { ConfigurationProvider.self, from: Data(contentsOf: confDataURL) ) - ConfigurationLoader.logger().debug( + ConfigurationLoader.logger().info( "Initializing default configuration version \(configurationProvider.metaInf.serial)" ) @@ -291,7 +291,7 @@ public actor ConfigurationLoader: ConfigurationLoaderProtocol, Loggable { ).trimmingCharacters(in: .whitespaces) if !centralSignature.isEmpty && currentSignature != centralSignature.data(using: .utf8) { - ConfigurationLoader.logger().debug("Found new configuration") + ConfigurationLoader.logger().info("Found new configuration") let centralConfig = try await centralConfigurationRepository.fetchConfiguration( proxyInfo: proxyInfo, @@ -306,7 +306,7 @@ public actor ConfigurationLoader: ConfigurationLoaderProtocol, Loggable { let centralConfigurationProvider = try JSONDecoder().decode( ConfigurationProvider.self, from: Data(centralConfig.utf8) ) - ConfigurationLoader.logger().debug( + ConfigurationLoader.logger().info( "Initializing configuration version \(centralConfigurationProvider.metaInf.serial)" ) @@ -350,7 +350,7 @@ public actor ConfigurationLoader: ConfigurationLoaderProtocol, Loggable { try await loadCachedConfiguration(afterCentralCheck: true, cacheDir: configDir) } } else { - ConfigurationLoader.logger().debug( + ConfigurationLoader.logger().info( "New configuration not found. Using cached configuration" ) try await loadCachedConfiguration(afterCentralCheck: true, cacheDir: configDir) diff --git a/Modules/CryptoLib/Sources/CryptoSwift/CryptoContainer.swift b/Modules/CryptoLib/Sources/CryptoSwift/CryptoContainer.swift index ce6ecb25..89d34db7 100644 --- a/Modules/CryptoLib/Sources/CryptoSwift/CryptoContainer.swift +++ b/Modules/CryptoLib/Sources/CryptoSwift/CryptoContainer.swift @@ -200,7 +200,7 @@ extension CryptoContainer { dataFiles: [URL], containerUtil: ContainerUtilProtocol = Container.shared.containerUtil(), ) async throws -> CryptoContainerProtocol { - logger().debug("Opening or creating crypto container. Found \(dataFiles.count) datafile(s)") + logger().info("Opening or creating crypto container. Found \(dataFiles.count) datafile(s)") guard let firstFile = dataFiles.first else { logger().error("Unable to create or open crypto container. First datafile is nil") throw CryptoError.containerCreationFailed( @@ -245,10 +245,10 @@ extension CryptoContainer { } if dataFiles.count == 1 && isFirstDataFileContainer { - CryptoContainer.logger().debug("Opening existing crypto container") + CryptoContainer.logger().info("Opening existing crypto container") return try await open(containerFile: containerFile) } else { - CryptoContainer.logger().debug("Creating a new crypto container") + CryptoContainer.logger().info("Creating a new crypto container") return try await create( containerFile: containerFile, dataFiles: dataFiles, diff --git a/Modules/CryptoLib/Sources/CryptoSwift/Ldap/OpenLdap.swift b/Modules/CryptoLib/Sources/CryptoSwift/Ldap/OpenLdap.swift index 8579eb0b..7ad8bdaf 100644 --- a/Modules/CryptoLib/Sources/CryptoSwift/Ldap/OpenLdap.swift +++ b/Modules/CryptoLib/Sources/CryptoSwift/Ldap/OpenLdap.swift @@ -92,14 +92,14 @@ final public class OpenLdap: OpenLdapProtocol, Loggable { if fileManager.fileExists(atPath: ldapCertFilePath) { filePath = ldapCertFilePath } else { - OpenLdap.logger().debug("File ldapCerts.pem does not exist at directory path: \(ldapCertFilePath)") + OpenLdap.logger().info("File ldapCerts.pem does not exist at directory path: \(ldapCertFilePath)") filePath = nil } } let searchType = SearchType(from: identityCode) if case .personalCode = searchType { - OpenLdap.logger().debug("Searching with personal code from LDAP") + OpenLdap.logger().info("Searching with personal code from LDAP") var result = [Addressee]() var tooManyResults = false for url in await self.ldapConfiguration.getLdapPersonURLS() { @@ -117,7 +117,7 @@ final public class OpenLdap: OpenLdapProtocol, Loggable { return (result, tooManyResults) } else { if let ldapCorpURL = await self.ldapConfiguration.getLdapCorpURL() { - OpenLdap.logger().debug("Searching with corporation keyword from LDAP") + OpenLdap.logger().info("Searching with corporation keyword from LDAP") let (addresses, found) = OpenLdap.search( searchType: searchType, url: ldapCorpURL, @@ -146,7 +146,7 @@ final public class OpenLdap: OpenLdapProtocol, Loggable { var ldapConnectionReset = 0 let result = ldap_set_option(nil, LDAP_OPT_X_TLS_NEWCTX, &ldapConnectionReset) guard result == LDAP_SUCCESS else { - OpenLdap.logger().debug( + OpenLdap.logger().info( "ldap_set_option(LDAP_OPT_X_TLS_NEWCTX) failed: \(String(cString: ldap_err2string(result)))" ) return ([], 0) @@ -168,14 +168,14 @@ final public class OpenLdap: OpenLdapProtocol, Loggable { if let ldap = ldap { ldap_destroy(ldap) } } guard ldapReturnCode == LDAP_SUCCESS else { - OpenLdap.logger().debug("Failed to initialize LDAP: \(String(cString: ldap_err2string(ldapReturnCode)))") + OpenLdap.logger().info("Failed to initialize LDAP: \(String(cString: ldap_err2string(ldapReturnCode)))") return ([], 0) } var ldapVersion = LDAP_VERSION3 ldapReturnCode = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion) guard ldapReturnCode == LDAP_SUCCESS else { - OpenLdap.logger().debug( + OpenLdap.logger().info( "ldap_set_option(PROTOCOL_VERSION) failed: \(String(cString: ldap_err2string(ldapReturnCode)))" ) return ([], 0) @@ -187,7 +187,7 @@ final public class OpenLdap: OpenLdapProtocol, Loggable { } else { distinguishedName.remove(at: distinguishedName.startIndex) } - OpenLdap.logger().debug("Searching from LDAP. Url: \(url) \(distinguishedName) \(searchType.filter)") + OpenLdap.logger().info("Searching from LDAP. Url: \(url) \(distinguishedName) \(searchType.filter)") var msgId: Int32 = 0 var attr = Array("userCertificate;binary".utf8CString) ldapReturnCode = attr.withUnsafeMutableBufferPointer { attr in @@ -210,7 +210,7 @@ final public class OpenLdap: OpenLdapProtocol, Loggable { } guard ldapReturnCode == LDAP_SUCCESS else { - OpenLdap.logger().debug("ldap_search_ext failed: \(String(cString: ldap_err2string(ldapReturnCode)))") + OpenLdap.logger().info("ldap_search_ext failed: \(String(cString: ldap_err2string(ldapReturnCode)))") return ([], 0) } @@ -232,7 +232,7 @@ final public class OpenLdap: OpenLdapProtocol, Loggable { case Int32(LDAP_SUCCESS): break default: - OpenLdap.logger().debug("ldap_result failed: \(String(cString: ldap_err2string(ldapReturnCode)))") + OpenLdap.logger().info("ldap_result failed: \(String(cString: ldap_err2string(ldapReturnCode)))") return (addressees: result, totalAddressees: totalAddressees) } } @@ -279,7 +279,7 @@ final public class OpenLdap: OpenLdapProtocol, Loggable { } if let namePointer = ldap_get_dn(ldap, currentMessage) { - OpenLdap.logger().debug("Result (\(result.count)) \(String(cString: namePointer))") + OpenLdap.logger().info("Result (\(result.count)) \(String(cString: namePointer))") ldap_memfree(namePointer) } } diff --git a/Modules/IdCardLib/Sources/IdCardLib/CardActions/CardReaderNFC.swift b/Modules/IdCardLib/Sources/IdCardLib/CardActions/CardReaderNFC.swift index 0404f3a9..e7d7f4e5 100644 --- a/Modules/IdCardLib/Sources/IdCardLib/CardActions/CardReaderNFC.swift +++ b/Modules/IdCardLib/Sources/IdCardLib/CardActions/CardReaderNFC.swift @@ -72,9 +72,9 @@ class CardReaderNFC: @unchecked CardReader, Loggable { init(_ tag: NFCISO7816Tag, CAN: String) async throws { self.tag = SendableISO7816Tag(tag: tag) - CardReaderNFC.logger().debug("Select CardAccess") + CardReaderNFC.logger().info("Select CardAccess") _ = try await self.tag.sendCommand(cls: 0x00, ins: 0xA4, p1Byte: 0x02, p2Byte: 0x0C, data: Data([0x01, 0x1C])) - CardReaderNFC.logger().debug("Read CardAccess") + CardReaderNFC.logger().info("Read CardAccess") let data = try await self.tag.sendCommand(cls: 0x00, ins: 0xB0, p1Byte: 0x00, p2Byte: 0x00, leByte: 256) guard let (mappingType, parameterId) = TLV.sequenceOfRecords(from: data)? @@ -102,9 +102,9 @@ class CardReaderNFC: @unchecked CardReader, Loggable { // Step1 - General Authentication let nonceEnc = try await self.tag.sendPaceCommand(records: [], tagExpected: 0x80) - CardReaderNFC.logger().debug("Challenge \(nonceEnc.value.toHex)") + CardReaderNFC.logger().info("Challenge \(nonceEnc.value.toHex)") let nonce = try CardReaderNFC.decryptNonce(CAN: CAN, encryptedNonce: nonceEnc.value) - CardReaderNFC.logger().debug("Nonce \(nonce.toHex)") + CardReaderNFC.logger().info("Nonce \(nonce.toHex)") // Step2 let (terminalPubKey, terminalPrivKey) = domain.makeKeyPair() @@ -115,7 +115,7 @@ class CardReaderNFC: @unchecked CardReader, Loggable { )], tagExpected: 0x82 ) - CardReaderNFC.logger().debug("Mapping key \(mappingKey.value.toHex)") + CardReaderNFC.logger().info("Mapping key \(mappingKey.value.toHex)") guard let cardPubKey = try ECPublicKey(domain: domain, point: mappingKey.value) else { throw IdCardInternalError.authenticationFailed } @@ -123,17 +123,17 @@ class CardReaderNFC: @unchecked CardReader, Loggable { let nonceS = BInt(magnitude: nonce) let mappingBasePoint = ECPublicKey(privateKey: try ECPrivateKey(domain: domain, s: nonceS)) // S*G // swiftlint:disable line_length - CardReaderNFC.logger().debug("Card Key x: \(mappingBasePoint.w.x.asMagnitudeBytes().toHex, privacy: .public), y: \(mappingBasePoint.w.y.asMagnitudeBytes().toHex, privacy: .public)") + CardReaderNFC.logger().info("Card Key x: \(mappingBasePoint.w.x.asMagnitudeBytes().toHex, privacy: .public), y: \(mappingBasePoint.w.y.asMagnitudeBytes().toHex, privacy: .public)") // swiftlint:enable line_length let sharedSecretH = try domain.multiplyPoint(cardPubKey.w, terminalPrivKey.s) // swiftlint:disable line_length - CardReaderNFC.logger().debug("Shared Secret x: \(sharedSecretH.x.asMagnitudeBytes().toHex, privacy: .public), y: \(sharedSecretH.y.asMagnitudeBytes().toHex, privacy: .public)") + CardReaderNFC.logger().info("Shared Secret x: \(sharedSecretH.x.asMagnitudeBytes().toHex, privacy: .public), y: \(sharedSecretH.y.asMagnitudeBytes().toHex, privacy: .public)") // swiftlint:enable line_length let mappedPoint = try domain.addPoints(mappingBasePoint.w, sharedSecretH) // MAP G = (S*G) + H // Ephemeral data // swiftlint:disable line_length - CardReaderNFC.logger().debug("Mapped point x: \(mappedPoint.x.asMagnitudeBytes().toHex, privacy: .public), y: \(mappedPoint.y.asMagnitudeBytes().toHex, privacy: .public)") + CardReaderNFC.logger().info("Mapped point x: \(mappedPoint.x.asMagnitudeBytes().toHex, privacy: .public), y: \(mappedPoint.y.asMagnitudeBytes().toHex, privacy: .public)") // swiftlint:enable line_length let mappedDomain = try Domain.instance( name: domain.name + " Mapped", @@ -153,17 +153,17 @@ class CardReaderNFC: @unchecked CardReader, Loggable { )], tagExpected: 0x84 ) - CardReaderNFC.logger().debug("Card Ephermal key \(ephemeralKey.value.toHex)") + CardReaderNFC.logger().info("Card Ephermal key \(ephemeralKey.value.toHex)") guard let ephemeralCardPubKey = try ECPublicKey(domain: mappedDomain, point: ephemeralKey.value) else { throw IdCardInternalError.authenticationFailed } // Derive shared secret and session keys let sharedSecret = try terminalEphemeralPrivKey.sharedSecret(pubKey: ephemeralCardPubKey) - CardReaderNFC.logger().debug("Shared secret \(sharedSecret.toHex)") + CardReaderNFC.logger().info("Shared secret \(sharedSecret.toHex)") ksEnc = CardReaderNFC.KDF(key: sharedSecret, counter: 1) ksMac = CardReaderNFC.KDF(key: sharedSecret, counter: 2) - CardReaderNFC.logger().debug("KS.Enc \(self.ksEnc.toHex)") - CardReaderNFC.logger().debug("KS.Mac \(self.ksMac.toHex)") + CardReaderNFC.logger().info("KS.Enc \(self.ksEnc.toHex)") + CardReaderNFC.logger().info("KS.Mac \(self.ksMac.toHex)") // Mutual authentication let macCalc = try AES.CMAC(key: ksMac) @@ -183,7 +183,7 @@ class CardReaderNFC: @unchecked CardReader, Loggable { )], tagExpected: 0x86 ) - CardReaderNFC.logger().debug("Mac response \(macValue.data.toHex)") + CardReaderNFC.logger().info("Mac response \(macValue.data.toHex)") // verify chip's MAC let macResult = TLV(tag: 0x7f49, records: [ @@ -229,14 +229,14 @@ class CardReaderNFC: @unchecked CardReader, Loggable { case 0x87: tlvEnc = tlv case 0x99: tlvRes = tlv case 0x8E: tlvMac = tlv - default: CardReaderNFC.logger().debug("Unknown tag") + default: CardReaderNFC.logger().info("Unknown tag") } } return (tlvEnc, tlvRes, tlvMac) } func transmit(_ apduData: Bytes) async throws -> (responseData: Bytes, sw: UInt16) { - CardReaderNFC.logger().debug("Plain >: \(apduData.toHex)") + CardReaderNFC.logger().info("Plain >: \(apduData.toHex)") guard let apdu = NFCISO7816APDU(data: Data(apduData)) else { throw IdCardInternalError.invalidAPDU } @@ -269,12 +269,12 @@ class CardReaderNFC: @unchecked CardReader, Loggable { throw IdCardInternalError.invalidMACValue } guard let tlvEnc else { - CardReaderNFC.logger().debug("Plain <: \(tlvRes.value.toHex)") + CardReaderNFC.logger().info("Plain <: \(tlvRes.value.toHex)") return (.init(), UInt16(tlvRes.value[0], tlvRes.value[1])) } let ivValue = try AES.CBC(key: ksEnc).encrypt(SSC) let responseData = try (try AES.CBC(key: ksEnc, ivVal: ivValue).decrypt(tlvEnc.value[1...])).removePadding() - CardReaderNFC.logger().debug("Plain <: \(responseData.toHex) \(tlvRes.value.toHex)") + CardReaderNFC.logger().info("Plain <: \(responseData.toHex) \(tlvRes.value.toHex)") return (Bytes(responseData), UInt16(tlvRes.value[0], tlvRes.value[1])) } diff --git a/Modules/IdCardLib/Sources/IdCardLib/CardActions/CardReaderiR301.swift b/Modules/IdCardLib/Sources/IdCardLib/CardActions/CardReaderiR301.swift index 4dfeae35..cef74eb5 100644 --- a/Modules/IdCardLib/Sources/IdCardLib/CardActions/CardReaderiR301.swift +++ b/Modules/IdCardLib/Sources/IdCardLib/CardActions/CardReaderiR301.swift @@ -47,7 +47,7 @@ class CardReaderiR301: CardReader, @unchecked Sendable, Loggable { return Int(modelNameLength) } - CardReaderiR301.logger().debug("ID-CARD: Checking if card reader is supported: \(modelName)") + CardReaderiR301.logger().info("ID-CARD: Checking if card reader is supported: \(modelName)") guard modelName.hasPrefix("iR301") else { CardReaderiR301.logger().error("ID-CARD: Unsupported reader: \(modelName)") return nil @@ -94,7 +94,7 @@ class CardReaderiR301: CardReader, @unchecked Sendable, Loggable { } initializedCount = Int(atrSize) } - CardReaderiR301.logger().debug("SCardStatus status: \(dwStatus) ATR: \(self.atr.hex))") + CardReaderiR301.logger().info("SCardStatus status: \(dwStatus) ATR: \(self.atr.hex))") guard dwStatus == SCARD_PRESENT else { CardReaderiR301.logger().error("ID-CARD: Did not successfully power on card") @@ -103,7 +103,7 @@ class CardReaderiR301: CardReader, @unchecked Sendable, Loggable { } func transmit(_ apdu: Bytes) async throws -> (responseData: Bytes, sw: UInt16) { - CardReaderiR301.logger().debug("ID-CARD Transmitting: \(apdu.hex)") + CardReaderiR301.logger().info("ID-CARD Transmitting: \(apdu.hex)") var responseSize: DWORD = 512 var response = try Bytes(unsafeUninitializedCapacity: Int(responseSize)) { buffer, initializedCount in guard SCardTransmit( @@ -126,7 +126,7 @@ class CardReaderiR301: CardReader, @unchecked Sendable, Loggable { "ID-CARD: Response size must be at least 2. Response size: \(response.count)") throw IdCardInternalError.readerProcessFailed } - CardReaderiR301.logger().debug("ID-CARD Response: \(response.hex)") + CardReaderiR301.logger().info("ID-CARD Response: \(response.hex)") let swVal = UInt16(response[response.count - 2], response[response.count - 1]) response.removeLast(2) return (response, swVal) diff --git a/Modules/IdCardLib/Sources/IdCardLib/Operations/UsbReaderConnection.swift b/Modules/IdCardLib/Sources/IdCardLib/Operations/UsbReaderConnection.swift index 9ead3663..100583f3 100644 --- a/Modules/IdCardLib/Sources/IdCardLib/Operations/UsbReaderConnection.swift +++ b/Modules/IdCardLib/Sources/IdCardLib/Operations/UsbReaderConnection.swift @@ -39,7 +39,7 @@ public actor UsbReaderConnection: UsbReaderConnectionProtocol, Loggable { return } - UsbReaderConnection.logger().debug("ID-CARD: Starting reader discovery") + UsbReaderConnection.logger().info("ID-CARD: Starting reader discovery") await updateStatus(status) let result = SCardEstablishContext(DWORD(SCARD_SCOPE_SYSTEM), nil, nil, &handle) @@ -50,18 +50,18 @@ public actor UsbReaderConnection: UsbReaderConnectionProtocol, Loggable { return } - UsbReaderConnection.logger().debug("ID-CARD: Started reader discovery: \(self.handle)") + UsbReaderConnection.logger().info("ID-CARD: Started reader discovery: \(self.handle)") } public func stopDiscoveringReaders(with status: UsbReaderStatus = .sInitial) async { await ensureHandler() - UsbReaderConnection.logger().debug("ID-CARD: Stopping reader discovery") + UsbReaderConnection.logger().info("ID-CARD: Stopping reader discovery") self.status = status FtDidEnterBackground(1) SCardCancel(handle) SCardReleaseContext(handle) - UsbReaderConnection.logger().debug("ID-CARD: Stopped reader discovery with status: \(self.handle)") + UsbReaderConnection.logger().info("ID-CARD: Stopped reader discovery with status: \(self.handle)") handle = 0 } @@ -96,7 +96,7 @@ public actor UsbReaderConnection: UsbReaderConnectionProtocol, Loggable { // MARK: ID-Card Actions public func getPublicData() async throws -> CardInfo { - UsbReaderConnection.logger().debug("ID-CARD: Getting ID-card public data") + UsbReaderConnection.logger().info("ID-CARD: Getting ID-card public data") guard let handler = cardHandler else { UsbReaderConnection.logger().error("ID-CARD: Unable to get card handler to get public data") @@ -109,7 +109,7 @@ public actor UsbReaderConnection: UsbReaderConnectionProtocol, Loggable { public func readAuthenticationCertificate() async throws -> Data { await ensureHandler() - UsbReaderConnection.logger().debug("ID-CARD: Reading authentication certificate with reader") + UsbReaderConnection.logger().info("ID-CARD: Reading authentication certificate with reader") guard let handler = cardHandler else { UsbReaderConnection @@ -131,7 +131,7 @@ public actor UsbReaderConnection: UsbReaderConnectionProtocol, Loggable { public func readSignatureCertificate() async throws -> Data { await ensureHandler() - UsbReaderConnection.logger().debug("ID-CARD: Reading signature certificate with reader") + UsbReaderConnection.logger().info("ID-CARD: Reading signature certificate with reader") guard let handler = cardHandler else { UsbReaderConnection @@ -153,7 +153,7 @@ public actor UsbReaderConnection: UsbReaderConnectionProtocol, Loggable { public func readCodeTryCounterRecord(for codeType: CodeType) async throws -> UInt8 { await ensureHandler() - UsbReaderConnection.logger().debug("ID-CARD: Reading try counter with reader for \(codeType.name)") + UsbReaderConnection.logger().info("ID-CARD: Reading try counter with reader for \(codeType.name)") guard let handler = cardHandler else { UsbReaderConnection @@ -175,7 +175,7 @@ public actor UsbReaderConnection: UsbReaderConnectionProtocol, Loggable { public func isPUKChangeable() async throws -> Bool { await ensureHandler() - UsbReaderConnection.logger().debug("ID-CARD: Checking if PUK is changeable with reader") + UsbReaderConnection.logger().info("ID-CARD: Checking if PUK is changeable with reader") guard let handler = cardHandler else { UsbReaderConnection.logger().error("ID-CARD: Unable to check if PUK is changeable with reader") @@ -188,7 +188,7 @@ public actor UsbReaderConnection: UsbReaderConnectionProtocol, Loggable { public func changeCode(_ codeType: CodeType, to newCode: Data, verifyCode: Data) async throws { await ensureHandler() - UsbReaderConnection.logger().debug("ID-CARD: Changing code for \(codeType.name)") + UsbReaderConnection.logger().info("ID-CARD: Changing code for \(codeType.name)") guard let handler = cardHandler else { UsbReaderConnection.logger().error("ID-CARD: Unable to get card handler to change \(codeType.name)") @@ -209,7 +209,7 @@ public actor UsbReaderConnection: UsbReaderConnectionProtocol, Loggable { public func unblockCode(_ codeType: CodeType, puk: Data, newCode: Data) async throws { await ensureHandler() - UsbReaderConnection.logger().debug("ID-CARD: Unblocking code for \(codeType.name)") + UsbReaderConnection.logger().info("ID-CARD: Unblocking code for \(codeType.name)") guard let handler = cardHandler else { UsbReaderConnection.logger().error("ID-CARD: Unable to get card handler to unblock \(codeType.name)") @@ -255,14 +255,14 @@ private final class UsbReaderInterfaceHandler: NSObject, ReaderInterfaceDelegate } func readerInterfaceDidChange(_ attached: Bool, bluetoothID _: String?) { - UsbReaderInterfaceHandler.logger().debug("ID-CARD: Reader attached: \(attached)") + UsbReaderInterfaceHandler.logger().info("ID-CARD: Reader attached: \(attached)") Task { await usbReaderConnection.updateStatus(attached ? .sReaderConnected : .sReaderNotConnected) } } func cardInterfaceDidDetach(_ attached: Bool) { - UsbReaderInterfaceHandler.logger().debug("ID-CARD: Card (interface) attached: \(attached)") + UsbReaderInterfaceHandler.logger().info("ID-CARD: Card (interface) attached: \(attached)") Task { do { let contextHandle = await usbReaderConnection.getHandle() @@ -289,7 +289,7 @@ private final class UsbReaderInterfaceHandler: NSObject, ReaderInterfaceDelegate if let handler { await usbReaderConnection.setCardHandler(handler) - UsbReaderInterfaceHandler.logger().debug("ID-CARD: Card connected") + UsbReaderInterfaceHandler.logger().info("ID-CARD: Card connected") await usbReaderConnection.updateStatus(.sCardConnected) } @@ -303,6 +303,6 @@ private final class UsbReaderInterfaceHandler: NSObject, ReaderInterfaceDelegate func didGetBattery(_: Int) {} func findPeripheralReader(_ readerName: String) { - UsbReaderInterfaceHandler.logger().debug("ID-CARD: Reader name: \(readerName)") + UsbReaderInterfaceHandler.logger().info("ID-CARD: Reader name: \(readerName)") } } diff --git a/Modules/LibdigidocLib/Sources/LibdigidocObjC/include/Conf/DigiDocConfWrapper.mm b/Modules/LibdigidocLib/Sources/LibdigidocObjC/include/Conf/DigiDocConfWrapper.mm index 14ee7068..d52218c2 100644 --- a/Modules/LibdigidocLib/Sources/LibdigidocObjC/include/Conf/DigiDocConfWrapper.mm +++ b/Modules/LibdigidocLib/Sources/LibdigidocObjC/include/Conf/DigiDocConfWrapper.mm @@ -183,11 +183,7 @@ int logLevel() const final { } std::string logFile() const final { - return logFileLocation(currentConf.logFile); - } - - std::string logFileLocation(NSString *logsFolderPath) const { - return [logsFolderPath stringByAppendingPathComponent:@"libdigidocpp.log"].UTF8String; + return currentConf.logFile.UTF8String; } std::vector toX509Certs(NSArray *certBundle, NSURL *cert = nil) const { diff --git a/Modules/LibdigidocLib/Sources/LibdigidocSwift/Domain/Container/ContainerWrapper.swift b/Modules/LibdigidocLib/Sources/LibdigidocSwift/Domain/Container/ContainerWrapper.swift index bd9aa231..32ff8af4 100644 --- a/Modules/LibdigidocLib/Sources/LibdigidocSwift/Domain/Container/ContainerWrapper.swift +++ b/Modules/LibdigidocLib/Sources/LibdigidocSwift/Domain/Container/ContainerWrapper.swift @@ -84,7 +84,7 @@ public actor ContainerWrapper: ContainerWrapperProtocol, Loggable { saveDataFile: dataFile.fileId, to: tempSavedFileLocation.resolvedPath ) - ContainerWrapper.logger().debug("Successfully saved \(sanitizedFilename) to 'Saved Files' directory") + ContainerWrapper.logger().info("Successfully saved \(sanitizedFilename) to 'Saved Files' directory") return tempSavedFileLocation } catch { let nsError = (error as NSError?) ?? NSError(domain: "ContainerWrapper - cannot save data file", code: 2) @@ -108,7 +108,7 @@ public actor ContainerWrapper: ContainerWrapperProtocol, Loggable { @MainActor public func open(containerFile: URL, isSivaConfirmed: Bool) async throws -> ContainerWrapper { - ContainerWrapper.logger().debug("Opening container file '\(containerFile.lastPathComponent)'") + ContainerWrapper.logger().info("Opening container file '\(containerFile.lastPathComponent)'") do { let container = try DigiDocContainerWrapper.open( diff --git a/Modules/LibdigidocLib/Sources/LibdigidocSwift/SignedContainer.swift b/Modules/LibdigidocLib/Sources/LibdigidocSwift/SignedContainer.swift index dd367226..e3fc35ef 100644 --- a/Modules/LibdigidocLib/Sources/LibdigidocSwift/SignedContainer.swift +++ b/Modules/LibdigidocLib/Sources/LibdigidocSwift/SignedContainer.swift @@ -309,7 +309,7 @@ extension SignedContainer { containerUtil: ContainerUtilProtocol = Container.shared.containerUtil(), isSivaConfirmed: Bool ) async throws -> SignedContainerProtocol { - logger().debug("Opening or creating container. Found \(dataFiles.count) datafile(s)") + logger().info("Opening or creating container. Found \(dataFiles.count) datafile(s)") guard let firstFile = dataFiles.first else { logger().error("Unable to create or open container. First datafile is nil") throw DigiDocError.containerCreationFailed( @@ -349,11 +349,11 @@ extension SignedContainer { } if dataFiles.count == 1 && isFirstDataFileContainer { - SignedContainer.logger().debug("Opening existing container") + SignedContainer.logger().info("Opening existing container") return try await open(file: containerFile, isSivaConfirmed: isSivaConfirmed) } - SignedContainer.logger().debug("Creating a new container") + SignedContainer.logger().info("Creating a new container") return try await create(containerFile: containerFile, dataFiles: dataFiles) } @@ -433,20 +433,20 @@ extension SignedContainer { containerUtil: Container.shared.containerUtil() ) - SignedContainer.logger().debug("Container created. Removing \(dataFiles.count) saved data files") + SignedContainer.logger().info("Container created. Removing \(dataFiles.count) saved data files") for (index, dataFile) in dataFiles.enumerated() { let containerName = await signedContainer.getContainerName() if await dataFile.isContainer() && containerName == dataFile.lastPathComponent { continue } - SignedContainer.logger().debug( + SignedContainer.logger().info( "Removing data file (\(index + 1) / \(dataFiles.count)): \(dataFile.lastPathComponent)" ) if fileManager.fileExists(atPath: dataFile.resolvedPath) && fileManager.isDeletableFile(atPath: dataFile.resolvedPath) { try fileManager.removeItem(at: dataFile) - SignedContainer.logger().debug("Data file: '\(dataFile.lastPathComponent)' removed") + SignedContainer.logger().info("Data file: '\(dataFile.lastPathComponent)' removed") } } diff --git a/Modules/UtilsLib/Sources/UtilsLib/Container/ContainerUtil.swift b/Modules/UtilsLib/Sources/UtilsLib/Container/ContainerUtil.swift index 1abfc849..6453e787 100644 --- a/Modules/UtilsLib/Sources/UtilsLib/Container/ContainerUtil.swift +++ b/Modules/UtilsLib/Sources/UtilsLib/Container/ContainerUtil.swift @@ -62,7 +62,7 @@ public struct ContainerUtil: ContainerUtilProtocol, Loggable { do { try fileManager .createDirectory(at: signedContainersDirectory, withIntermediateDirectories: true, attributes: [:]) - ContainerUtil.logger().debug( + ContainerUtil.logger().info( "Directories created or already exist for \(signedContainersDirectory.resolvedPath)" ) } catch { @@ -118,7 +118,7 @@ public struct ContainerUtil: ContainerUtilProtocol, Loggable { attributes: [:] ) if let base = directory { - ContainerUtil.logger().debug("Directories created or already exist for \(base.resolvedPath)") + ContainerUtil.logger().info("Directories created or already exist for \(base.resolvedPath)") } } catch { ContainerUtil.logger().error("Failed to create directory: \(error.localizedDescription)") diff --git a/Modules/UtilsLib/Sources/UtilsLib/File/Directories.swift b/Modules/UtilsLib/Sources/UtilsLib/File/Directories.swift index 4e1f6280..b869b1d8 100644 --- a/Modules/UtilsLib/Sources/UtilsLib/File/Directories.swift +++ b/Modules/UtilsLib/Sources/UtilsLib/File/Directories.swift @@ -28,19 +28,9 @@ public struct Directories { ) throws -> URL { var tempDirectory = fileManager.temporaryDirectory .appending(path: BundleUtil.getBundleIdentifier(), directoryHint: .isDirectory) - if !subfolder.isEmpty { - tempDirectory = tempDirectory.appending(path: subfolder, directoryHint: .isDirectory) - } - - if !fileManager.fileExists(atPath: tempDirectory.resolvedPath) { - try fileManager - .createDirectory( - at: tempDirectory, - withIntermediateDirectories: true, - attributes: nil - ) - } + tempDirectory = appendSubfolder(baseFolder: tempDirectory, subfolder: subfolder) + try createDirectoryIfNeeded(at: tempDirectory, fileManager: fileManager) return tempDirectory } @@ -56,16 +46,7 @@ public struct Directories { } let sharedContainerSubfolder = sharedContainerURL.appending(path: subfolder) - - if !fileManager.fileExists(atPath: sharedContainerSubfolder.resolvedPath) { - try fileManager - .createDirectory( - at: sharedContainerSubfolder, - withIntermediateDirectories: true, - attributes: nil - ) - } - + try createDirectoryIfNeeded(at: sharedContainerSubfolder, fileManager: fileManager) return sharedContainerSubfolder } @@ -81,53 +62,28 @@ public struct Directories { .appending(path: BundleUtil.getBundleIdentifier(), directoryHint: .isDirectory) for subfolder in subfolders { - cacheDirectory = cacheDirectory.appending(path: subfolder, directoryHint: .isDirectory) - } - - if !fileManager.fileExists(atPath: cacheDirectory.resolvedPath) { - try fileManager - .createDirectory( - at: cacheDirectory, - withIntermediateDirectories: true, - attributes: nil - ) + cacheDirectory = appendSubfolder(baseFolder: cacheDirectory, subfolder: subfolder) } + try createDirectoryIfNeeded(at: cacheDirectory, fileManager: fileManager) return cacheDirectory } - public static func getLibraryDirectory( - fileManager: FileManagerProtocol - ) -> URL? { - if let libraryDirectory = fileManager.urls(for: .libraryDirectory, in: .userDomainMask).first { - return libraryDirectory - } - return nil + public static func getLibraryDirectory(fileManager: FileManagerProtocol) -> URL? { + return fileManager.urls(for: .libraryDirectory, in: .userDomainMask).first } - public static func getDocumentsDirectory( - fileManager: FileManagerProtocol - ) -> URL? { - if let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first { - return documentsDirectory - } - return nil + public static func getDocumentsDirectory(fileManager: FileManagerProtocol) -> URL? { + return fileManager.urls(for: .documentDirectory, in: .userDomainMask).first } - public static func getApplicationDirectory( - fileManager: FileManagerProtocol - ) -> URL? { - if let applicationDirectory = fileManager.urls(for: .applicationDirectory, in: .userDomainMask).first { - return applicationDirectory - } - return nil + public static func getApplicationDirectory(fileManager: FileManagerProtocol) -> URL? { + return fileManager.urls(for: .applicationDirectory, in: .userDomainMask).first } public static func getConfigDirectory(from directory: URL? = nil, fileManager: FileManagerProtocol) throws -> URL { let baseDirectory = try directory ?? getCacheDirectory(fileManager: fileManager) - return baseDirectory.appending(path: - Constants.Configuration.CacheConfigFolder, directoryHint: .isDirectory - ) + return appendSubfolder(baseFolder: baseDirectory, subfolder: Constants.Configuration.CacheConfigFolder) } public static func getTslCacheDirectory(fileManager: FileManagerProtocol) -> URL? { @@ -138,34 +94,29 @@ public struct Directories { from directory: URL?, fileManager: FileManagerProtocol ) throws -> URL? { - let libdigidocppLogFile = "libdigidocpp.log" - - if let mainDirectory = directory { - let primaryLogsDirectory = mainDirectory.appending(path: "logs") - - if !fileManager.fileExists(atPath: primaryLogsDirectory.resolvedPath) { - try fileManager.createDirectory( - at: primaryLogsDirectory, - withIntermediateDirectories: true, - attributes: nil - ) - return primaryLogsDirectory.appending(path: libdigidocppLogFile) - } else { - return primaryLogsDirectory.appending(path: libdigidocppLogFile) - } - } + let baseDirectory = try directory ?? getCacheDirectory(fileManager: fileManager) + let logsDirectory = baseDirectory.appending(path: "logs") + try createDirectoryIfNeeded(at: logsDirectory, fileManager: fileManager) + return logsDirectory.appending(path: Constants.File.LibDigidocLog) + } - let cacheDirectory = try getCacheDirectory(fileManager: fileManager) - let fallbackLogsDirectory = cacheDirectory.appending(path: "logs") + private static func appendSubfolder(baseFolder: URL, subfolder: String = "") -> URL { + if !subfolder.isEmpty { + return baseFolder.appending(path: subfolder, directoryHint: .isDirectory) + } + return baseFolder + } - if !fileManager.fileExists(atPath: fallbackLogsDirectory.resolvedPath) { + private static func createDirectoryIfNeeded( + at url: URL, + fileManager: FileManagerProtocol + ) throws { + if !fileManager.fileExists(atPath: url.resolvedPath) { try fileManager.createDirectory( - at: fallbackLogsDirectory, + at: url, withIntermediateDirectories: true, attributes: nil ) } - - return fallbackLogsDirectory.appending(path: libdigidocppLogFile) } } diff --git a/Modules/UtilsLib/Sources/UtilsLib/File/FileUtil.swift b/Modules/UtilsLib/Sources/UtilsLib/File/FileUtil.swift index e2091765..7ad32684 100644 --- a/Modules/UtilsLib/Sources/UtilsLib/File/FileUtil.swift +++ b/Modules/UtilsLib/Sources/UtilsLib/File/FileUtil.swift @@ -64,7 +64,7 @@ public struct FileUtil: FileUtilProtocol, Loggable { // Check file path so its valid and is not modified by someone else public func getValidPath(url: URL) async -> URL? { - FileUtil.logger().debug("Getting valid path for file: \(url)") + FileUtil.logger().info("Getting valid path for file: \(url)") let resolvedURL = url.resolvingSymlinksInPath() let filePath = FilePath(resolvedURL.resolvedPath).lexicallyNormalized() @@ -96,7 +96,7 @@ public struct FileUtil: FileUtilProtocol, Loggable { .lexicallyNormalized() if filePath.starts(with: appContainerPath) { - FileUtil.logger().debug("Resolved valid file path: \(resolvedURL)") + FileUtil.logger().info("Resolved valid file path: \(resolvedURL)") return resolvedURL } } @@ -104,39 +104,39 @@ public struct FileUtil: FileUtilProtocol, Loggable { // Check if file is opened externally (outside of application) let fileFromAppGroup = getFileUrlFromAppGroup(resolvedURL, appGroupIdentifier: Constants.Identifier.Group) if let fileUrl = fileFromAppGroup { - FileUtil.logger().debug("File is from app group: \(fileUrl)") + FileUtil.logger().info("File is from app group: \(fileUrl)") return fileUrl } if isFileInsideMailFolder(resolvedURL) { - FileUtil.logger().debug("File is from Mail app") + FileUtil.logger().info("File is from Mail app") return resolvedURL } else { - FileUtil.logger().debug("Checking if file is from iCloud") + FileUtil.logger().info("Checking if file is from iCloud") // Check if file is opened from iCloud if isFileFromiCloud(fileURL: resolvedURL) { if !isFileDownloadedFromiCloud(fileURL: resolvedURL) { - FileUtil.logger().debug( + FileUtil.logger().info( "File '\(resolvedURL.lastPathComponent)' from iCloud is not downloaded. Downloading..." ) let downloadedFileUrl = await downloadFileFromiCloud(fileURL: resolvedURL) if let fileUrl = downloadedFileUrl { - FileUtil.logger().debug("File '\(resolvedURL.lastPathComponent)' downloaded from iCloud") + FileUtil.logger().info("File '\(resolvedURL.lastPathComponent)' downloaded from iCloud") return fileUrl } else { - FileUtil.logger().debug( + FileUtil.logger().info( "Unable to download file '\(resolvedURL.lastPathComponent)' from iCloud") return nil } } else { - FileUtil.logger().debug("File '\(resolvedURL.lastPathComponent)' from iCloud is already downloaded") + FileUtil.logger().info("File '\(resolvedURL.lastPathComponent)' from iCloud is already downloaded") return url } } } - FileUtil.logger().debug("File is NOT from iCloud") + FileUtil.logger().info("File is NOT from iCloud") return nil } @@ -162,7 +162,7 @@ public struct FileUtil: FileUtilProtocol, Loggable { ) if isFromAppGroup { - FileUtil.logger().debug("File is from app group: \(normalizedURL)") + FileUtil.logger().info("File is from app group: \(normalizedURL)") return normalizedURL } @@ -190,7 +190,7 @@ public struct FileUtil: FileUtilProtocol, Loggable { let values = try fileURL.resourceValues(forKeys: [.ubiquitousItemDownloadingStatusKey]) if let downloadingStatus = values.ubiquitousItemDownloadingStatus, downloadingStatus == .current { - FileUtil.logger().debug("File downloaded from iCloud") + FileUtil.logger().info("File downloaded from iCloud") return true } } catch { @@ -208,13 +208,13 @@ public struct FileUtil: FileUtilProtocol, Loggable { public func downloadFileFromiCloud(fileURL: URL) async -> URL? { do { try fileManager.startDownloadingUbiquitousItem(at: fileURL) - FileUtil.logger().debug("Downloading file '\(fileURL.lastPathComponent)' from iCloud") + FileUtil.logger().info("Downloading file '\(fileURL.lastPathComponent)' from iCloud") while !isFileDownloadedFromiCloud(fileURL: fileURL) { try await Task.sleep(for: .seconds(0.5)) } - FileUtil.logger().debug("iCloud file '\(fileURL.lastPathComponent)' downloaded") + FileUtil.logger().info("iCloud file '\(fileURL.lastPathComponent)' downloaded") return fileURL } catch { FileUtil.logger().error( @@ -229,7 +229,7 @@ public struct FileUtil: FileUtilProtocol, Loggable { let filePath = FilePath(stringLiteral: url.resolvedPath).lexicallyNormalized() if filePath == mailFolderPath { - FileUtil.logger().debug("File '\(url.lastPathComponent)' is from Mail app") + FileUtil.logger().info("File '\(url.lastPathComponent)' is from Mail app") return true } @@ -238,18 +238,18 @@ public struct FileUtil: FileUtilProtocol, Loggable { let filePathString = filePath.string if filePathString.count == mailPathString.count { - FileUtil.logger().debug("File '\(url.lastPathComponent)' is from Mail app") + FileUtil.logger().info("File '\(url.lastPathComponent)' is from Mail app") return true } let index = filePathString.index(filePathString.startIndex, offsetBy: mailPathString.count) if filePathString[index] == "/" { - FileUtil.logger().debug("File '\(url.lastPathComponent)' is from Mail app") + FileUtil.logger().info("File '\(url.lastPathComponent)' is from Mail app") return true } } - FileUtil.logger().debug("File '\(url.lastPathComponent)' is NOT from Mail app") + FileUtil.logger().info("File '\(url.lastPathComponent)' is NOT from Mail app") return false } @@ -268,7 +268,7 @@ public struct FileUtil: FileUtilProtocol, Loggable { } public func removeSharedFiles(url: URL?) throws { - FileUtil.logger().debug("Removing shared files") + FileUtil.logger().info("Removing shared files") let sharedFilesFolder = try url ?? Directories.getSharedFolder(fileManager: fileManager) @@ -278,20 +278,55 @@ public struct FileUtil: FileUtilProtocol, Loggable { try fileManager.removeItem(at: fileURL) } - FileUtil.logger().debug("Shared files removed") + FileUtil.logger().info("Shared files removed") } public func removeSavedFilesDirectory(savedFilesDirectory: URL? = nil) { - FileUtil.logger().debug("Removing saved files directory") do { let directory = try savedFilesDirectory ?? Directories.getCacheDirectory( subfolders: [CommonsLib.Constants.Folder.SavedFiles], fileManager: fileManager ) - try fileManager.removeItem(at: directory) - FileUtil.logger().debug("Saved Files directory removed") + removeDirectory(at: directory) } catch { - FileUtil.logger().error("Unable to delete saved files directory: \(error.localizedDescription)") + FileUtil.logger().error("Unable to locate Saved Files directory: \(error.localizedDescription)") + } + } + + public func removeCacheLogsDirectory() { + do { + let directory = try Directories.getCacheDirectory( + subfolders: [CommonsLib.Constants.Folder.Logs], + fileManager: fileManager + ) + removeDirectory(at: directory) + } catch { + FileUtil.logger().error("Unable to locate cache/logs directory: \(error.localizedDescription)") + } + } + + public func removeLibraryLogsDirectory(directory: URL? = nil) { + guard let libraryDirectory = directory ?? Directories.getLibraryDirectory( + fileManager: fileManager + ) else { + FileUtil.logger().error("Unable to locate library directory") + return + } + let directory = libraryDirectory.appendingPathComponent( + "logs", + isDirectory: true + ) + removeDirectory(at: directory) + } + + private func removeDirectory(at url: URL) { + let filePath = url.path(percentEncoded: false) + FileUtil.logger().info("Removing \(filePath)") + do { + try fileManager.removeItem(at: url) + FileUtil.logger().info("\(filePath) removed") + } catch { + FileUtil.logger().error("Unable to remove \(filePath): \(error.localizedDescription)") } } } diff --git a/Modules/UtilsLib/Sources/UtilsLib/File/FileUtilProtocol.swift b/Modules/UtilsLib/Sources/UtilsLib/File/FileUtilProtocol.swift index caa471d1..6f2a45f4 100644 --- a/Modules/UtilsLib/Sources/UtilsLib/File/FileUtilProtocol.swift +++ b/Modules/UtilsLib/Sources/UtilsLib/File/FileUtilProtocol.swift @@ -48,4 +48,8 @@ public protocol FileUtilProtocol: Sendable { func removeSharedFiles(url: URL?) throws func removeSavedFilesDirectory(savedFilesDirectory: URL?) + + func removeCacheLogsDirectory() + + func removeLibraryLogsDirectory(directory: URL?) } diff --git a/Modules/UtilsLib/Sources/UtilsLib/Notification/NotificationUtil.swift b/Modules/UtilsLib/Sources/UtilsLib/Notification/NotificationUtil.swift index 2e525db9..033b0fa3 100644 --- a/Modules/UtilsLib/Sources/UtilsLib/Notification/NotificationUtil.swift +++ b/Modules/UtilsLib/Sources/UtilsLib/Notification/NotificationUtil.swift @@ -29,14 +29,14 @@ public final class NotificationUtil: NSObject, NotificationUtilProtocol, Loggabl } public func requestAuthorization() async -> Bool { - NotificationUtil.logger().debug("Requesting authorization to send notifications") + NotificationUtil.logger().info("Requesting authorization to send notifications") let center = UNUserNotificationCenter.current() do { let granted = try await center.requestAuthorization(options: [.alert, .sound, .badge]) - NotificationUtil.logger().debug("Notification sending authorized: \(granted)") + NotificationUtil.logger().info("Notification sending authorized: \(granted)") return granted } catch { - NotificationUtil.logger().debug("Unable to request authorization to send notifications. \(error)") + NotificationUtil.logger().info("Unable to request authorization to send notifications. \(error)") return false } } @@ -45,7 +45,7 @@ public final class NotificationUtil: NSObject, NotificationUtilProtocol, Loggabl title: String, body: String ) async throws -> String { - NotificationUtil.logger().debug("Sending notification (\(title))") + NotificationUtil.logger().info("Sending notification (\(title))") let notificationId = UUID().uuidString let content = UNMutableNotificationContent() @@ -57,17 +57,17 @@ public final class NotificationUtil: NSObject, NotificationUtilProtocol, Loggabl try await UNUserNotificationCenter.current().add(request) - NotificationUtil.logger().debug("Notification sent. ID: \(notificationId)") + NotificationUtil.logger().info("Notification sent. ID: \(notificationId)") return notificationId } public func removeNotification(id: String) { - NotificationUtil.logger().debug("Removing notification (\(id))") + NotificationUtil.logger().info("Removing notification (\(id))") let center = UNUserNotificationCenter.current() center.removePendingNotificationRequests(withIdentifiers: [id]) center.removeDeliveredNotifications(withIdentifiers: [id]) - NotificationUtil.logger().debug("Removed notification \(id)") + NotificationUtil.logger().info("Removed notification \(id)") } } diff --git a/Modules/UtilsLib/Sources/UtilsLib/System/SystemUtil.swift b/Modules/UtilsLib/Sources/UtilsLib/System/SystemUtil.swift index 26f4f6f7..a162dc08 100644 --- a/Modules/UtilsLib/Sources/UtilsLib/System/SystemUtil.swift +++ b/Modules/UtilsLib/Sources/UtilsLib/System/SystemUtil.swift @@ -22,7 +22,7 @@ import Foundation public struct SystemUtil: Loggable { public static var isSimulator: Bool { #if targetEnvironment(simulator) - logger().debug("App is running on a simulator") + logger().info("App is running on a simulator") return true #else return false @@ -32,7 +32,7 @@ public struct SystemUtil: Loggable { public static func getOSVersion() -> String { let osVersion = ProcessInfo.processInfo.operatingSystemVersion let versionString = "\(osVersion.majorVersion).\(osVersion.minorVersion).\(osVersion.patchVersion)" - logger().debug("Operating system version: \(versionString)") + logger().info("Operating system version: \(versionString)") return versionString } } diff --git a/Modules/UtilsLib/Sources/UtilsLib/Validator/PersonalCodeValidator.swift b/Modules/UtilsLib/Sources/UtilsLib/Validator/PersonalCodeValidator.swift index e513586b..ad883848 100644 --- a/Modules/UtilsLib/Sources/UtilsLib/Validator/PersonalCodeValidator.swift +++ b/Modules/UtilsLib/Sources/UtilsLib/Validator/PersonalCodeValidator.swift @@ -38,7 +38,7 @@ public struct PersonalCodeValidator: Loggable { guard !personalCode.isEmpty, personalCode.count == Constants.Validation.MaximumLatvianPersonalCodeLength else { - PersonalCodeValidator.logger().debug("Personal code is NOT Latvian") + PersonalCodeValidator.logger().info("Personal code is NOT Latvian") return false } diff --git a/Modules/UtilsLib/Sources/UtilsLib/Validator/PhoneNumberValidator.swift b/Modules/UtilsLib/Sources/UtilsLib/Validator/PhoneNumberValidator.swift index 77338a3d..fb3d15f6 100644 --- a/Modules/UtilsLib/Sources/UtilsLib/Validator/PhoneNumberValidator.swift +++ b/Modules/UtilsLib/Sources/UtilsLib/Validator/PhoneNumberValidator.swift @@ -27,22 +27,22 @@ public struct PhoneNumberValidator: Loggable { public static func isCountryCodeMissing(_ phoneNumber: String) -> Bool { let isCountryCodeMissing = (4.. Bool { for allowedCountryCode in allowedPhoneNumberCountryCodes where phoneNumber.hasPrefix(allowedCountryCode) { - PhoneNumberValidator.logger().debug("Phone number country code is correct") + PhoneNumberValidator.logger().info("Phone number country code is correct") return true } - PhoneNumberValidator.logger().debug("Phone number country code is NOT correct") + PhoneNumberValidator.logger().info("Phone number country code is NOT correct") return false } public static func isPhoneNumberCorrect(_ phoneNumber: String) -> Bool { let isCorrect = phoneNumber.count >= minimumPhoneNumberLength - PhoneNumberValidator.logger().debug("Is phone number correct: \(isCorrect)") + PhoneNumberValidator.logger().info("Is phone number correct: \(isCorrect)") return isCorrect } } diff --git a/RIADigiDoc/DI/AppContainer.swift b/RIADigiDoc/DI/AppContainer.swift index f6b9dcb6..fee6f9d3 100644 --- a/RIADigiDoc/DI/AppContainer.swift +++ b/RIADigiDoc/DI/AppContainer.swift @@ -32,6 +32,7 @@ extension Container { configurationLoader: self.configurationLoader(), configurationRepository: self.configurationRepository(), fileManager: self.fileManager(), + fileUtil: self.fileUtil(), tslUtil: self.tslUtil(), dataStore: self.dataStore(), advancedSettingsRepository: self.advancedSettingsRepository(), @@ -253,7 +254,8 @@ extension Container { tslUtil: self.tslUtil(), dataStore: self.dataStore(), proxyUtil: self.proxyUtil(), - userAgentUtil: self.userAgentUtil() + userAgentUtil: self.userAgentUtil(), + fileUtil: self.fileUtil() ) } } diff --git a/RIADigiDoc/Domain/Preferences/DataStore.swift b/RIADigiDoc/Domain/Preferences/DataStore.swift index 234ef744..88f02827 100644 --- a/RIADigiDoc/Domain/Preferences/DataStore.swift +++ b/RIADigiDoc/Domain/Preferences/DataStore.swift @@ -478,12 +478,28 @@ public actor DataStore: DataStoreProtocol { // MARK: - Logging - public func getIsLoggingEnabled() async -> Bool { - userDefaults().bool(forKey: Keys.isLoggingEnabled) + public func getEnableLoggingNextSession() async -> Bool { + userDefaults().bool(forKey: Keys.enableLoggingNextSession) } - public func setIsLoggingEnabled(_ isEnabled: Bool) async { - userDefaults().set(isEnabled, forKey: Keys.isLoggingEnabled) + public func setEnableLoggingNextSession(_ isEnabled: Bool) async { + userDefaults().set(isEnabled, forKey: Keys.enableLoggingNextSession) + } + + public func getEnableLoggingThisSession() async -> Bool { + userDefaults().bool(forKey: Keys.enableLoggingThisSession) + } + + public func setEnableLoggingThisSession(_ isEnabled: Bool) async { + userDefaults().set(isEnabled, forKey: Keys.enableLoggingThisSession) + } + + public func getIsLogFileSaved() async -> Bool { + userDefaults().bool(forKey: Keys.isLogFileSaved) + } + + public func setIsLogFileSaved(_ isSaved: Bool) async { + userDefaults().set(isSaved, forKey: Keys.isLogFileSaved) } // MARK: - Constants @@ -550,6 +566,8 @@ public actor DataStore: DataStoreProtocol { static let roleZipCode = "roleZipCode" static let nfcCanNumber = "nfcCanNumber" static let nfcRememberMe = "nfcRememberMe" - static let isLoggingEnabled = "isLoggingEnabled" + static let enableLoggingNextSession = "enableLoggingNextSession" + static let enableLoggingThisSession = "enableLoggingThisSession" + static let isLogFileSaved = "isLogFileSaved" } } diff --git a/RIADigiDoc/Domain/Preferences/DataStoreProtocol.swift b/RIADigiDoc/Domain/Preferences/DataStoreProtocol.swift index 7ea827e6..fc7f4b2f 100644 --- a/RIADigiDoc/Domain/Preferences/DataStoreProtocol.swift +++ b/RIADigiDoc/Domain/Preferences/DataStoreProtocol.swift @@ -112,6 +112,10 @@ public protocol DataStoreProtocol: Sendable { func setNFCInputData(_ inputData: NFCInputData) async // MARK: - Logging - func getIsLoggingEnabled() async -> Bool - func setIsLoggingEnabled(_ isEnabled: Bool) async + func getEnableLoggingNextSession() async -> Bool + func setEnableLoggingNextSession(_ isEnabled: Bool) async + func getEnableLoggingThisSession() async -> Bool + func setEnableLoggingThisSession(_ isEnabled: Bool) async + func getIsLogFileSaved() async -> Bool + func setIsLogFileSaved(_ isSaved: Bool) async } diff --git a/RIADigiDoc/LibrarySetup.swift b/RIADigiDoc/LibrarySetup.swift index 7952d01a..d4c48817 100644 --- a/RIADigiDoc/LibrarySetup.swift +++ b/RIADigiDoc/LibrarySetup.swift @@ -30,6 +30,7 @@ actor LibrarySetup: Loggable { private let configurationLoader: ConfigurationLoaderProtocol private let configurationRepository: ConfigurationRepositoryProtocol private let fileManager: FileManagerProtocol + private let fileUtil: FileUtilProtocol private let tslUtil: TSLUtilProtocol private let dataStore: DataStoreProtocol private let advancedSettingsRepository: AdvancedSettingsRepositoryProtocol @@ -42,6 +43,7 @@ actor LibrarySetup: Loggable { configurationLoader: ConfigurationLoaderProtocol, configurationRepository: ConfigurationRepositoryProtocol, fileManager: FileManagerProtocol, + fileUtil: FileUtilProtocol, tslUtil: TSLUtilProtocol, dataStore: DataStoreProtocol, advancedSettingsRepository: AdvancedSettingsRepositoryProtocol, @@ -53,6 +55,7 @@ actor LibrarySetup: Loggable { self.configurationLoader = configurationLoader self.configurationRepository = configurationRepository self.fileManager = fileManager + self.fileUtil = fileUtil self.tslUtil = tslUtil self.dataStore = dataStore self.advancedSettingsRepository = advancedSettingsRepository @@ -63,8 +66,7 @@ actor LibrarySetup: Loggable { } func setupLibraries() async { - let isLoggingEnabled = await dataStore.getIsLoggingEnabled() - await initializeLogging(isLoggingEnabled: isLoggingEnabled) + let isLoggingEnabled = await initializeLogging() do { let proxyInfo = await proxyUtil.getProxyInfo() @@ -97,7 +99,7 @@ actor LibrarySetup: Loggable { LibrarySetup.logger().error("Unable to initialize configuration: \(error)") } - LibrarySetup.logger().debug("Initializing Libdigidocpp") + LibrarySetup.logger().info("Initializing Libdigidocpp") try await DigiDocConf.initDigiDoc( isLoggingEnabled: isLoggingEnabled, tsaOption: getTSAOption(), @@ -175,8 +177,24 @@ actor LibrarySetup: Loggable { ) } - private func initializeLogging(isLoggingEnabled: Bool) async { + private func initializeLogging() async -> Bool { + #if DEBUG || ENABLE_LOGGING + let isLoggingEnabled = true + await dataStore.setEnableLoggingNextSession(true) + #else + let isLoggingEnabled = await dataStore.getEnableLoggingNextSession() + if !isLoggingEnabled { + fileUtil.removeCacheLogsDirectory() + fileUtil.removeLibraryLogsDirectory(directory: nil) + } + await dataStore.setEnableLoggingNextSession(false) + #endif + Container.shared.isLoggingEnabled.register { isLoggingEnabled } + await dataStore.setEnableLoggingThisSession(isLoggingEnabled) + await dataStore.setIsLogFileSaved(false) + + return isLoggingEnabled } private func getTSAOption() async -> ServicesSettingsOption { diff --git a/RIADigiDoc/Supporting files/Localizable.xcstrings b/RIADigiDoc/Supporting files/Localizable.xcstrings index fab0d8d8..d85ca895 100644 --- a/RIADigiDoc/Supporting files/Localizable.xcstrings +++ b/RIADigiDoc/Supporting files/Localizable.xcstrings @@ -3029,13 +3029,13 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enable one-time log generation" + "value" : "Create a log file on the next startup" } }, "et" : { "stringUnit" : { "state" : "translated", - "value" : "Aktiveeri ühekordne logifaili genereerimine" + "value" : "Loo järgmisel käivitusel logifail" } } } @@ -3094,6 +3094,24 @@ } } }, + "Main diagnostics restart message deactivate" : { + "comment" : "Main diagnostics restart message deactivate logging", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restart RIA DigiDoc to deactivate logging." + } + }, + "et" : { + "stringUnit" : { + "state" : "translated", + "value" : "Logimise deaktiveerimiseks taaskäivita RIA DigiDoc." + } + } + } + }, "Main diagnostics restart message restart now" : { "comment" : "Main diagnostics restart message restart now", "extractionState" : "manual", diff --git a/RIADigiDoc/UI/Component/DiagnosticsView.swift b/RIADigiDoc/UI/Component/DiagnosticsView.swift index 2a0c01bd..43569a6d 100644 --- a/RIADigiDoc/UI/Component/DiagnosticsView.swift +++ b/RIADigiDoc/UI/Component/DiagnosticsView.swift @@ -23,21 +23,34 @@ import UtilsLib struct DiagnosticsView: View { @AppTheme private var theme + @AppTypography private var typography @Environment(LanguageSettings.self) private var languageSettings - @Environment(\.dismiss) private var dismiss + @Environment(\.openURL) var openURL private let fileUtil: FileUtilProtocol - @State private var enableOneTimeLogGeneration = false // TODO: implement one time log generation logic + private enum ExportType { + case diagnosticsFile + case logFile + } - @State private var tempDiagnosticsFileURL: URL? - @State private var isShowingFileSaver = false + @State private var activeExportType: ExportType? + @State private var tempFileURL: URL? + @State private var isShowingFileSaver: Bool = false @State private var isFileSaved: Bool = false @State private var viewModel: DiagnosticsViewModel + private var restartText: String { + if viewModel.enableOneTimeLogGeneration { + return languageSettings.localized("Main diagnostics restart message") + } else { + return languageSettings.localized("Main diagnostics restart message deactivate") + } + } + init( fileUtil: FileUtilProtocol = Container.shared.fileUtil(), ) { @@ -67,22 +80,43 @@ struct DiagnosticsView: View { }, onSaveDiagnosticsClick: { Task { - tempDiagnosticsFileURL = await viewModel.createLogFile( + tempFileURL = await viewModel.createDiagnosticsFile( languageSettings: languageSettings ) - - if fileUtil.fileExists(fileLocation: tempDiagnosticsFileURL) { - isShowingFileSaver = true - } + triggerFileSaver(type: .diagnosticsFile) } } ) ToggleSection( - isOn: $enableOneTimeLogGeneration, + isOn: $viewModel.enableOneTimeLogGeneration, label: languageSettings.localized("Main diagnostics logging switch") ) + if viewModel.showSaveLogButton { + PrimaryOutlinedButton( + text: languageSettings.localized("Main diagnostics save log"), + assetImageName: "ic_m3_download_48pt_wght400", + action: { + Task { + tempFileURL = await viewModel.createLogFile() + triggerFileSaver(type: .logFile) + } + } + ) + } + + if viewModel.showRestartText { + HStack { + Text(restartText) + .font(typography.bodyLarge) + .foregroundStyle(theme.error) + .padding(.top, Dimensions.Padding.ZeroPadding) + .padding(.bottom, Dimensions.Padding.MSPadding) + Spacer() + } + } + DiagnosticsSections( versionSectionContent: viewModel.versionSectionContent, osSectionContent: viewModel.osSectionContent, @@ -98,14 +132,22 @@ struct DiagnosticsView: View { .background( FileSaverHandler( isPresented: $isShowingFileSaver, - fileURL: tempDiagnosticsFileURL, + fileURL: tempFileURL, languageSettings: languageSettings, onComplete: { - viewModel.removeLogFilesDirectory() + handleFileSaverCompletion() }, isFileSaved: $isFileSaved ) ) + .alert( + languageSettings.localized("Main diagnostics restart message"), + isPresented: $viewModel.showRestartActivateAlert + ) {alertContent} + .alert( + languageSettings.localized("Main diagnostics restart message deactivate"), + isPresented: $viewModel.showRestartDeactivateAlert + ) {alertContent} .task { await viewModel .getConfigurationData( @@ -117,6 +159,11 @@ struct DiagnosticsView: View { Task { await viewModel.getConfigurationData(configuration: newConfig) } } }) + .onChange(of: viewModel.enableOneTimeLogGeneration) { _, newValue in + Task { + await viewModel.onEnableOneTimeLogGenerationChange(newValue) + } + } .onDisappear { Task { await viewModel.removeObservers() @@ -126,6 +173,40 @@ struct DiagnosticsView: View { } ) } + + @ViewBuilder + private var alertContent: some View { + Button(languageSettings.localized("OK")) {} + Button(languageSettings.localized("Read more here")) { + if let url = URL( + string: languageSettings.localized("main diagnostics restart message url") + ) { + openURL(url) + } + } + } + + private func triggerFileSaver(type: ExportType) { + self.activeExportType = type + if fileUtil.fileExists(fileLocation: tempFileURL) { + isShowingFileSaver = true + } + } + + private func handleFileSaverCompletion() { + guard let type = activeExportType else { return } + + switch type { + case .diagnosticsFile: + viewModel.onDiagnosticsFileSavingComplete() + case .logFile: + Task { + await viewModel.onLogFileSavingComplete() + } + } + + activeExportType = nil + } } // MARK: - Preview @@ -133,4 +214,5 @@ struct DiagnosticsView: View { DiagnosticsView() .environment(Container.shared.languageSettings()) .environment(Container.shared.themeSettings()) + .environment(NavigationPathManager()) } diff --git a/RIADigiDoc/ViewModel/ContentViewModel.swift b/RIADigiDoc/ViewModel/ContentViewModel.swift index b9e064ea..2e508f2f 100644 --- a/RIADigiDoc/ViewModel/ContentViewModel.swift +++ b/RIADigiDoc/ViewModel/ContentViewModel.swift @@ -38,7 +38,7 @@ class ContentViewModel: ContentViewModelProtocol, Loggable { func getSharedFiles() async -> [URL] { do { - ContentViewModel.logger().debug("Checking for shared files...") + ContentViewModel.logger().info("Checking for shared files...") let sharedFolderURL = try await Directories.getSharedFolder(fileManager: fileManager) .validURL(fileUtil: fileUtil) @@ -48,9 +48,9 @@ class ContentViewModel: ContentViewModelProtocol, Loggable { options: .skipsHiddenFiles) if contents.isEmpty { - ContentViewModel.logger().debug("Shared files folder is empty") + ContentViewModel.logger().info("Shared files folder is empty") } else { - ContentViewModel.logger().debug("Found \(contents.count) shared files") + ContentViewModel.logger().info("Found \(contents.count) shared files") } return contents diff --git a/RIADigiDoc/ViewModel/CryptoFileOpeningViewModel.swift b/RIADigiDoc/ViewModel/CryptoFileOpeningViewModel.swift index feb8ccb4..8de369dd 100644 --- a/RIADigiDoc/ViewModel/CryptoFileOpeningViewModel.swift +++ b/RIADigiDoc/ViewModel/CryptoFileOpeningViewModel.swift @@ -60,17 +60,17 @@ class CryptoFileOpeningViewModel: CryptoFileOpeningViewModelProtocol, Loggable { func handleFiles() async { do { - CryptoFileOpeningViewModel.logger().debug("Handling chosen files from file system or from external sources") + CryptoFileOpeningViewModel.logger().info("Handling chosen files from file system or from external sources") let validFiles = try await fileOpeningRepository.getValidFiles( sharedContainerViewModel.getFileOpeningResult() ?? .failure(FileOpeningError.noDataFiles) ) try fileUtil.removeSharedFiles(url: Directories.getSharedFolder(fileManager: fileManager)) - CryptoFileOpeningViewModel.logger().debug("Found \(validFiles.count) valid file(s)") + CryptoFileOpeningViewModel.logger().info("Found \(validFiles.count) valid file(s)") if validFiles.isEmpty { - CryptoFileOpeningViewModel.logger().debug("No valid files found") + CryptoFileOpeningViewModel.logger().info("No valid files found") throw FileOpeningError.noDataFiles } diff --git a/RIADigiDoc/ViewModel/DataFilesViewModel.swift b/RIADigiDoc/ViewModel/DataFilesViewModel.swift index b1e06cc9..dfa1bee8 100644 --- a/RIADigiDoc/ViewModel/DataFilesViewModel.swift +++ b/RIADigiDoc/ViewModel/DataFilesViewModel.swift @@ -62,7 +62,7 @@ class DataFilesViewModel: DataFilesViewModelProtocol, Loggable { fileManager: fileManager ) try fileManager.removeItem(at: directory) - DataFilesViewModel.logger().debug("Saved Files directory removed") + DataFilesViewModel.logger().info("Saved Files directory removed") } catch { DataFilesViewModel.logger().error("Unable to delete saved files directory: \(error.localizedDescription)") } diff --git a/RIADigiDoc/ViewModel/DiagnosticsViewModel.swift b/RIADigiDoc/ViewModel/DiagnosticsViewModel.swift index 3bb9a251..84f67125 100644 --- a/RIADigiDoc/ViewModel/DiagnosticsViewModel.swift +++ b/RIADigiDoc/ViewModel/DiagnosticsViewModel.swift @@ -17,10 +17,10 @@ * */ -import SwiftUI import CommonsLib import ConfigLib import LibdigidocLibSwift +import OSLog import UtilsLib @Observable @@ -28,6 +28,13 @@ import UtilsLib class DiagnosticsViewModel: DiagnosticsViewModelProtocol, Loggable { var configuration: ConfigurationProvider? + var enableOneTimeLogGeneration = false + var showSaveLogButton = false + var showRestartActivateAlert = false + var showRestartDeactivateAlert = false + + var showRestartText = false + // MARK: - section content var versionSectionContent: String = "" var osSectionContent: (key: String, content: String) = (key: "", content: "") @@ -46,6 +53,7 @@ class DiagnosticsViewModel: DiagnosticsViewModelProtocol, Loggable { private let dataStore: DataStoreProtocol private let proxyUtil: ProxyUtilProtocol private let userAgentUtil: UserAgentUtilProtocol + private let fileUtil: FileUtilProtocol private var configurationObservationTask: Task? @@ -57,7 +65,8 @@ class DiagnosticsViewModel: DiagnosticsViewModelProtocol, Loggable { tslUtil: TSLUtilProtocol, dataStore: DataStoreProtocol, proxyUtil: ProxyUtilProtocol, - userAgentUtil: UserAgentUtilProtocol + userAgentUtil: UserAgentUtilProtocol, + fileUtil: FileUtilProtocol ) { self.containerWrapper = containerWrapper self.fileManager = fileManager @@ -67,6 +76,7 @@ class DiagnosticsViewModel: DiagnosticsViewModelProtocol, Loggable { self.dataStore = dataStore self.proxyUtil = proxyUtil self.userAgentUtil = userAgentUtil + self.fileUtil = fileUtil configurationObservationTask = Task { await observeConfigurationUpdates() @@ -74,6 +84,7 @@ class DiagnosticsViewModel: DiagnosticsViewModelProtocol, Loggable { Task { await loadLibdigidocVersion() + await loadLoggingVariables() } } @@ -81,7 +92,7 @@ class DiagnosticsViewModel: DiagnosticsViewModelProtocol, Loggable { configurationObservationTask?.cancel() } - // MARK: - Fetching content + // MARK: - Fetching content - public func getConfigurationData( configuration: ConfigurationProvider?, @@ -95,6 +106,8 @@ class DiagnosticsViewModel: DiagnosticsViewModelProtocol, Loggable { loadCentralConfigurationContent(configuration: configuration) } + // MARK: - Fetching content - private + func getRpUuid() async -> String { await dataStore.getRelyingPartyUUID() } @@ -216,43 +229,24 @@ class DiagnosticsViewModel: DiagnosticsViewModelProtocol, Loggable { return "\(dateTime.date) \(dateTime.time)" } - // MARK: - Create Log File + // MARK: - Create Diagnostics File - public - func createLogFile(languageSettings: LanguageSettingsProtocol, directory: URL? = nil) async -> URL? { + func createDiagnosticsFile(languageSettings: LanguageSettingsProtocol, directory: URL? = nil) async -> URL? { let diagnosticsText = buildDiagnosticsText(languageSettings: languageSettings) - do { - - let savedFilesDirectory = try directory ?? Directories.getCacheDirectory( - subfolders: [CommonsLib.Constants.Folder.Logs], - fileManager: fileManager - ) - let diagnosticsFileName = "ria_digidoc_\(self.versionSectionContent)_diagnostics.log" - let fileURL = savedFilesDirectory.appending(path: diagnosticsFileName) - - try diagnosticsText.write(to: fileURL, atomically: true, encoding: .utf8) - - return fileURL - } catch { - DiagnosticsViewModel.logger().error( - "Failed to write diagnostics file: \(error.localizedDescription)") - } - return nil + let diagnosticsFileName = "ria_digidoc_\(self.versionSectionContent)_diagnostics.log" + return writeToTempFile( + content: diagnosticsText, + fileName: diagnosticsFileName, + directory: directory + ) } - func removeLogFilesDirectory() { - do { - let directory = try Directories.getCacheDirectory( - subfolders: [CommonsLib.Constants.Folder.Logs], - fileManager: fileManager - ) - try fileManager.removeItem(at: directory) - DiagnosticsViewModel.logger().debug("Saved Files directory removed") - } catch { - DiagnosticsViewModel.logger().error( - "Unable to delete saved files directory: \(error.localizedDescription)") - } + func onDiagnosticsFileSavingComplete() { + fileUtil.removeCacheLogsDirectory() } + // MARK: - Create Diagnostics File - private + private func buildDiagnosticsText(languageSettings: LanguageSettingsProtocol) -> String { var lines: [String] = [] @@ -290,7 +284,15 @@ class DiagnosticsViewModel: DiagnosticsViewModelProtocol, Loggable { return lines.joined(separator: "\n") } - // MARK: - Update configuration + private func getTempFileURL(fileName: String, directory: URL? = nil) throws -> URL { + let savedFilesDirectory = try directory ?? Directories.getCacheDirectory( + subfolders: [CommonsLib.Constants.Folder.Logs], + fileManager: fileManager + ) + return savedFilesDirectory.appending(path: fileName) + } + + // MARK: - Update configuration - public func updateConfiguration() async -> Bool { do { @@ -314,6 +316,174 @@ class DiagnosticsViewModel: DiagnosticsViewModelProtocol, Loggable { } } + // MARK: - Creating Log - public + + public func onEnableOneTimeLogGenerationChange(_ isEnabled: Bool) async { + let dataStoreValue = await dataStore.getEnableLoggingNextSession() + if isEnabled == dataStoreValue { + return + } + + if isEnabled { + self.showRestartActivateAlert = true + } + await dataStore.setEnableLoggingNextSession(isEnabled) + + showRestartText = isEnabled + + if !isEnabled { + showSaveLogButton = false + removeAllLogFiles() + await dataStore.setEnableLoggingThisSession(false) + } + } + + public func createLogFile(directory: URL? = nil) async -> URL? { + let appLogEntries = await readAppLogEntries() + let libdigidocLogEntries = await readLibDigidocLogEntries() + let mergedLines = mergeLogEntries(appLogEntries, libdigidocLogEntries) + let logFileName = "ria_digidoc_\(self.versionSectionContent).log" + return writeToTempFile( + content: mergedLines, + fileName: logFileName, + directory: directory + ) + } + + public func onLogFileSavingComplete() async { + removeAllLogFiles() + + await dataStore.setEnableLoggingNextSession(false) + await dataStore.setIsLogFileSaved(true) + self.showRestartText = true + self.enableOneTimeLogGeneration = false + self.showSaveLogButton = false + self.showRestartDeactivateAlert = true + } + + // MARK: - Creating Log - private + + private func loadLoggingVariables() async { + enableOneTimeLogGeneration = await dataStore.getEnableLoggingNextSession() + let enableLoggingThisSession = await dataStore.getEnableLoggingThisSession() + let isLogFileSaved = await dataStore.getIsLogFileSaved() + showSaveLogButton = enableLoggingThisSession && !isLogFileSaved + + if (enableOneTimeLogGeneration && !showSaveLogButton) || + (enableLoggingThisSession && isLogFileSaved) { + showRestartText = true + } + } + + private func writeToTempFile(content: String, fileName: String, directory: URL?) -> URL? { + do { + let fileURL = try getTempFileURL(fileName: fileName, directory: directory) + try content.write(to: fileURL, atomically: true, encoding: .utf8) + return fileURL + } catch { + DiagnosticsViewModel.logger().error("Unable to write \"\(fileName)\" file: \(error)") + } + return nil + } + + private func removeAllLogFiles() { + fileUtil.removeCacheLogsDirectory() + fileUtil.removeLibraryLogsDirectory(directory: nil) + } + + private func entriesToLines(_ entries: AnySequence) -> [String] { + var lines = [String]() + for entry in entries { + if let log = entry as? OSLogEntryLog { + lines.append(""" + \(entry.date) \ + [\(log.subsystem):\(log.category)] - \ + \(entry.composedMessage) + """) + } else { + lines.append("\(entry.date): \(entry.composedMessage)\n") + } + } + return lines + } + + private func readAppLogEntries() async -> [String]? { + return await withCheckedContinuation { continuation in + Task.detached(priority: .userInitiated) { + do { + let store = try OSLogStore(scope: .currentProcessIdentifier) + let oneDayAgo = Calendar.current.date(byAdding: .day, value: -1, to: Date()) + guard let yesterday = oneDayAgo else { + continuation.resume(returning: nil) + return + } + + let position = store.position(date: yesterday) + let bundleIdentifier = BundleUtil.getBundleIdentifier() + let predicate = NSPredicate( + format: "subsystem BEGINSWITH %@", + bundleIdentifier + ) + let entries = try store.getEntries(at: position, matching: predicate) + let lines = await self.entriesToLines(entries) + continuation.resume(returning: lines) + } catch { + DiagnosticsViewModel.logger().error("Unable to get app log entries: \(error)") + continuation.resume(returning: nil) + } + } + } + } + + private func readLibDigidocLogEntries() async -> [String]? { + if let libdigidocLogURL = await getLibDigidocLogURL() { + return getLines(from: libdigidocLogURL) + } + return nil + } + + private func getLibDigidocLogURL() async -> URL? { + do { + return try Directories.getLibdigidocLogFile( + from: Directories.getLibraryDirectory(fileManager: fileManager), + fileManager: fileManager + ) + } catch { + DiagnosticsViewModel.logger().error("Unable to get libdigidoc log URL: \(error)") + } + return nil + } + + private func getLines(from url: URL?) -> [String] { + guard let url = url, fileManager.fileExists(atPath: url.path) else { return [] } + do { + let content = try String(contentsOf: url, encoding: .utf8) + return content.components(separatedBy: .newlines).filter { !$0.isEmpty } + } catch { + return [] + } + } + + private func mergeLogEntries(_ appLogEntries: [String]?, _ libDigidocLogEntries: [String]?) -> String { + var allEntries: [String] = [] + + allEntries.append("===== File: \(Constants.File.LibDigidocLog) =====") + allEntries.append("") + if let libDigidocLogEntries = libDigidocLogEntries { + allEntries.append(contentsOf: libDigidocLogEntries) + } + + allEntries.append("") + allEntries.append("") + allEntries.append("===== File: ria_digidoc.log =====") + allEntries.append("") + if let appLogEntries = appLogEntries { + allEntries.append(contentsOf: appLogEntries) + } + + return allEntries.joined(separator: "\n") + } + // MARK: - Observer public func observeConfigurationUpdates() async { diff --git a/RIADigiDoc/ViewModel/EncryptViewModel.swift b/RIADigiDoc/ViewModel/EncryptViewModel.swift index 340d4346..3e9b8ea9 100644 --- a/RIADigiDoc/ViewModel/EncryptViewModel.swift +++ b/RIADigiDoc/ViewModel/EncryptViewModel.swift @@ -84,7 +84,7 @@ class EncryptViewModel: EncryptViewModelProtocol, Loggable { } func loadContainerData(cryptoContainer: CryptoContainerProtocol?) async { - EncryptViewModel.logger().debug("Loading container data") + EncryptViewModel.logger().info("Loading container data") let openedContainer = (cryptoContainer ?? sharedContainerViewModel.currentContainer()) as? any CryptoContainerProtocol guard let openedContainer else { @@ -100,7 +100,7 @@ class EncryptViewModel: EncryptViewModelProtocol, Loggable { self.containerMimetype = await openedContainer.getContainerMimetype() self.containerURL = await openedContainer.getRawContainerFile() - EncryptViewModel.logger().debug("Container data loaded") + EncryptViewModel.logger().info("Container data loaded") } func createCopyOfContainerForSaving(containerURL: URL?) -> URL? { @@ -157,7 +157,7 @@ class EncryptViewModel: EncryptViewModelProtocol, Loggable { } await cryptoContainer?.addDataFiles(files) - EncryptViewModel.logger().debug("Added data files to container") + EncryptViewModel.logger().info("Added data files to container") errorMessage = ToastMessage( key: files.count == 1 ? "File successfully added" : "Files successfully added", args: [] diff --git a/RIADigiDoc/ViewModel/FileOpeningViewModel.swift b/RIADigiDoc/ViewModel/FileOpeningViewModel.swift index 92d83af8..ecad40f0 100644 --- a/RIADigiDoc/ViewModel/FileOpeningViewModel.swift +++ b/RIADigiDoc/ViewModel/FileOpeningViewModel.swift @@ -61,17 +61,17 @@ class FileOpeningViewModel: FileOpeningViewModelProtocol, Loggable { func handleFiles() async { do { - FileOpeningViewModel.logger().debug("Handling chosen files from file system or from external sources") + FileOpeningViewModel.logger().info("Handling chosen files from file system or from external sources") let validFiles = try await fileOpeningRepository.getValidFiles( sharedContainerViewModel.getFileOpeningResult() ?? .failure(FileOpeningError.noDataFiles) ) try fileUtil.removeSharedFiles(url: Directories.getSharedFolder(fileManager: fileManager)) - FileOpeningViewModel.logger().debug("Found \(validFiles.count) valid file(s)") + FileOpeningViewModel.logger().info("Found \(validFiles.count) valid file(s)") if validFiles.isEmpty { - FileOpeningViewModel.logger().debug("No valid files found") + FileOpeningViewModel.logger().info("No valid files found") throw FileOpeningError.noDataFiles } @@ -118,7 +118,7 @@ class FileOpeningViewModel: FileOpeningViewModelProtocol, Loggable { let container = try await fileOpeningRepository .openOrCreateContainer(urls: files, isSivaConfirmed: false) sharedContainerViewModel.setSignedContainer(container) - FileOpeningViewModel.logger().debug("Asics signed container set successfully") + FileOpeningViewModel.logger().info("Asics signed container set successfully") handleLoadingSuccess(isSivaConfirmed: false) } catch { FileOpeningViewModel.logger().error("Unable to handle SiVa container. \(error)") diff --git a/RIADigiDoc/ViewModel/Protocols/DiagnosticsViewModelProtocol.swift b/RIADigiDoc/ViewModel/Protocols/DiagnosticsViewModelProtocol.swift index f755a034..fa688840 100644 --- a/RIADigiDoc/ViewModel/Protocols/DiagnosticsViewModelProtocol.swift +++ b/RIADigiDoc/ViewModel/Protocols/DiagnosticsViewModelProtocol.swift @@ -25,6 +25,11 @@ import CommonsLib @MainActor public protocol DiagnosticsViewModelProtocol: Sendable { // MARK: Published properties + var enableOneTimeLogGeneration: Bool { get } + var showSaveLogButton: Bool { get } + var showRestartActivateAlert: Bool { get } + var showRestartDeactivateAlert: Bool { get } + var versionSectionContent: String { get } var osSectionContent: (key: String, content: String) { get } var libdigidocVersion: String { get } @@ -38,8 +43,13 @@ public protocol DiagnosticsViewModelProtocol: Sendable { // MARK: Actions func updateConfiguration() async -> Bool - func createLogFile(languageSettings: LanguageSettingsProtocol, directory: URL?) async -> URL? - func removeLogFilesDirectory() + func createDiagnosticsFile(languageSettings: LanguageSettingsProtocol, directory: URL?) async -> URL? + func onDiagnosticsFileSavingComplete() + func onEnableOneTimeLogGenerationChange(_ isEnabled: Bool) async + func createLogFile(directory: URL?) async -> URL? + func onLogFileSavingComplete() async func removeObservers() async + func getRpUuid() async -> String + func observeConfigurationUpdates() async } diff --git a/RIADigiDoc/ViewModel/Signing/IdCard/IdCardViewModel.swift b/RIADigiDoc/ViewModel/Signing/IdCard/IdCardViewModel.swift index ff76469c..2c915b6b 100644 --- a/RIADigiDoc/ViewModel/Signing/IdCard/IdCardViewModel.swift +++ b/RIADigiDoc/ViewModel/Signing/IdCard/IdCardViewModel.swift @@ -143,7 +143,7 @@ class IdCardViewModel: IdCardViewModelProtocol, Loggable { } private func getPublicData() async throws -> CardInfo { - IdCardViewModel.logger().debug( + IdCardViewModel.logger().info( "ID-CARD: Getting public data from ID-card with reader" ) @@ -151,7 +151,7 @@ class IdCardViewModel: IdCardViewModelProtocol, Loggable { } private func readAuthenticationCertificateNotValidDate() async throws -> String? { - IdCardViewModel.logger().debug( + IdCardViewModel.logger().info( "Reading authentication certificate from ID-card with reader" ) @@ -162,7 +162,7 @@ class IdCardViewModel: IdCardViewModelProtocol, Loggable { } private func readSignatureCertificateNotValidDate() async throws -> String? { - IdCardViewModel.logger().debug( + IdCardViewModel.logger().info( "ID-CARD: Reading signature certificate from ID-card with reader" ) @@ -173,7 +173,7 @@ class IdCardViewModel: IdCardViewModelProtocol, Loggable { } private func readCodeTryCounterRecord() async throws -> RetryCount { - IdCardViewModel.logger().debug( + IdCardViewModel.logger().info( "ID-CARD: Reading retry counter record from ID-card with reader" ) @@ -188,7 +188,7 @@ class IdCardViewModel: IdCardViewModelProtocol, Loggable { } private func isPukChangeable() async throws -> Bool { - IdCardViewModel.logger().debug( + IdCardViewModel.logger().info( "ID-CARD: Reading if PUK is changeable for this ID-card with reader" ) diff --git a/RIADigiDoc/ViewModel/Signing/MobileId/MobileIdViewModel.swift b/RIADigiDoc/ViewModel/Signing/MobileId/MobileIdViewModel.swift index ed824960..7d6e847f 100644 --- a/RIADigiDoc/ViewModel/Signing/MobileId/MobileIdViewModel.swift +++ b/RIADigiDoc/ViewModel/Signing/MobileId/MobileIdViewModel.swift @@ -112,7 +112,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { roleData: RoleData, signedContainer: SignedContainerProtocol ) async -> SignedContainerProtocol? { - MobileIdViewModel.logger().debug("Mobile-ID: Signing with Mobile-ID") + MobileIdViewModel.logger().info("Mobile-ID: Signing with Mobile-ID") let currentConfiguration = await configurationRepository.getConfiguration() guard let configuration = currentConfiguration else { @@ -134,17 +134,17 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { let proxyInfo = await proxyUtil.getProxyInfo() - MobileIdViewModel.logger().debug("Mobile-ID: Getting language") + MobileIdViewModel.logger().info("Mobile-ID: Getting language") let appLanguage = await dataStore.getSelectedLanguage() - MobileIdViewModel.logger().debug("Mobile-ID: Getting User-Agent") + MobileIdViewModel.logger().info("Mobile-ID: Getting User-Agent") let userAgent = userAgentUtil.userAgent(diagnostics: .none, language: appLanguage) let containerFile: URL do { containerFile = try await getContainerFile(signedContainer: signedContainer) } catch { - MobileIdViewModel.logger().debug("Mobile-ID: Unable to get container file from container") + MobileIdViewModel.logger().info("Mobile-ID: Unable to get container file from container") handleSigningError(error) return nil } @@ -160,7 +160,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { userAgent: userAgent ) } catch { - MobileIdViewModel.logger().debug("Mobile-ID: Unable to request certificate or get cert from response") + MobileIdViewModel.logger().info("Mobile-ID: Unable to request certificate or get cert from response") handleSigningError(error) return nil } @@ -175,7 +175,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { userAgent: userAgent ) } catch { - MobileIdViewModel.logger().debug("Mobile-ID: Unable to prepare signature for signing") + MobileIdViewModel.logger().info("Mobile-ID: Unable to prepare signature for signing") handleSigningError(error) return nil } @@ -184,7 +184,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { do { verificationCode = try await getVerificationCode(hash: hash) } catch { - MobileIdViewModel.logger().debug("Mobile-ID: Unable to get verification code (control code)") + MobileIdViewModel.logger().info("Mobile-ID: Unable to get verification code (control code)") handleSigningError(error) return nil } @@ -204,7 +204,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { userAgent: userAgent ) } catch { - MobileIdViewModel.logger().debug("Mobile-ID: Unable to request signature") + MobileIdViewModel.logger().info("Mobile-ID: Unable to request signature") handleSigningError(error) return nil } @@ -219,7 +219,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { userAgent: userAgent ) } catch { - MobileIdViewModel.logger().debug( + MobileIdViewModel.logger().info( "Mobile-ID: Unable to request session" ) handleSigningError(error) @@ -234,7 +234,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { containerFile: containerFile ) - MobileIdViewModel.logger().debug("Signature added successfully (Mobile-ID)") + MobileIdViewModel.logger().info("Signature added successfully (Mobile-ID)") mobileIdMessageKey = "Signature added" return updatedContainer } catch { @@ -257,7 +257,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { proxyInfo: ProxyInfo, userAgent: String ) async throws -> Data { - MobileIdViewModel.logger().debug("Mobile-ID: Getting certificate") + MobileIdViewModel.logger().info("Mobile-ID: Getting certificate") let certResponse = try await mobileIdSignService .getCertificateRequest( url: "\(midUrl)\(MobileIdViewModel.certificateEndpoint)", @@ -292,7 +292,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { proxyInfo: ProxyInfo, userAgent: String ) async throws -> String { - MobileIdViewModel.logger().debug("Mobile-ID: Getting signature") + MobileIdViewModel.logger().info("Mobile-ID: Getting signature") let signatureResponse = try await mobileIdSignService.getSignatureRequest( url: "\(midUrl)\(MobileIdViewModel.signatureEndpoint)", relyingPartyName: Constants.Signing.RelyingPartyName, @@ -309,7 +309,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { userAgent: userAgent ) - MobileIdViewModel.logger().debug("Mobile-ID: Getting sessionID") + MobileIdViewModel.logger().info("Mobile-ID: Getting sessionID") guard let sessionId = signatureResponse.sessionID else { MobileIdViewModel.logger().error("Unable to get Mobile-ID sessionID") mobileIdMessageKey = "Technical error" @@ -326,7 +326,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { proxyInfo: ProxyInfo, userAgent: String ) async throws -> Data { - MobileIdViewModel.logger().debug("Mobile-ID: Getting session request") + MobileIdViewModel.logger().info("Mobile-ID: Getting session request") let sessionResponse = try await mobileIdSignService.getSessionRequest( url: "\(midUrl)\(MobileIdViewModel.signatureSessionEndpoint)", sessionId: sessionId, @@ -350,7 +350,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { signedContainer: SignedContainerProtocol, userAgent: String ) async throws -> Data { - MobileIdViewModel.logger().debug( + MobileIdViewModel.logger().info( "Mobile-ID: Preparing signature. Calculating hash" ) @@ -363,7 +363,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { } private func getVerificationCode(hash: Data) async throws -> String { - MobileIdViewModel.logger().debug( + MobileIdViewModel.logger().info( "Mobile-ID: Calculating verification code (control code)" ) guard let verificationCode = await mobileIdSignService.getVerificationCode(hash: hash) else { @@ -392,7 +392,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { } private func getTrustedCertificates(certificates: [Data]) -> [SecCertificate] { - MobileIdViewModel.logger().debug("Mobile-ID: Getting trusted certificates list") + MobileIdViewModel.logger().info("Mobile-ID: Getting trusted certificates list") return certificates.compactMap { certificateUtil.certificate(from: $0) } } @@ -427,7 +427,7 @@ class MobileIdViewModel: MobileIdViewModelProtocol, Loggable { case .explicitlyCancelled: // Do not throw an error if user cancelled signing with back button - MobileIdViewModel.logger().debug("Mobile-ID signing manually cancelled") + MobileIdViewModel.logger().info("Mobile-ID signing manually cancelled") mobileIdMessageKey = nil case .timeout: diff --git a/RIADigiDoc/ViewModel/Signing/SmartId/SmartIdViewModel.swift b/RIADigiDoc/ViewModel/Signing/SmartId/SmartIdViewModel.swift index b00afb3f..f3589a16 100644 --- a/RIADigiDoc/ViewModel/Signing/SmartId/SmartIdViewModel.swift +++ b/RIADigiDoc/ViewModel/Signing/SmartId/SmartIdViewModel.swift @@ -119,7 +119,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { roleData: RoleData, signedContainer: SignedContainerProtocol ) async -> SignedContainerProtocol? { - SmartIdViewModel.logger().debug("Smart-ID: Signing with Smart-ID") + SmartIdViewModel.logger().info("Smart-ID: Signing with Smart-ID") let currentConfiguration = await configurationRepository.getConfiguration() @@ -142,17 +142,17 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { let proxyInfo = await proxyUtil.getProxyInfo() - SmartIdViewModel.logger().debug("Smart-ID: Getting language") + SmartIdViewModel.logger().info("Smart-ID: Getting language") let appLanguage = await dataStore.getSelectedLanguage() - SmartIdViewModel.logger().debug("Smart-ID: Getting User-Agent") + SmartIdViewModel.logger().info("Smart-ID: Getting User-Agent") let userAgent = userAgentUtil.userAgent(diagnostics: .none, language: appLanguage) let containerFile: URL do { containerFile = try await getContainerFile(signedContainer: signedContainer) } catch { - SmartIdViewModel.logger().debug("Smart-ID: Unable to get container file from container") + SmartIdViewModel.logger().info("Smart-ID: Unable to get container file from container") handleSigningError(error) return nil } @@ -171,7 +171,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { userAgent: userAgent ) } catch { - SmartIdViewModel.logger().debug("Smart-ID: Unable to request certificate or get response") + SmartIdViewModel.logger().info("Smart-ID: Unable to request certificate or get response") handleSigningError(error) return nil } @@ -194,7 +194,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { userAgent: userAgent ) } catch { - SmartIdViewModel.logger().debug("Smart-ID: Unable to prepare signature for signing") + SmartIdViewModel.logger().info("Smart-ID: Unable to prepare signature for signing") handleSigningError(error) return nil } @@ -235,7 +235,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { ) } catch { endBackgroundTask() - SmartIdViewModel.logger().debug("Smart-ID: Unable to request signature or get cert from response") + SmartIdViewModel.logger().info("Smart-ID: Unable to request signature or get cert from response") handleSigningError(error) notificationUtil.removeNotification(id: notificationIdentifier) return nil @@ -251,7 +251,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { containerFile: containerFile ) - SmartIdViewModel.logger().debug("Signature added successfully (Smart-ID)") + SmartIdViewModel.logger().info("Signature added successfully (Smart-ID)") smartIdMessageKey = "Signature added" notificationUtil.removeNotification(id: notificationIdentifier) return updatedContainer @@ -296,7 +296,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { proxyInfo: ProxyInfo, userAgent: String ) async throws -> SmartIdSessionResponse { - SmartIdViewModel.logger().debug("Smart-ID: Getting certificate") + SmartIdViewModel.logger().info("Smart-ID: Getting certificate") let certResponse = try await smartIdSignService .getCertificateRequest( url: "\(sidUrl)\(SmartIdViewModel.certificateEndpoint)", @@ -313,7 +313,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { throw SmartIdError.missingSessionId } - SmartIdViewModel.logger().debug("Smart-ID: Requesting session from certificate response") + SmartIdViewModel.logger().info("Smart-ID: Requesting session from certificate response") return try await requestSession( sidUrl: "\(sidUrl)\(SmartIdViewModel.sessionEndpoint)", @@ -338,7 +338,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { proxyInfo: ProxyInfo, userAgent: String ) async throws -> Data { - SmartIdViewModel.logger().debug("Smart-ID: Getting signature") + SmartIdViewModel.logger().info("Smart-ID: Getting signature") let certResponse = try await smartIdSignService .getSignatureRequest( @@ -359,7 +359,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { throw SmartIdError.missingSessionId } - SmartIdViewModel.logger().debug("Smart-ID: Requesting session from signature response") + SmartIdViewModel.logger().info("Smart-ID: Requesting session from signature response") let sessionResponse = try await requestSession( sidUrl: "\(sidUrl)\(SmartIdViewModel.sessionEndpoint)", @@ -399,7 +399,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { proxyInfo: ProxyInfo, userAgent: String ) async throws -> SmartIdSessionResponse { - SmartIdViewModel.logger().debug("Smart-ID: Getting session") + SmartIdViewModel.logger().info("Smart-ID: Getting session") return try await smartIdSignService.getSessionRequest( url: sidUrl, sessionId: sessionId, @@ -415,7 +415,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { } private func getTrustedCertificates(certificates: [Data]) -> [SecCertificate] { - SmartIdViewModel.logger().debug("Smart-ID: Getting trusted certificates list") + SmartIdViewModel.logger().info("Smart-ID: Getting trusted certificates list") return certificates.compactMap { certificateUtil.certificate(from: $0) } } @@ -438,7 +438,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { signedContainer: SignedContainerProtocol, userAgent: String ) async throws -> Data { - SmartIdViewModel.logger().debug( + SmartIdViewModel.logger().info( "Smart-ID: Preparing signature. Calculating hash" ) @@ -451,7 +451,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { } private func getVerificationCode(hash: Data) async -> String { - SmartIdViewModel.logger().debug( + SmartIdViewModel.logger().info( "Smart-ID: Calculating verification code (control code)" ) return await smartIdSignService.getVerificationCode(digest: sha256(data: hash)) @@ -483,7 +483,7 @@ class SmartIdViewModel: SmartIdViewModelProtocol, Loggable { case .explicitlyCancelled: // Do not throw an error if user cancelled signing with back button - SmartIdViewModel.logger().debug("Smart-ID signing manually cancelled") + SmartIdViewModel.logger().info("Smart-ID signing manually cancelled") smartIdMessageKey = nil case .noInternetConnection, .noResponse: diff --git a/RIADigiDoc/ViewModel/SigningViewModel.swift b/RIADigiDoc/ViewModel/SigningViewModel.swift index 5db648e8..a8a4b733 100644 --- a/RIADigiDoc/ViewModel/SigningViewModel.swift +++ b/RIADigiDoc/ViewModel/SigningViewModel.swift @@ -78,7 +78,7 @@ class SigningViewModel: SigningViewModelProtocol, Loggable { } func loadContainerData(signedContainer: SignedContainerProtocol?) async { - SigningViewModel.logger().debug("Loading container data") + SigningViewModel.logger().info("Loading container data") sharedContainerViewModel.setIsSignatureAdded(false) let openedContainer = (signedContainer ?? sharedContainerViewModel.currentContainer()) as? any SignedContainerProtocol @@ -101,7 +101,7 @@ class SigningViewModel: SigningViewModelProtocol, Loggable { self.containerNotifications = await getContainerNotifications(container: openedContainer) - SigningViewModel.logger().debug("Container data loaded") + SigningViewModel.logger().info("Container data loaded") } func getContainerNotifications(container: SignedContainerProtocol) async -> [ContainerNotificationType] { @@ -185,7 +185,7 @@ class SigningViewModel: SigningViewModelProtocol, Loggable { do { let updatedContainer = try await signedContainer?.addDataFiles(files, to: container) - SigningViewModel.logger().debug("Added data files to container") + SigningViewModel.logger().info("Added data files to container") successMessage = ToastMessage( key: files.count == 1 ? "File successfully added" : "Files successfully added", args: [] diff --git a/RIADigiDocTests/ViewModel/DiagnosticsViewModelTests.swift b/RIADigiDocTests/ViewModel/DiagnosticsViewModelTests.swift index 8b8b52b7..c2d106b4 100644 --- a/RIADigiDocTests/ViewModel/DiagnosticsViewModelTests.swift +++ b/RIADigiDocTests/ViewModel/DiagnosticsViewModelTests.swift @@ -41,6 +41,7 @@ final class DiagnosticsViewModelTests { private let mockDataStore: DataStoreProtocolMock private let mockProxyUtil: ProxyUtilProtocolMock private let mockUserAgentUtil: UserAgentUtilProtocolMock + private let mockFileUtil: FileUtilProtocolMock let mockConfigProvider: ConfigurationProvider? @@ -53,6 +54,7 @@ final class DiagnosticsViewModelTests { mockDataStore = DataStoreProtocolMock() mockProxyUtil = ProxyUtilProtocolMock() mockUserAgentUtil = UserAgentUtilProtocolMock() + mockFileUtil = FileUtilProtocolMock() mockConfigProvider = try TestConfigurationProvider.mockConfigurationProvider() TestConfigurationSetup.configureMocks( @@ -70,7 +72,8 @@ final class DiagnosticsViewModelTests { tslUtil: mockTSLUtil, dataStore: mockDataStore, proxyUtil: mockProxyUtil, - userAgentUtil: mockUserAgentUtil + userAgentUtil: mockUserAgentUtil, + fileUtil: mockFileUtil ) } @@ -257,10 +260,10 @@ final class DiagnosticsViewModelTests { #expect(rpUuid == uuid) } - // MARK: - Create Log File Tests + // MARK: - Create Diagnostics File Tests @Test - func createLogFile_success() async throws { + func createDiagnosticsFile_success() async throws { let mockLanguageSettings = LanguageSettingsProtocolMock() await viewModel.getConfigurationData(configuration: mockConfigProvider) @@ -275,7 +278,7 @@ final class DiagnosticsViewModelTests { try? FileManager.default.removeItem(at: tempDirectoryURL) } - if let logFileUrl = await viewModel.createLogFile( + if let logFileUrl = await viewModel.createDiagnosticsFile( languageSettings: mockLanguageSettings, directory: tempDirectoryURL ) { @@ -284,34 +287,24 @@ final class DiagnosticsViewModelTests { } @Test - func createLogFile_returnsNilWhenDirectoryDoesNotExist() async throws { + func createDiagnosticsFile_returnsNilWhenDirectoryDoesNotExist() async throws { mockFileManager.fileExistsHandler = { _ in false } let mockLanguageSettings = LanguageSettingsProtocolMock() await viewModel.getConfigurationData(configuration: mockConfigProvider) - let logFileUrl = await self.viewModel.createLogFile( + let logFileUrl = await self.viewModel.createDiagnosticsFile( languageSettings: mockLanguageSettings, ) #expect(logFileUrl == nil) } - // MARK: - Remove Log Files Directory Tests + // MARK: - onDiagnosticsFileSavingComplete Tests @Test - func removeLogFilesDirectory_success() async throws { - viewModel.removeLogFilesDirectory() - #expect(mockFileManager.removeItemCallCount == 1) - } - - @Test - func removeLogFilesDirectory_doesNotThrowWhenFails() async throws { - mockFileManager.removeItemHandler = { _ in - throw NSError(domain: "TestError", code: 1, userInfo: nil) - } - #expect(throws: Never.self) { - self.viewModel.removeLogFilesDirectory() - } + func onDiagnosticsFileSavingComplete_success() async throws { + viewModel.onDiagnosticsFileSavingComplete() + #expect(mockFileUtil.removeCacheLogsDirectoryCallCount == 1) } // MARK: - Update Configuration Tests diff --git a/codemagic.yaml b/codemagic.yaml index ec6f78be..05303a39 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -167,7 +167,11 @@ workflows: REPO_RIA_CONF_FLAGS="DEFAULT_CENTRAL_CONFIGURATION_URL='' DEFAULT_CENTRAL_CONFIGURATION_UPDATE_INTERVAL=4 DEFAULT_CENTRAL_CONFIGURATION_TSL_URL=''" fi - ARCHIVE_FLAGS="$REPO_RIA_CONF_FLAGS" + # Enable logging flag + LOGGING_FLAGS="OTHER_SWIFT_FLAGS=\"\$(inherited) -DENABLE_LOGGING\"" + + # Add logging flag to conf flags + ARCHIVE_FLAGS="$REPO_RIA_CONF_FLAGS $LOGGING_FLAGS" xcode-project build-ipa \ --clean \