Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions Privitty.framework/Headers/Privitty.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,77 @@ FOUNDATION_EXPORT const unsigned char PrivittyVersionString[];

- (nullable NSDictionary*)getConfigWithKey:(NSString*)key;

// =============================================================================
// FORWARD ACCESS CONTROL (Three-party Trust Model)
// =============================================================================

/**
* Forward peer add request - Add a forwardee to forward file access
* @param chatId Chat identifier with the relay/owner
* @param forwardeeChatId Chat identifier with the forwardee
* @param prvFile Path to the .prv file to forward
* @return Result dictionary with PDU (nullable on error)
*/
- (nullable NSDictionary*)processInitForwardPeerAddRequestWithChatId:(NSString*)chatId
forwardeeChatId:(NSString*)forwardeeChatId
prvFile:(NSString*)prvFile;

/**
* Forwardee initiates forward access request - Request access to a forwarded file
* @param chatId Chat identifier with the relay
* @param filePath Path to the .prv file
* @return Result dictionary with PDU (nullable on error)
*/
- (nullable NSDictionary*)processInitForwardAccessRequestWithChatId:(NSString*)chatId
filePath:(NSString*)filePath;

/**
* Original owner accepts relay forward access request
* @param chatId Chat identifier with the relay
* @param filePath Path to the .prv file
* @param contactId ID of the forwarder/contact requesting access
* @param accessDuration Duration in seconds for access validity (0 for unlimited)
* @param allowDownload Whether to allow download of the file
* @return Result dictionary with PDU (nullable on error)
*/
- (nullable NSDictionary*)processInitRevertRelayForwardAccessAcceptWithChatId:(NSString*)chatId
filePath:(NSString*)filePath
contactId:(NSString*)contactId
accessDuration:(NSInteger)accessDuration
allowDownload:(BOOL)allowDownload;

/**
* Original owner denies relay forward access request
* @param chatId Chat identifier with the relay
* @param filePath Path to the .prv file
* @param contactId ID of the forwarder/contact requesting access
* @param denialReason Optional reason for denial (can be nil)
* @return Result dictionary with PDU (nullable on error)
*/
- (nullable NSDictionary*)processInitRevertRelayForwardAccessDeniedWithChatId:(NSString*)chatId
filePath:(NSString*)filePath
contactId:(NSString*)contactId
denialReason:(nullable NSString*)denialReason;

/**
* Decrypt a forwarded file (file that was forwarded from another peer)
* @param fileId File identifier (actually the prv_file path)
* @param forwarderPeer Peer who forwarded the file (actually the chat_id)
* @return Result dictionary with decryption result (nullable on error)
*/
- (nullable NSDictionary*)processForwardedFileDecryptRequestWithFileId:(NSString*)fileId
forwarderPeer:(NSString*)forwarderPeer;

/**
* Get detailed file access status list with owner, shared, and forwarded information
* Returns comprehensive view of the file's access control chain in three-party trust model
* @param chatId Chat identifier
* @param filePath Path to the .prv file
* @return Result dictionary with owner_info, shared_info, and forwarded_list (nullable on error)
*/
- (nullable NSDictionary*)getFileAccessStatusListWithChatId:(NSString*)chatId
filePath:(NSString*)filePath;

