Skip to content

Commit 52141e2

Browse files
committed
Fix adding data files in crypto containers
1 parent e8e55ed commit 52141e2

10 files changed

Lines changed: 170 additions & 34 deletions

File tree

Modules/CryptoLib/Sources/CryptoObjC/include/Decrypt.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ @implementation Addressee (label)
3232

3333
- (instancetype)initWithLabel:(const std::string &)label pub:(NSData*)pub concatKDFAlgorithmURI:(NSString *)concatKDFAlgorithmURI {
3434
std::map<std::string, std::string> info = libcdoc::Recipient::parseLabel(label);
35-
id cn = info.contains("cn") ? [NSString stringWithStdString:info["cn"]] : nil;
35+
id cn = info.contains("cn") ? [NSString stringWithStdString:info["cn"]] : [NSString stringWithStdString:label];
3636
id type = info.contains("type") ? [NSString stringWithStdString:info["type"]] : nil;
3737
id serial = info.contains("serial_number") ? [NSString stringWithStdString:info["serial_number"]] : nil;
3838
CertType certType = CertTypeUnknownType;

Modules/CryptoLib/Sources/CryptoSwift/CryptoContainer.swift

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,74 @@ public actor CryptoContainer: CryptoContainerProtocol, Loggable {
7575
return containerFile
7676
}
7777

78-
public func addDataFiles(_ filesToAdd: [URL]) async {
79-
dataFiles?.append(contentsOf: filesToAdd)
78+
public func addDataFiles(_ filesToAdd: [URL]) async throws {
79+
let cryptoContainersDirectory = try Directories.getCacheDirectory(
80+
subfolders: [Constants.Folder.ContainerFolder, Constants.Folder.Temp],
81+
fileManager: fileManager
82+
)
83+
84+
var movedFiles: [URL] = []
85+
var duplicateFileCount = 0
86+
var failedFileCount = 0
87+
let totalFileCount = filesToAdd.count
88+
89+
let existingDataFiles = Set(dataFiles?.compactMap { $0?.lastPathComponent } ?? [])
90+
91+
var duplicateFileName: String?
92+
93+
for fileToAdd in filesToAdd {
94+
let fileName = fileToAdd.lastPathComponent
95+
let destinationURL = cryptoContainersDirectory.appendingPathComponent(fileName)
96+
97+
if existingDataFiles.contains(fileName) ||
98+
movedFiles.contains(where: { $0.lastPathComponent == fileName }) {
99+
100+
duplicateFileCount += 1
101+
102+
if duplicateFileCount == 1 {
103+
duplicateFileName = fileName
104+
}
105+
106+
continue
107+
}
108+
109+
do {
110+
if fileManager.fileExists(atPath: destinationURL.path) {
111+
try fileManager.removeItem(at: destinationURL)
112+
}
113+
114+
try fileManager.copyItem(at: fileToAdd, to: destinationURL)
115+
movedFiles.append(destinationURL)
116+
117+
} catch {
118+
failedFileCount += 1
119+
}
120+
}
121+
122+
if dataFiles == nil {
123+
dataFiles = movedFiles
124+
} else {
125+
dataFiles?.append(contentsOf: movedFiles)
126+
}
127+
128+
if duplicateFileCount > 0 || failedFileCount > 0 {
129+
var userInfo: [String: String] = [
130+
"totalFileCount": String(totalFileCount),
131+
"failedFileCount": String(failedFileCount),
132+
"duplicateFileCount": String(duplicateFileCount)
133+
]
134+
135+
if duplicateFileCount == 1, let name = duplicateFileName {
136+
userInfo["fileName"] = name
137+
}
138+
139+
throw CryptoError.addingFilesToContainerFailed(
140+
CryptoErrorDetail(
141+
message: "Unable to add files to container",
142+
userInfo: userInfo
143+
)
144+
)
145+
}
80146
}
81147

82148
public func addRecipients(_ recipientsToAdd: [Addressee]) async {
@@ -216,7 +282,6 @@ extension CryptoContainer {
216282
CryptoContainer.logger().info("Is single file: \(isSingleFile, privacy: .public)")
217283
CryptoContainer.logger().info("Is crypto container: \(isCryptoContainer, privacy: .public)")
218284

219-
220285
guard isSingleFile && isCryptoContainer else {
221286
var defaultExtension = CommonsLib.Constants.Extension.DefaultCrypto
222287
if CDoc2Setting.isEncryptionEnabled {

Modules/CryptoLib/Sources/CryptoSwift/CryptoContainerProtocol.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public protocol CryptoContainerProtocol: GeneralContainer, Sendable {
2828
func getContainerName() async -> String
2929
func getContainerMimetype() async -> String
3030
func getRawContainerFile() async -> URL?
31-
func addDataFiles(_ filesToAdd: [URL]) async
31+
func addDataFiles(_ filesToAdd: [URL]) async throws
3232
func addRecipients(_ recipientsToAdd: [Addressee]) async
3333

3434
func getDataFiles() async -> [URL]

Modules/UtilsLib/Sources/UtilsLib/Mimetype/Parser/XMLParserHandler.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,8 @@ final class XMLParserHandler: NSObject, XMLParserDelegate {
3838
) {
3939
let formatAttribute = attributeDict["format"]
4040
let nameAttribute = attributeDict["Name"]
41-
if elementName == "SignedDoc", (
42-
formatAttribute == "DIGIDOC-XML" || formatAttribute == "SK-XML"
43-
) {
41+
if elementName == "SignedDoc",
42+
formatAttribute == "DIGIDOC-XML" || formatAttribute == "SK-XML" {
4443
foundElement = .ddoc
4544
parser.abortParsing()
4645
} else if elementName == "denc:EncryptionProperty" &&

RIADigiDoc/UI/Component/Container/Crypto/EncryptView.swift

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ struct EncryptView: View {
202202
isEditContainerButtonShown: viewModel.isEditButtonShown,
203203
isSaveButtonShown: viewModel.isContainerEncrypted || viewModel.isContainerDecrypted,
204204
isSignButtonShown: viewModel.isSignButtonShown,
205-
isEncryptButtonShown: viewModel.isEncryptButtonShown,
205+
isEncryptButtonShown: false,
206206
showLeftActionButton: false,
207207
showRightActionButton: viewModel.isEncryptButtonShown ||
208208
viewModel.isDecryptButtonShown,
@@ -324,7 +324,8 @@ struct EncryptView: View {
324324
viewModel: viewModel,
325325
showOpenFileButton: viewModel.isContainerUnlocked,
326326
showSaveFileButton: viewModel.isContainerUnlocked,
327-
showRemoveFileButton: viewModel.isContainerWithoutRecipients,
327+
showRemoveFileButton: !viewModel.isContainerEncrypted &&
328+
!viewModel.isContainerDecrypted,
328329
isNestedContainer: isNestedContainer,
329330
selectedDataFile: $selectedDataFile,
330331
showSivaMessage: $showSivaMessage,
@@ -363,24 +364,30 @@ struct EncryptView: View {
363364
)
364365
}
365366
} else if !viewModel.isContainerEncrypted && !viewModel.isContainerDecrypted {
366-
let rightButtonLabel = viewModel.isContainerWithoutRecipients ? nextLabel : encryptLabel
367-
let rightButtonIconName = viewModel.isContainerWithoutRecipients
368-
? "ic_m3_arrow_forward_48pt_wght400"
369-
: "ic_m3_encrypted_48pt_wght400"
367+
let rightButtonLabel = (
368+
!viewModel.isContainerEncrypted && !viewModel.isContainerDecrypted
369+
) ? nextLabel : encryptLabel
370+
let rightButtonIconName = (
371+
!viewModel.isContainerEncrypted && !viewModel.isContainerDecrypted
372+
)
373+
? "ic_m3_arrow_forward_48pt_wght400"
374+
: "ic_m3_encrypted_48pt_wght400"
370375
UnsignedBottomBarView(
371-
showLeftButton: viewModel.isContainerWithoutRecipients,
376+
showLeftButton: !viewModel.isContainerEncrypted && !viewModel.isContainerDecrypted,
372377
leftButtonIconName: "ic_m3_add_48pt_wght400",
373378
leftButtonLabel: addMoreFilesLabel,
374379
leftButtonAccessibilityLabel: addMoreFilesLabel.lowercased(),
375380
leftButtonAction: {
376381
isImportingAddedFiles = true
377382
},
378-
rightButtonEnabled: viewModel.isContainerWithoutRecipients || encryptionButtonEnabled,
383+
rightButtonEnabled: (
384+
!viewModel.isContainerEncrypted && !viewModel.isContainerDecrypted
385+
) || encryptionButtonEnabled,
379386
rightButtonIconName: rightButtonIconName,
380387
rightButtonLabel: rightButtonLabel,
381388
rightButtonAccessibilityLabel: rightButtonLabel.lowercased(),
382389
rightButtonAction: {
383-
if viewModel.isContainerWithoutRecipients {
390+
if !viewModel.isContainerEncrypted && !viewModel.isContainerDecrypted {
384391
pathManager.replaceLast(to: .encryptRecipientView)
385392
} else {
386393
if encryptionButtonEnabled {

RIADigiDoc/UI/Component/Container/Crypto/RecipientView.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,15 @@ struct RecipientView: View {
147147
)
148148

149149
let certType = recipientUtil.getRecipientCertTypeText(certType: recipient.certType)
150-
Text(verbatim:
151-
"\(languageSettings.localized(certType)) " +
152-
"\(languageSettings.localized("Valid to", [validToDate]))")
153-
.font(typography.bodyMedium)
154-
.foregroundStyle(theme.onSurfaceVariant)
155-
.fixedSize(horizontal: false, vertical: true)
156-
.multilineTextAlignment(.leading)
150+
let validPart = validToDate.isEmpty
151+
? ""
152+
: " " + languageSettings.localized("Valid to", [validToDate])
153+
154+
Text(verbatim: languageSettings.localized(certType) + validPart)
155+
.font(typography.bodyMedium)
156+
.foregroundStyle(theme.onSurfaceVariant)
157+
.fixedSize(horizontal: false, vertical: true)
158+
.multilineTextAlignment(.leading)
157159
}
158160
.accessibilityElement(children: .combine)
159161

RIADigiDoc/UI/Component/Container/UnsignedBottomBarView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ struct UnsignedBottomBarView: View {
8484
.disabled(!rightButtonEnabled)
8585
.foregroundStyle(theme.surfaceContainer)
8686
}
87-
.padding(.vertical,Dimensions.Padding.SPadding)
88-
.padding(.horizontal,Dimensions.Padding.MPadding)
87+
.padding(.vertical, Dimensions.Padding.SPadding)
88+
.padding(.horizontal, Dimensions.Padding.MPadding)
8989
.background(theme.surfaceContainer)
9090
}
9191
}

RIADigiDoc/UI/Component/Toast/ToastOverlay.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct ToastOverlay: View {
5555
height: Dimensions.Icon.IconSizeXXS
5656
)
5757
.foregroundStyle(style.foreground)
58+
.accessibilityHidden(true)
5859

5960
Text(verbatim: message)
6061
.lineLimit(nil)

RIADigiDoc/ViewModel/EncryptViewModel.swift

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,15 +159,77 @@ class EncryptViewModel: EncryptViewModelProtocol, Loggable {
159159
return
160160
}
161161

162-
await cryptoContainer?.addDataFiles(files)
163-
EncryptViewModel.logger().info("Added data files to container")
164-
successMessage = ToastMessage(
165-
key: files.count == 1 ? "File successfully added" : "Files successfully added",
166-
args: []
167-
)
162+
do {
163+
try await cryptoContainer?.addDataFiles(files)
164+
165+
EncryptViewModel.logger().info("Added data files to crypto container")
166+
successMessage = ToastMessage(
167+
key: files.count == 1 ? "File successfully added" : "Files successfully added",
168+
args: []
169+
)
170+
} catch {
171+
EncryptViewModel.logger().error(
172+
"Unable to add data files to container: \(String(reflecting: error))"
173+
)
174+
175+
guard let containerUrl = containerURL else {
176+
errorMessage = ToastMessage(key: "General error")
177+
return
178+
}
179+
180+
await handleAddFilesError(error)
181+
}
182+
168183
await loadContainerData(cryptoContainer: cryptoContainer)
169184
}
170185

186+
private func handleAddFilesError(_ error: Error) async {
187+
SigningViewModel.logger().error("Unable to add data files to container: \(error.localizedDescription)")
188+
189+
var totalFileCount = 0
190+
var failedFileCount = 0
191+
var duplicateFileCount = 0
192+
193+
guard let cryptoError = error as? CryptoError else {
194+
errorMessage = ToastMessage(key: "General error", args: [])
195+
return
196+
}
197+
198+
switch cryptoError {
199+
case .addingFilesToContainerFailed(let errorDetail):
200+
totalFileCount = Int(errorDetail.userInfo["totalFileCount"] ?? "0") ?? 0
201+
failedFileCount = Int(errorDetail.userInfo["failedFileCount"] ?? "0") ?? 0
202+
duplicateFileCount = Int(errorDetail.userInfo["duplicateFileCount"] ?? "0") ?? 0
203+
204+
if duplicateFileCount > 1 {
205+
errorMessage = ToastMessage(key: "Multiple documents already exist", args: [String(duplicateFileCount)])
206+
} else if duplicateFileCount == 1 {
207+
if let fileName = errorDetail.userInfo["fileName"] {
208+
errorMessage = ToastMessage(key: "Document already exists", args: [fileName])
209+
} else {
210+
errorMessage = ToastMessage(key: errorDetail.message, args: [String(failedFileCount)])
211+
}
212+
} else {
213+
errorMessage = ToastMessage(key: errorDetail.message, args: [String(failedFileCount)])
214+
}
215+
216+
default:
217+
errorMessage = ToastMessage(key: "General error", args: [])
218+
}
219+
220+
// Update container when at least one file has been added to container
221+
guard totalFileCount > failedFileCount, totalFileCount > duplicateFileCount else { return }
222+
223+
let successfulFilesCount = totalFileCount - failedFileCount - duplicateFileCount
224+
225+
successMessage = successfulFilesCount == 1 ?
226+
ToastMessage(key: "Single document added") :
227+
ToastMessage(
228+
key: "Multiple documents added",
229+
args: [String(successfulFilesCount)]
230+
)
231+
}
232+
171233
private func validateFiles(_ files: [URL]) throws {
172234
guard let firstFile = files.first else {
173235
throw FileOpeningError.noDataFiles
@@ -388,7 +450,7 @@ class EncryptViewModel: EncryptViewModelProtocol, Loggable {
388450
case .success(let fileURL):
389451
return await sivaRepository.isSivaConfirmationNeeded(files: [fileURL])
390452
case .failure:
391-
errorMessage = ToastMessage(key: "Failed to open container", args: [dataFile.lastPathComponent])
453+
errorMessage = ToastMessage(key: "Failed to open file", args: [dataFile.lastPathComponent])
392454
return false
393455
}
394456
}

RIADigiDoc/ViewModel/Protocols/EncryptViewModelProtocol.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public protocol EncryptViewModelProtocol: Sendable {
2727
func loadContainerData(cryptoContainer: CryptoContainerProtocol?) async
2828
func createCopyOfContainerForSaving(containerURL: URL?) -> URL?
2929
func removeSavedFilesDirectory(savedFilesDirectory: URL?)
30-
func addDataFiles(_ files: [URL]) async
30+
func addDataFiles(_ files: [URL]) async throws
3131
func encryptContainer() async
3232
@discardableResult func renameContainer(to newName: String) async -> URL?
3333
func getDataFileURL(_ dataFile: URL) async -> Result<URL, Error>

0 commit comments

Comments
 (0)