diff --git a/SignalMessaging/attachments/MediaMessageView.swift b/SignalMessaging/attachments/MediaMessageView.swift index 0c515af70..48440b358 100644 --- a/SignalMessaging/attachments/MediaMessageView.swift +++ b/SignalMessaging/attachments/MediaMessageView.swift @@ -343,13 +343,6 @@ public class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate { } private func createUrlPreview() { - - let data = attachment.data - guard let messageText = String(data: data, encoding: String.Encoding.utf8) else { - createGenericPreview() - return - } - // Show nothing; URLs should only appear in the attachment approval view // of the SAE and in this context the URL will be placed in the caption field. } diff --git a/SignalServiceKit/src/Util/OWSFileSystem.h b/SignalServiceKit/src/Util/OWSFileSystem.h index bb7f249ac..b2db4cd45 100644 --- a/SignalServiceKit/src/Util/OWSFileSystem.h +++ b/SignalServiceKit/src/Util/OWSFileSystem.h @@ -29,6 +29,9 @@ NS_ASSUME_NONNULL_BEGIN + (NSArray *_Nullable)allFilesInDirectoryRecursive:(NSString *)dirPath error:(NSError **)error; +// Returns nil on failure. ++ (nullable NSString *)writeDataToTemporaryFile:(NSData *)data fileExtension:(NSString *_Nullable)fileExtension; + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Util/OWSFileSystem.m b/SignalServiceKit/src/Util/OWSFileSystem.m index 91520d0f3..13a0bb7f4 100644 --- a/SignalServiceKit/src/Util/OWSFileSystem.m +++ b/SignalServiceKit/src/Util/OWSFileSystem.m @@ -166,6 +166,28 @@ NS_ASSUME_NONNULL_BEGIN return filePaths; } ++ (nullable NSString *)writeDataToTemporaryFile:(NSData *)data fileExtension:(NSString *_Nullable)fileExtension +{ + OWSAssert(data); + + NSString *temporaryDirectory = NSTemporaryDirectory(); + NSString *tempFileName = NSUUID.UUID.UUIDString; + if (fileExtension.length > 0) { + tempFileName = [[tempFileName stringByAppendingString:@"."] stringByAppendingString:fileExtension]; + } + NSString *tempFilePath = [temporaryDirectory stringByAppendingPathComponent:tempFileName]; + NSError *error; + BOOL success = [data writeToFile:tempFilePath options:NSDataWritingAtomic error:&error]; + if (!success || error) { + OWSFail(@"%@ could not write to temporary file: %@", self.logTag, error); + return nil; + } + + [self protectFileOrFolderAtPath:tempFilePath]; + + return tempFilePath; +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalShareExtension/ShareViewController.swift b/SignalShareExtension/ShareViewController.swift index 8a63eeec5..f5d9d5107 100644 --- a/SignalShareExtension/ShareViewController.swift +++ b/SignalShareExtension/ShareViewController.swift @@ -494,6 +494,36 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE return matchingUtiType } + private class func createDataSource(utiType: String, url: URL, customFileName: String?) -> DataSource? { + if utiType == (kUTTypeURL as String) { + // Share URLs as oversize text messages whose text content is the URL. + // + // NOTE: SharingThreadPickerViewController will try to unpack them + // and send them as normal text messages if possible. + let urlString = url.absoluteString + return DataSourceValue.dataSource(withOversizeText:urlString) + } else if UTTypeConformsTo(utiType as CFString, kUTTypeText) { + // Share text as oversize text messages. + // + // NOTE: SharingThreadPickerViewController will try to unpack them + // and send them as normal text messages if possible. + let urlString = url.absoluteString + return DataSourceValue.dataSource(withOversizeText:urlString) + } else { + guard let dataSource = DataSourcePath.dataSource(with: url) else { + return nil + } + + if let customFileName = customFileName { + dataSource.sourceFilename = customFileName + } else { + // Ignore the filename for URLs. + dataSource.sourceFilename = url.lastPathComponent + } + return dataSource + } + } + private func buildAttachment() -> Promise { guard let inputItem: NSExtensionItem = self.extensionContext?.inputItems.first as? NSExtensionItem else { let error = ShareViewControllerError.assertionError(description: "no input item") @@ -540,24 +570,27 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE customFileName = "Contact.vcf" } - let tempDirPath = NSTemporaryDirectory() - var tempFileName = NSUUID().uuidString - if let customFileExtension = MIMETypeUtil.fileExtension(forUTIType:utiType) { - tempFileName += "." + customFileExtension + let customFileExtension = MIMETypeUtil.fileExtension(forUTIType:utiType) + guard let tempFilePath = OWSFileSystem.writeData(toTemporaryFile: data, fileExtension: customFileExtension) else { + let writeError = ShareViewControllerError.assertionError(description: "Error writing item data: \(String(describing: error))") + reject(writeError) + return } - let tempFilePath = (tempDirPath as NSString).appendingPathComponent(tempFileName) - do { - let fileUrl = URL(fileURLWithPath:tempFilePath) - try data.write(to: fileUrl) - - // Don't back up Giphy downloads. - OWSFileSystem.protectFileOrFolder(atPath:tempFilePath) - - fulfill(fileUrl) - } catch let error as NSError { + let fileUrl = URL(fileURLWithPath:tempFilePath) + fulfill(fileUrl) + } else if let string = provider as? String { + guard let data = string.data(using: String.Encoding.utf8) else { let writeError = ShareViewControllerError.assertionError(description: "Error writing item data: \(String(describing: error))") reject(writeError) + return } + guard let tempFilePath = OWSFileSystem.writeData(toTemporaryFile:data, fileExtension:"txt") else { + let writeError = ShareViewControllerError.assertionError(description: "Error writing item data: \(String(describing: error))") + reject(writeError) + return + } + let fileUrl = URL(fileURLWithPath:tempFilePath) + fulfill(fileUrl) } else if let url = provider as? URL { fulfill(url) } else { @@ -581,31 +614,16 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE Logger.debug("\(self.logTag) building DataSource with url: \(url)") - var rawDataSource: DataSource? - if utiType == (kUTTypeURL as String) { - // Share URLs as oversize text messages whose text content is the URL. - // - // NOTE: SharingThreadPickerViewController will try to unpack them - // and send them as normal text messages if possible. - let urlString = url.absoluteString - rawDataSource = DataSourceValue.dataSource(withOversizeText:urlString) - } else { - rawDataSource = DataSourcePath.dataSource(with: url) - } - guard let dataSource = rawDataSource else { + guard let dataSource = ShareViewController.createDataSource(utiType : utiType, url : url, customFileName : customFileName) else { throw ShareViewControllerError.assertionError(description: "Unable to read attachment data") } - if let customFileName = customFileName { - dataSource.sourceFilename = customFileName - } else if utiType != (kUTTypeURL as String) { - // Ignore the filename for URLs. - dataSource.sourceFilename = url.lastPathComponent - } // start with base utiType, but it might be something generic like "image" var specificUTIType = utiType if utiType == (kUTTypeURL as String) { // Use kUTTypeURL for URLs. + } else if UTTypeConformsTo(utiType as CFString, kUTTypeText) { + // Use kUTTypeText for text. } else if url.pathExtension.count > 0 { // Determine a more specific utiType based on file extension if let typeExtension = MIMETypeUtil.utiType(forFileExtension: url.pathExtension) {