// =============================================================================
// UNIFIED MESSAGE PROCESSING (PRIMARY METHOD)
// =============================================================================
Expand Down
2 changes: 1 addition & 1 deletion Privitty.framework/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.0.3</string>
<string>0.0.4</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>MinimumOSVersion</key>
Expand Down
Binary file modified Privitty.framework/Privitty
Binary file not shown.
Binary file modified Privitty.framework/_CodeSignature/CodeDirectory
Binary file not shown.
Binary file modified Privitty.framework/_CodeSignature/CodeRequirements-1
Binary file not shown.
8 changes: 4 additions & 4 deletions Privitty.framework/_CodeSignature/CodeResources
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
<dict>
<key>Headers/Privitty.h</key>
<data>
XOZiDTCe+NAWBhmcV7UHd7cQLlA=
7XJwNRZwtCUxomO8O7IL7fBgT74=
</data>
<key>Info.plist</key>
<data>
gVvxK6BoiqDGhhpTsPceMUMZ6Fg=
g3lk23mWzYzi7ThM/ASFGdIBWjw=
</data>
<key>Modules/module.modulemap</key>
<data>
Expand All @@ -23,11 +23,11 @@
<dict>
<key>hash</key>
<data>
XOZiDTCe+NAWBhmcV7UHd7cQLlA=
7XJwNRZwtCUxomO8O7IL7fBgT74=
</data>
<key>hash2</key>
<data>
lOUboneq2t71vqTNSqqXPH0sq0AwaZ1Nj9DQ6cW0Di8=
Ctj7MZKa+vxsHYPazciSO4F29ZdWOQJWl4BdECBZvS0=
</data>
</dict>
<key>Modules/module.modulemap</key>
Expand Down
Binary file modified Privitty.framework/_CodeSignature/CodeSignature
Binary file not shown.
4 changes: 2 additions & 2 deletions deltachat-ios.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
0D481EC62ED3EEF700CFB244 /* poppins_semibold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0D481EC32ED3EEF700CFB244 /* poppins_semibold.ttf */; };
0D481EC72ED3EEF700CFB244 /* poppins_regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0D481EC22ED3EEF700CFB244 /* poppins_regular.ttf */; };
0D481EC92ED3F1EB00CFB244 /* AppFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D481EC82ED3F1EB00CFB244 /* AppFont.swift */; };
0D6B54552EEB3B5B006BFEF5 /* Privitty.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F437DF72E94F68900297EED /* Privitty.framework */; };
0DACEEFC2EE47A7400043D27 /* ContentDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DACEEFB2EE47A7400043D27 /* ContentDetailsViewController.swift */; };
0DACEF672EE4A7C600043D27 /* ContentDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DACEF662EE4A7C600043D27 /* ContentDetailsViewController.swift */; };
21D54500299415B9008B54D5 /* Character+Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D544FF299415B9008B54D5 /* Character+Extentions.swift */; };
21D6C941260623F500D0755A /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D6C9392606190600D0755A /* NotificationManager.swift */; };
2F11E3E62E9FABD900CA4BB4 /* PrvContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F11E3E52E9FABD900CA4BB4 /* PrvContext.swift */; };
2F2A09F22EC24FCC00A37097 /* Privitty.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F437DF72E94F68900297EED /* Privitty.framework */; };
2F2A09F32EC24FCC00A37097 /* Privitty.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 2F437DF72E94F68900297EED /* Privitty.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
3008CB7224F93EB900E6A617 /* AudioMessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3008CB7124F93EB900E6A617 /* AudioMessageCell.swift */; };
3008CB7424F9436C00E6A617 /* AudioPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3008CB7324F9436C00E6A617 /* AudioPlayerView.swift */; };
Expand Down Expand Up @@ -733,7 +733,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2F2A09F22EC24FCC00A37097 /* Privitty.framework in Frameworks */,
0D6B54552EEB3B5B006BFEF5 /* Privitty.framework in Frameworks */,
8A19190635E24C1DF7F0A35D /* Pods_deltachat_ios.framework in Frameworks */,
D89EC91A2D4BDEB800352298 /* PhotosUI.framework in Frameworks */,
);
Expand Down
113 changes: 78 additions & 35 deletions deltachat-ios/Chat/ChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1464,7 +1464,7 @@
}

private func sendPrivittyHandshake() {
let chat = dcContext.getChat(chatId: chatId)

Check warning on line 1467 in deltachat-ios/Chat/ChatViewController.swift

View workflow job for this annotation

GitHub Actions / build

initialization of immutable value 'chat' was never used; consider replacing with assignment to '_' or removing it

logger.info("Initiating Privitty handshake for chat: \(chatId)")

Expand Down Expand Up @@ -1873,27 +1873,27 @@
}

private func stageVCard(url: URL) {
draft.setAttachment(viewType: DC_MSG_VCARD, path: url.relativePath)
draft.setAttachment(viewType: DC_MSG_VCARD, path: url.path)
configureDraftArea(draft: draft)
focusInputTextView()
FileHelper.deleteFileAsync(atPath: url.relativePath)
// Note: File cleanup handled by Delta Chat core
}

private func stageDocument(url: NSURL) {
DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.draft.setAttachment(viewType: url.pathExtension == "xdc" ? DC_MSG_WEBXDC : DC_MSG_FILE, path: url.relativePath)
self.draft.setAttachment(viewType: url.pathExtension == "xdc" ? DC_MSG_WEBXDC : DC_MSG_FILE, path: url.path)
self.configureDraftArea(draft: self.draft)
self.focusInputTextView()
FileHelper.deleteFileAsync(atPath: url.relativePath)
// Note: File cleanup handled by Delta Chat core
}
}

private func stageVideo(url: NSURL) {
self.draft.setAttachment(viewType: DC_MSG_VIDEO, path: url.relativePath)
self.draft.setAttachment(viewType: DC_MSG_VIDEO, path: url.path)
self.configureDraftArea(draft: self.draft)
self.focusInputTextView()
FileHelper.deleteFileAsync(atPath: url.relativePath)
// Note: File cleanup handled by Delta Chat core
}

private func stageImage(url: NSURL) {
Expand All @@ -1920,7 +1920,7 @@
}
self.configureDraftArea(draft: self.draft)
self.focusInputTextView()
FileHelper.deleteFileAsync(atPath: pathInCachesDir)
// Note: File cleanup will be handled by Delta Chat core after message is sent
}
}
}
Expand All @@ -1931,16 +1931,16 @@
guard let self else { return }
if let path = ImageFormat.saveImage(image: image, directory: .cachesDirectory) {
self.sendAttachmentMessage(viewType: DC_MSG_IMAGE, filePath: path, message: message)
FileHelper.deleteFileAsync(atPath: path)
// Note: File cleanup handled by Delta Chat core after message is sent
}
}
}

private func sendVideo(url: URL, message: String? = nil) {
DispatchQueue.global().async { [weak self] in
guard let self else { return }
self.sendAttachmentMessage(viewType: DC_MSG_VIDEO, filePath: url.relativePath, message: message)
FileHelper.deleteFileAsync(atPath: url.relativePath)
self.sendAttachmentMessage(viewType: DC_MSG_VIDEO, filePath: url.path, message: message)
// Note: File cleanup handled by Delta Chat core after message is sent
}
}

Expand All @@ -1957,7 +1957,7 @@
}
self.sendAttachmentMessage(viewType: DC_MSG_STICKER, filePath: path, message: nil, quoteMessage: self.draft.quoteMessage)

FileHelper.deleteFileAsync(atPath: path)
// Note: File cleanup handled by Delta Chat core
self.draft.clear()
DispatchQueue.main.async {
self.draftArea.quotePreview.cancel()
Expand Down Expand Up @@ -2081,7 +2081,7 @@
if let quoteMessage = self.draft.quoteMessage {
msg.quoteMessage = quoteMessage
}
msg.setFile(filepath: url.relativePath, mimeType: "audio/m4a")
msg.setFile(filepath: url.path, mimeType: "audio/m4a")
self.dcContext.sendMessage(chatId: self.chatId, message: msg)
DispatchQueue.main.async {
self.draft.setQuote(quotedMsg: nil)
Expand Down Expand Up @@ -2375,17 +2375,18 @@
)
}

if !dcChat.isSelfTalk && message.canSave {
if message.savedMessageId != 0 {
children.append(
UIAction.menuAction(localizationKey: "unsave", systemImageName: "bookmark.slash.fill", with: messageId, action: toggleSave)
)
} else {
children.append(
UIAction.menuAction(localizationKey: "save_desktop", systemImageName: "bookmark", with: messageId, action: toggleSave)
)
}
}
// MARK: - Save menu hidden for Privitty
// if !dcChat.isSelfTalk && message.canSave {
// if message.savedMessageId != 0 {
// children.append(
// UIAction.menuAction(localizationKey: "unsave", systemImageName: "bookmark.slash.fill", with: messageId, action: toggleSave)
// )
// } else {
// children.append(
// UIAction.menuAction(localizationKey: "save_desktop", systemImageName: "bookmark", with: messageId, action: toggleSave)
// )
// }
// }

if let link = isLinkTapped(indexPath: indexPath, point: point) {
children.append(
Expand Down Expand Up @@ -2534,6 +2535,7 @@
!indexPaths.isEmpty {
editingBar.isEnabled = true
evaluateMoreButton()
evaluateForwardButton()
} else {
editingBar.isEnabled = false
}
Expand Down Expand Up @@ -2570,7 +2572,26 @@
}

private func evaluateMoreButton() {
editingBar.moreButton.isEnabled = canSaveOrUnsaveMultiple().0 || canResend()
// Only enable more button for resend (save functionality is hidden)
editingBar.moreButton.isEnabled = canResend()
}

private func hasFileInSelection() -> Bool {
guard let rows = tableView.indexPathsForSelectedRows else { return false }
let msgIds = rows.compactMap { messageIds[$0.row] }
for msgId in msgIds {
let msg = dcContext.getMessage(id: msgId)
if msg.file != nil {
return true
}
}
return false
}

private func evaluateForwardButton() {
// Hide forward button if any selected message contains a file
editingBar.forwardButton.isHidden = hasFileInSelection()
editingBar.forwardButton.isEnabled = !hasFileInSelection()
}

func setEditing(isEditing: Bool, selectedAtIndexPath: IndexPath? = nil) {
Expand All @@ -2580,6 +2601,13 @@
if let indexPath = selectedAtIndexPath {
_ = handleSelection(indexPath: indexPath)
}

// Reset forward button visibility when exiting edit mode
if !isEditing {
editingBar.forwardButton.isHidden = false
editingBar.forwardButton.isEnabled = true
}

self.updateTitle()
if refreshMessagesAfterEditing && isEditing == false {
refreshMessages()
Expand Down Expand Up @@ -3174,9 +3202,23 @@
if let typeIdentifier = itemProvider.registeredTypeIdentifiers.first {
itemProvider.loadFileRepresentation(forTypeIdentifier: typeIdentifier) { [weak self] url, error in
if let url {
var copyURL = URL.temporaryDirectory.appendingPathComponent(url.lastPathComponent)
copyURL = FileHelper.copyIfPossible(src: url, dest: copyURL)
self?.stageDocument(url: copyURL as NSURL)
// Check if this is an iOS temporary photo file (.pvt)
if url.pathExtension.lowercased() == "pvt" || url.path.contains("/tmp/") && url.lastPathComponent.hasPrefix("IMG_") {
// Load as image instead of treating as file
if let image = ImageFormat.loadImageFrom(url: url) {
self?.stageImage(image)
} else {
// Failed to load as image, fall back to file handling
var copyURL = URL.temporaryDirectory.appendingPathComponent(url.lastPathComponent)
copyURL = FileHelper.copyIfPossible(src: url, dest: copyURL)
self?.stageDocument(url: copyURL as NSURL)
}
} else {
// Regular file, copy and stage normally
var copyURL = URL.temporaryDirectory.appendingPathComponent(url.lastPathComponent)
copyURL = FileHelper.copyIfPossible(src: url, dest: copyURL)
self?.stageDocument(url: copyURL as NSURL)
}
} else if let error {
self?.logAndAlert(error: error.localizedDescription)
}
Expand Down Expand Up @@ -3323,12 +3365,13 @@
self?.onResendActionPressed()
})
}
let (canSaveOrUnsave, doSave) = canSaveOrUnsaveMultiple()
if canSaveOrUnsave {
actions.append(UIAction(title: String.localized(doSave ? "save_desktop" : "unsave"), image: UIImage(systemName: doSave ? "bookmark" : "bookmark.slash.fill")) { [weak self] _ in
self?.onMultipleSaveOrUnsave(doSave: doSave)
})
}
// MARK: - Save menu hidden for Privitty
// let (canSaveOrUnsave, doSave) = canSaveOrUnsaveMultiple()
// if canSaveOrUnsave {
// actions.append(UIAction(title: String.localized(doSave ? "save_desktop" : "unsave"), image: UIImage(systemName: doSave ? "bookmark" : "bookmark.slash.fill")) { [weak self] _ in
// self?.onMultipleSaveOrUnsave(doSave: doSave)
// })
// }
return UIMenu(children: actions)
}

Expand Down Expand Up @@ -3559,9 +3602,9 @@

extension ChatViewController: AppPickerViewControllerDelegate {
func pickedApp(_ viewController: AppPickerViewController, fileURL url: URL) {
draft.setAttachment(viewType: DC_MSG_WEBXDC, path: url.relativePath)
draft.setAttachment(viewType: DC_MSG_WEBXDC, path: url.path)
configureDraftArea(draft: draft)
focusInputTextView()
FileHelper.deleteFileAsync(atPath: url.relativePath)
// Note: File cleanup handled by Delta Chat core
}
}
2 changes: 1 addition & 1 deletion deltachat-ios/Chat/DraftModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class DraftModel {
return draftMsg?.quoteText
}
var attachment: String? {
return draftMsg?.fileURL?.relativePath
return draftMsg?.fileURL?.path
}
var attachmentMimeType: String? {
return draftMsg?.filemime
Expand Down
Loading
Loading