NCUtility.swift 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  1. //
  2. // NCUtility.swift
  3. // Nextcloud
  4. //
  5. // Created by Marino Faggiana on 25/06/18.
  6. // Copyright © 2018 Marino Faggiana. All rights reserved.
  7. //
  8. // Author Marino Faggiana <marino.faggiana@nextcloud.com>
  9. //
  10. // This program is free software: you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published by
  12. // the Free Software Foundation, either version 3 of the License, or
  13. // (at your option) any later version.
  14. //
  15. // This program is distributed in the hope that it will be useful,
  16. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. // GNU General Public License for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License
  21. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. //
  23. import UIKit
  24. import SVGKit
  25. import KTVHTTPCache
  26. import NextcloudKit
  27. import PDFKit
  28. import Accelerate
  29. import CoreMedia
  30. import Queuer
  31. import Photos
  32. import JGProgressHUD
  33. class NCUtility: NSObject {
  34. @objc static let shared: NCUtility = {
  35. let instance = NCUtility()
  36. return instance
  37. }()
  38. func setLayoutForView(key: String, serverUrl: String, layoutForView: NCGlobal.layoutForViewType) {
  39. let string = layoutForView.layout + "|" + layoutForView.sort + "|" + "\(layoutForView.ascending)" + "|" + layoutForView.groupBy + "|" + "\(layoutForView.directoryOnTop)" + "|" + layoutForView.titleButtonHeader + "|" + "\(layoutForView.itemForLine)"
  40. var keyStore = key
  41. if serverUrl != "" {
  42. keyStore = serverUrl
  43. }
  44. UICKeyChainStore.setString(string, forKey: keyStore, service: NCGlobal.shared.serviceShareKeyChain)
  45. }
  46. func setLayoutForView(key: String, serverUrl: String, layout: String?) {
  47. var layoutForView: NCGlobal.layoutForViewType = NCUtility.shared.getLayoutForView(key: key, serverUrl: serverUrl)
  48. if let layout = layout {
  49. layoutForView.layout = layout
  50. setLayoutForView(key: key, serverUrl: serverUrl, layoutForView: layoutForView)
  51. }
  52. }
  53. func getLayoutForView(key: String, serverUrl: String, sort: String = "fileName", ascending: Bool = true, titleButtonHeader: String = "_sorted_by_name_a_z_") -> (NCGlobal.layoutForViewType) {
  54. var keyStore = key
  55. var layoutForView: NCGlobal.layoutForViewType = NCGlobal.layoutForViewType(layout: NCGlobal.shared.layoutList, sort: sort, ascending: ascending, groupBy: "none", directoryOnTop: true, titleButtonHeader: titleButtonHeader, itemForLine: 3)
  56. if serverUrl != "" {
  57. keyStore = serverUrl
  58. }
  59. guard let string = UICKeyChainStore.string(forKey: keyStore, service: NCGlobal.shared.serviceShareKeyChain) else {
  60. setLayoutForView(key: key, serverUrl: serverUrl, layoutForView: layoutForView)
  61. return layoutForView
  62. }
  63. let array = string.components(separatedBy: "|")
  64. if array.count >= 7 {
  65. // version 1
  66. layoutForView.layout = array[0]
  67. layoutForView.sort = array[1]
  68. layoutForView.ascending = NSString(string: array[2]).boolValue
  69. layoutForView.groupBy = array[3]
  70. layoutForView.directoryOnTop = NSString(string: array[4]).boolValue
  71. layoutForView.titleButtonHeader = array[5]
  72. layoutForView.itemForLine = Int(NSString(string: array[6]).intValue)
  73. }
  74. return layoutForView
  75. }
  76. func convertSVGtoPNGWriteToUserData(svgUrlString: String, fileName: String?, width: CGFloat?, rewrite: Bool, account: String, closure: @escaping (String?) -> Void) {
  77. var fileNamePNG = ""
  78. guard let svgUrlString = svgUrlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
  79. let iconURL = URL(string: svgUrlString) else {
  80. return closure(nil)
  81. }
  82. if let fileName = fileName {
  83. fileNamePNG = fileName
  84. } else {
  85. fileNamePNG = iconURL.deletingPathExtension().lastPathComponent + ".png"
  86. }
  87. let imageNamePath = CCUtility.getDirectoryUserData() + "/" + fileNamePNG
  88. if !FileManager.default.fileExists(atPath: imageNamePath) || rewrite == true {
  89. NextcloudKit.shared.downloadContent(serverUrl: iconURL.absoluteString) { _, data, error in
  90. if error == .success && data != nil {
  91. if let image = UIImage(data: data!) {
  92. var newImage: UIImage = image
  93. if width != nil {
  94. let ratio = image.size.height / image.size.width
  95. let newSize = CGSize(width: width!, height: width! * ratio)
  96. let renderFormat = UIGraphicsImageRendererFormat.default()
  97. renderFormat.opaque = false
  98. let renderer = UIGraphicsImageRenderer(size: CGSize(width: newSize.width, height: newSize.height), format: renderFormat)
  99. newImage = renderer.image {
  100. _ in
  101. image.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
  102. }
  103. }
  104. guard let pngImageData = newImage.pngData() else {
  105. return closure(nil)
  106. }
  107. try? pngImageData.write(to: URL(fileURLWithPath: imageNamePath))
  108. return closure(imageNamePath)
  109. } else {
  110. guard let svgImage: SVGKImage = SVGKImage(data: data) else {
  111. return closure(nil)
  112. }
  113. if width != nil {
  114. let scale = svgImage.size.height / svgImage.size.width
  115. svgImage.size = CGSize(width: width!, height: width! * scale)
  116. }
  117. guard let image: UIImage = svgImage.uiImage else {
  118. return closure(nil)
  119. }
  120. guard let pngImageData = image.pngData() else {
  121. return closure(nil)
  122. }
  123. try? pngImageData.write(to: URL(fileURLWithPath: imageNamePath))
  124. return closure(imageNamePath)
  125. }
  126. } else {
  127. return closure(nil)
  128. }
  129. }
  130. } else {
  131. return closure(imageNamePath)
  132. }
  133. }
  134. @objc func isSimulatorOrTestFlight() -> Bool {
  135. guard let path = Bundle.main.appStoreReceiptURL?.path else {
  136. return false
  137. }
  138. return path.contains("CoreSimulator") || path.contains("sandboxReceipt")
  139. }
  140. @objc func isSimulator() -> Bool {
  141. guard let path = Bundle.main.appStoreReceiptURL?.path else {
  142. return false
  143. }
  144. return path.contains("CoreSimulator")
  145. }
  146. @objc func isRichDocument(_ metadata: tableMetadata) -> Bool {
  147. guard let mimeType = CCUtility.getMimeType(metadata.fileNameView) else {
  148. return false
  149. }
  150. guard let richdocumentsMimetypes = NCManageDatabase.shared.getCapabilitiesServerArray(account: metadata.account, elements: NCElementsJSON.shared.capabilitiesRichdocumentsMimetypes) else {
  151. return false
  152. }
  153. // contentype
  154. for richdocumentMimetype: String in richdocumentsMimetypes {
  155. if richdocumentMimetype.contains(metadata.contentType) || metadata.contentType == "text/plain" {
  156. return true
  157. }
  158. }
  159. // mimetype
  160. if richdocumentsMimetypes.count > 0 && mimeType.components(separatedBy: ".").count > 2 {
  161. let mimeTypeArray = mimeType.components(separatedBy: ".")
  162. let mimeType = mimeTypeArray[mimeTypeArray.count - 2] + "." + mimeTypeArray[mimeTypeArray.count - 1]
  163. for richdocumentMimetype: String in richdocumentsMimetypes {
  164. if richdocumentMimetype.contains(mimeType) {
  165. return true
  166. }
  167. }
  168. }
  169. return false
  170. }
  171. @objc func isDirectEditing(account: String, contentType: String) -> [String] {
  172. var editor: [String] = []
  173. guard let results = NCManageDatabase.shared.getDirectEditingEditors(account: account) else {
  174. return editor
  175. }
  176. for result: tableDirectEditingEditors in results {
  177. for mimetype in result.mimetypes {
  178. if mimetype == contentType {
  179. editor.append(result.editor)
  180. }
  181. // HARDCODE
  182. // https://github.com/nextcloud/text/issues/913
  183. if mimetype == "text/markdown" && contentType == "text/x-markdown" {
  184. editor.append(result.editor)
  185. }
  186. if contentType == "text/html" {
  187. editor.append(result.editor)
  188. }
  189. }
  190. for mimetype in result.optionalMimetypes {
  191. if mimetype == contentType {
  192. editor.append(result.editor)
  193. }
  194. }
  195. }
  196. // HARDCODE
  197. // if editor.count == 0 {
  198. // editor.append(NCGlobal.shared.editorText)
  199. // }
  200. return Array(Set(editor))
  201. }
  202. @objc func removeAllSettings() {
  203. URLCache.shared.memoryCapacity = 0
  204. URLCache.shared.diskCapacity = 0
  205. KTVHTTPCache.cacheDeleteAllCaches()
  206. NCManageDatabase.shared.clearDatabase(account: nil, removeAccount: true)
  207. CCUtility.removeGroupDirectoryProviderStorage()
  208. CCUtility.removeGroupLibraryDirectory()
  209. CCUtility.removeDocumentsDirectory()
  210. CCUtility.removeTemporaryDirectory()
  211. CCUtility.createDirectoryStandard()
  212. CCUtility.deleteAllChainStore()
  213. }
  214. @objc func permissionsContainsString(_ metadataPermissions: String, permissions: String) -> Bool {
  215. for char in permissions {
  216. if metadataPermissions.contains(char) == false {
  217. return false
  218. }
  219. }
  220. return true
  221. }
  222. @objc func getCustomUserAgentNCText() -> String {
  223. let userAgent: String = CCUtility.getUserAgent()
  224. if UIDevice.current.userInterfaceIdiom == .phone {
  225. // NOTE: Hardcoded (May 2022)
  226. // Tested for iPhone SE (1st), iOS 12; iPhone Pro Max, iOS 15.4
  227. // 605.1.15 = WebKit build version
  228. // 15E148 = frozen iOS build number according to: https://chromestatus.com/feature/4558585463832576
  229. return userAgent + " " + "AppleWebKit/605.1.15 Mobile/15E148"
  230. } else {
  231. return userAgent
  232. }
  233. }
  234. @objc func getCustomUserAgentOnlyOffice() -> String {
  235. let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString")!
  236. if UIDevice.current.userInterfaceIdiom == .pad {
  237. return "Mozilla/5.0 (iPad) Nextcloud-iOS/\(appVersion)"
  238. } else {
  239. return "Mozilla/5.0 (iPhone) Mobile Nextcloud-iOS/\(appVersion)"
  240. }
  241. }
  242. @objc func pdfThumbnail(url: URL, width: CGFloat = 240) -> UIImage? {
  243. guard let data = try? Data(contentsOf: url), let page = PDFDocument(data: data)?.page(at: 0) else {
  244. return nil
  245. }
  246. let pageSize = page.bounds(for: .mediaBox)
  247. let pdfScale = width / pageSize.width
  248. // Apply if you're displaying the thumbnail on screen
  249. let scale = UIScreen.main.scale * pdfScale
  250. let screenSize = CGSize(width: pageSize.width * scale, height: pageSize.height * scale)
  251. return page.thumbnail(of: screenSize, for: .mediaBox)
  252. }
  253. @objc func isQuickLookDisplayable(metadata: tableMetadata) -> Bool {
  254. return true
  255. }
  256. @objc func ocIdToFileId(ocId: String?) -> String? {
  257. guard let ocId = ocId else { return nil }
  258. let items = ocId.components(separatedBy: "oc")
  259. if items.count < 2 { return nil }
  260. guard let intFileId = Int(items[0]) else { return nil }
  261. return String(intFileId)
  262. }
  263. func getUserStatus(userIcon: String?, userStatus: String?, userMessage: String?) -> (onlineStatus: UIImage?, statusMessage: String, descriptionMessage: String) {
  264. var onlineStatus: UIImage?
  265. var statusMessage: String = ""
  266. var descriptionMessage: String = ""
  267. var messageUserDefined: String = ""
  268. if userStatus?.lowercased() == "online" {
  269. onlineStatus = UIImage(named: "circle_fill")!.image(color: UIColor(red: 103.0/255.0, green: 176.0/255.0, blue: 134.0/255.0, alpha: 1.0), size: 50)
  270. messageUserDefined = NSLocalizedString("_online_", comment: "")
  271. }
  272. if userStatus?.lowercased() == "away" {
  273. onlineStatus = UIImage(named: "userStatusAway")!.image(color: UIColor(red: 233.0/255.0, green: 166.0/255.0, blue: 75.0/255.0, alpha: 1.0), size: 50)
  274. messageUserDefined = NSLocalizedString("_away_", comment: "")
  275. }
  276. if userStatus?.lowercased() == "dnd" {
  277. onlineStatus = UIImage(named: "userStatusDnd")?.resizeImage(size: CGSize(width: 100, height: 100), isAspectRation: false)
  278. messageUserDefined = NSLocalizedString("_dnd_", comment: "")
  279. descriptionMessage = NSLocalizedString("_dnd_description_", comment: "")
  280. }
  281. if userStatus?.lowercased() == "offline" || userStatus?.lowercased() == "invisible" {
  282. onlineStatus = UIImage(named: "userStatusOffline")!.image(color: .black, size: 50)
  283. messageUserDefined = NSLocalizedString("_invisible_", comment: "")
  284. descriptionMessage = NSLocalizedString("_invisible_description_", comment: "")
  285. }
  286. if let userIcon = userIcon {
  287. statusMessage = userIcon + " "
  288. }
  289. if let userMessage = userMessage {
  290. statusMessage += userMessage
  291. }
  292. statusMessage = statusMessage.trimmingCharacters(in: .whitespaces)
  293. if statusMessage == "" {
  294. statusMessage = messageUserDefined
  295. }
  296. return(onlineStatus, statusMessage, descriptionMessage)
  297. }
  298. // MARK: -
  299. func extractFiles(from metadata: tableMetadata, viewController: UIViewController?, hud: JGProgressHUD, completition: @escaping (_ metadatas: [tableMetadata]) -> Void) {
  300. let chunckSize = CCUtility.getChunkSize() * 1000000
  301. var metadatas: [tableMetadata] = []
  302. let metadataSource = tableMetadata.init(value: metadata)
  303. guard !metadata.isExtractFile else { return completition([metadataSource]) }
  304. guard !metadataSource.assetLocalIdentifier.isEmpty else {
  305. let filePath = CCUtility.getDirectoryProviderStorageOcId(metadataSource.ocId, fileNameView: metadataSource.fileName)!
  306. metadataSource.size = NCUtilityFileSystem.shared.getFileSize(filePath: filePath)
  307. let results = NKCommon.shared.getInternalType(fileName: metadataSource.fileNameView, mimeType: metadataSource.contentType, directory: false)
  308. metadataSource.contentType = results.mimeType
  309. metadataSource.iconName = results.iconName
  310. metadataSource.classFile = results.classFile
  311. if let date = NCUtilityFileSystem.shared.getFileCreationDate(filePath: filePath) {
  312. metadataSource.creationDate = date
  313. }
  314. if let date = NCUtilityFileSystem.shared.getFileModificationDate(filePath: filePath) {
  315. metadataSource.date = date
  316. }
  317. metadataSource.chunk = chunckSize != 0 && metadata.size > chunckSize
  318. metadataSource.e2eEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
  319. metadataSource.isExtractFile = true
  320. if let metadata = NCManageDatabase.shared.addMetadata(metadataSource) {
  321. metadatas.append(metadata)
  322. }
  323. return completition(metadatas)
  324. }
  325. extractImageVideoFromAssetLocalIdentifier(metadata: metadataSource, modifyMetadataForUpload: true, viewController: viewController, hud: hud) { metadata, fileNamePath, returnError in
  326. if let metadata = metadata, let fileNamePath = fileNamePath, !returnError {
  327. metadatas.append(metadata)
  328. let toPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
  329. NCUtilityFileSystem.shared.moveFile(atPath: fileNamePath, toPath: toPath)
  330. } else {
  331. return completition(metadatas)
  332. }
  333. let fetchAssets = PHAsset.fetchAssets(withLocalIdentifiers: [metadataSource.assetLocalIdentifier], options: nil)
  334. if metadataSource.livePhoto, fetchAssets.count > 0 {
  335. NCUtility.shared.createMetadataLivePhotoFromMetadata(metadataSource, asset: fetchAssets.firstObject) { metadata in
  336. if let metadata = metadata, let metadata = NCManageDatabase.shared.addMetadata(metadata) {
  337. metadatas.append(metadata)
  338. }
  339. completition(metadatas)
  340. }
  341. } else {
  342. completition(metadatas)
  343. }
  344. }
  345. }
  346. func extractImageVideoFromAssetLocalIdentifier(metadata: tableMetadata, modifyMetadataForUpload: Bool, viewController: UIViewController?, hud: JGProgressHUD, completion: @escaping (_ metadata: tableMetadata?, _ fileNamePath: String?, _ error: Bool) -> ()) {
  347. var fileNamePath: String?
  348. let metadata = tableMetadata.init(value: metadata)
  349. let chunckSize = CCUtility.getChunkSize() * 1000000
  350. var compatibilityFormat: Bool = false
  351. func callCompletionWithError(_ error: Bool = true) {
  352. if error {
  353. completion(nil, nil, true)
  354. } else {
  355. var metadataReturn = metadata
  356. if modifyMetadataForUpload {
  357. metadata.chunk = chunckSize != 0 && metadata.size > chunckSize
  358. metadata.e2eEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
  359. metadata.isExtractFile = true
  360. if let metadata = NCManageDatabase.shared.addMetadata(metadata) {
  361. metadataReturn = metadata
  362. }
  363. }
  364. completion(metadataReturn, fileNamePath, error)
  365. }
  366. }
  367. let fetchAssets = PHAsset.fetchAssets(withLocalIdentifiers: [metadata.assetLocalIdentifier], options: nil)
  368. guard fetchAssets.count > 0, let asset = fetchAssets.firstObject, let extensionAsset = (asset.value(forKey: "filename") as? NSString)?.pathExtension.uppercased() else { return callCompletionWithError() }
  369. let creationDate = asset.creationDate ?? Date()
  370. let modificationDate = asset.modificationDate ?? Date()
  371. if asset.mediaType == PHAssetMediaType.image && (extensionAsset == "HEIC" || extensionAsset == "DNG") && CCUtility.getFormatCompatibility() {
  372. let fileName = (metadata.fileNameView as NSString).deletingPathExtension + ".jpg"
  373. metadata.contentType = "image/jpeg"
  374. metadata.ext = "jpg"
  375. fileNamePath = NSTemporaryDirectory() + fileName
  376. metadata.fileNameView = fileName
  377. if !metadata.e2eEncrypted {
  378. metadata.fileName = fileName
  379. }
  380. compatibilityFormat = true
  381. } else {
  382. fileNamePath = NSTemporaryDirectory() + metadata.fileNameView
  383. }
  384. guard let fileNamePath = fileNamePath else { return callCompletionWithError() }
  385. if asset.mediaType == PHAssetMediaType.image {
  386. let options = PHImageRequestOptions()
  387. options.isNetworkAccessAllowed = true
  388. if compatibilityFormat {
  389. options.deliveryMode = .opportunistic
  390. } else {
  391. options.deliveryMode = .highQualityFormat
  392. }
  393. options.isSynchronous = true
  394. if extensionAsset == "DNG" {
  395. options.version = PHImageRequestOptionsVersion.original
  396. }
  397. options.progressHandler = { (progress, error, stop, info) in
  398. print(progress)
  399. if error != nil { return callCompletionWithError() }
  400. }
  401. PHImageManager.default().requestImageDataAndOrientation(for: asset, options: options) { data, dataUI, orientation, info in
  402. guard var data = data else { return callCompletionWithError() }
  403. if compatibilityFormat {
  404. guard let ciImage = CIImage.init(data: data), let colorSpace = ciImage.colorSpace, let dataJPEG = CIContext().jpegRepresentation(of: ciImage, colorSpace: colorSpace) else { return callCompletionWithError() }
  405. data = dataJPEG
  406. }
  407. NCUtilityFileSystem.shared.deleteFile(filePath: fileNamePath)
  408. do {
  409. try data.write(to: URL(fileURLWithPath: fileNamePath), options: .atomic)
  410. } catch { return callCompletionWithError() }
  411. metadata.creationDate = creationDate as NSDate
  412. metadata.date = modificationDate as NSDate
  413. metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath)
  414. return callCompletionWithError(false)
  415. }
  416. } else if asset.mediaType == PHAssetMediaType.video {
  417. let options = PHVideoRequestOptions()
  418. options.isNetworkAccessAllowed = true
  419. options.version = PHVideoRequestOptionsVersion.current
  420. options.progressHandler = { (progress, error, stop, info) in
  421. print(progress)
  422. if error != nil { return callCompletionWithError() }
  423. }
  424. PHImageManager.default().requestAVAsset(forVideo: asset, options: options) { asset, audioMix, info in
  425. if let asset = asset as? AVURLAsset {
  426. NCUtilityFileSystem.shared.deleteFile(filePath: fileNamePath)
  427. do {
  428. try FileManager.default.copyItem(at: asset.url, to: URL(fileURLWithPath: fileNamePath))
  429. metadata.creationDate = creationDate as NSDate
  430. metadata.date = modificationDate as NSDate
  431. metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath)
  432. return callCompletionWithError(false)
  433. } catch { return callCompletionWithError() }
  434. } else if let asset = asset as? AVComposition, asset.tracks.count > 1, let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality), let viewController = viewController {
  435. DispatchQueue.main.async {
  436. hud.indicatorView = JGProgressHUDRingIndicatorView()
  437. if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView {
  438. indicatorView.ringWidth = 1.5
  439. }
  440. hud.textLabel.text = NSLocalizedString("_exporting_video_", comment: "")
  441. hud.show(in: viewController.view)
  442. hud.tapOnHUDViewBlock = { hud in
  443. exporter.cancelExport()
  444. }
  445. }
  446. exporter.outputURL = URL(fileURLWithPath: fileNamePath)
  447. exporter.outputFileType = AVFileType.mp4
  448. exporter.shouldOptimizeForNetworkUse = true
  449. exporter.exportAsynchronously {
  450. DispatchQueue.main.async { hud.dismiss() }
  451. if exporter.status == .completed {
  452. metadata.creationDate = creationDate as NSDate
  453. metadata.date = modificationDate as NSDate
  454. metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath)
  455. return callCompletionWithError(false)
  456. } else { return callCompletionWithError() }
  457. }
  458. while exporter.status == AVAssetExportSession.Status.exporting || exporter.status == AVAssetExportSession.Status.waiting {
  459. hud.progress = exporter.progress
  460. }
  461. } else {
  462. return callCompletionWithError()
  463. }
  464. }
  465. } else {
  466. return callCompletionWithError()
  467. }
  468. }
  469. func createMetadataLivePhotoFromMetadata(_ metadata: tableMetadata, asset: PHAsset?, completion: @escaping (_ metadata: tableMetadata?) -> ()) {
  470. guard let asset = asset else { return completion(nil) }
  471. let options = PHLivePhotoRequestOptions()
  472. options.deliveryMode = PHImageRequestOptionsDeliveryMode.fastFormat
  473. options.isNetworkAccessAllowed = true
  474. let chunckSize = CCUtility.getChunkSize() * 1000000
  475. let e2eEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
  476. let ocId = NSUUID().uuidString
  477. let fileName = (metadata.fileName as NSString).deletingPathExtension + ".mov"
  478. let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileName)!
  479. PHImageManager.default().requestLivePhoto(for: asset, targetSize: UIScreen.main.bounds.size, contentMode: PHImageContentMode.default, options: options) { livePhoto, info in
  480. guard let livePhoto = livePhoto else { return completion(nil) }
  481. var videoResource: PHAssetResource?
  482. for resource in PHAssetResource.assetResources(for: livePhoto) {
  483. if resource.type == PHAssetResourceType.pairedVideo {
  484. videoResource = resource
  485. break
  486. }
  487. }
  488. guard let videoResource = videoResource else { return completion(nil) }
  489. NCUtilityFileSystem.shared.deleteFile(filePath: fileNamePath)
  490. PHAssetResourceManager.default().writeData(for: videoResource, toFile: URL(fileURLWithPath: fileNamePath), options: nil) { error in
  491. if error != nil { return completion(nil) }
  492. let metadataLivePhoto = NCManageDatabase.shared.createMetadata(account: metadata.account, user: metadata.user, userId: metadata.userId, fileName: fileName, fileNameView: fileName, ocId: ocId, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, url: "", contentType: "", isLivePhoto: true)
  493. metadataLivePhoto.classFile = NKCommon.typeClassFile.video.rawValue
  494. metadataLivePhoto.e2eEncrypted = e2eEncrypted
  495. metadataLivePhoto.isExtractFile = true
  496. metadataLivePhoto.session = metadata.session
  497. metadataLivePhoto.sessionSelector = metadata.sessionSelector
  498. metadataLivePhoto.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath)
  499. metadataLivePhoto.status = metadata.status
  500. metadataLivePhoto.chunk = chunckSize != 0 && metadata.size > chunckSize
  501. return completion(NCManageDatabase.shared.addMetadata(metadataLivePhoto))
  502. }
  503. }
  504. }
  505. func imageFromVideo(url: URL, at time: TimeInterval) -> UIImage? {
  506. let asset = AVURLAsset(url: url)
  507. let assetIG = AVAssetImageGenerator(asset: asset)
  508. assetIG.appliesPreferredTrackTransform = true
  509. assetIG.apertureMode = AVAssetImageGenerator.ApertureMode.encodedPixels
  510. let cmTime = CMTime(seconds: time, preferredTimescale: 60)
  511. let thumbnailImageRef: CGImage
  512. do {
  513. thumbnailImageRef = try assetIG.copyCGImage(at: cmTime, actualTime: nil)
  514. } catch let error {
  515. print("Error: \(error)")
  516. return nil
  517. }
  518. return UIImage(cgImage: thumbnailImageRef)
  519. }
  520. func imageFromVideo(url: URL, at time: TimeInterval, completion: @escaping (UIImage?) -> Void) {
  521. DispatchQueue.global().async {
  522. let asset = AVURLAsset(url: url)
  523. let assetIG = AVAssetImageGenerator(asset: asset)
  524. assetIG.appliesPreferredTrackTransform = true
  525. assetIG.apertureMode = AVAssetImageGenerator.ApertureMode.encodedPixels
  526. let cmTime = CMTime(seconds: time, preferredTimescale: 60)
  527. let thumbnailImageRef: CGImage
  528. do {
  529. thumbnailImageRef = try assetIG.copyCGImage(at: cmTime, actualTime: nil)
  530. } catch let error {
  531. print("Error: \(error)")
  532. return completion(nil)
  533. }
  534. DispatchQueue.main.async {
  535. completion(UIImage(cgImage: thumbnailImageRef))
  536. }
  537. }
  538. }
  539. func createImageFrom(fileNameView: String, ocId: String, etag: String, classFile: String) {
  540. var originalImage, scaleImagePreview, scaleImageIcon: UIImage?
  541. let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileNameView)!
  542. let fileNamePathPreview = CCUtility.getDirectoryProviderStoragePreviewOcId(ocId, etag: etag)!
  543. let fileNamePathIcon = CCUtility.getDirectoryProviderStorageIconOcId(ocId, etag: etag)!
  544. if CCUtility.fileProviderStorageSize(ocId, fileNameView: fileNameView) > 0 && FileManager().fileExists(atPath: fileNamePathPreview) && FileManager().fileExists(atPath: fileNamePathIcon) { return }
  545. if classFile != NKCommon.typeClassFile.image.rawValue && classFile != NKCommon.typeClassFile.video.rawValue { return }
  546. if classFile == NKCommon.typeClassFile.image.rawValue {
  547. originalImage = UIImage(contentsOfFile: fileNamePath)
  548. scaleImagePreview = originalImage?.resizeImage(size: CGSize(width: NCGlobal.shared.sizePreview, height: NCGlobal.shared.sizePreview))
  549. scaleImageIcon = originalImage?.resizeImage(size: CGSize(width: NCGlobal.shared.sizeIcon, height: NCGlobal.shared.sizeIcon))
  550. try? scaleImagePreview?.jpegData(compressionQuality: 0.7)?.write(to: URL(fileURLWithPath: fileNamePathPreview))
  551. try? scaleImageIcon?.jpegData(compressionQuality: 0.7)?.write(to: URL(fileURLWithPath: fileNamePathIcon))
  552. } else if classFile == NKCommon.typeClassFile.video.rawValue {
  553. let videoPath = NSTemporaryDirectory()+"tempvideo.mp4"
  554. NCUtilityFileSystem.shared.linkItem(atPath: fileNamePath, toPath: videoPath)
  555. originalImage = imageFromVideo(url: URL(fileURLWithPath: videoPath), at: 0)
  556. try? originalImage?.jpegData(compressionQuality: 0.7)?.write(to: URL(fileURLWithPath: fileNamePathPreview))
  557. try? originalImage?.jpegData(compressionQuality: 0.7)?.write(to: URL(fileURLWithPath: fileNamePathIcon))
  558. }
  559. }
  560. @objc func getVersionApp(withBuild: Bool = true) -> String {
  561. if let dictionary = Bundle.main.infoDictionary {
  562. if let version = dictionary["CFBundleShortVersionString"], let build = dictionary["CFBundleVersion"] {
  563. if withBuild {
  564. return "\(version).\(build)"
  565. } else {
  566. return "\(version)"
  567. }
  568. }
  569. }
  570. return ""
  571. }
  572. func loadImage(named imageName: String, color: UIColor = NCBrandColor.shared.gray, size: CGFloat = 50, symbolConfiguration: Any? = nil) -> UIImage {
  573. var image: UIImage?
  574. // see https://stackoverflow.com/questions/71764255
  575. let sfSymbolName = imageName.replacingOccurrences(of: "_", with: ".")
  576. if let symbolConfiguration = symbolConfiguration {
  577. image = UIImage(systemName: sfSymbolName, withConfiguration: symbolConfiguration as? UIImage.Configuration)?.withTintColor(color, renderingMode: .alwaysOriginal)
  578. } else {
  579. image = UIImage(systemName: sfSymbolName)?.withTintColor(color, renderingMode: .alwaysOriginal)
  580. }
  581. if image == nil {
  582. image = UIImage(named: imageName)?.image(color: color, size: size)
  583. }
  584. if let image = image {
  585. return image
  586. }
  587. return UIImage(named: "file")!.image(color: color, size: size)
  588. }
  589. @objc func loadUserImage(for user: String, displayName: String?, userBaseUrl: NCUserBaseUrl) -> UIImage {
  590. let fileName = userBaseUrl.userBaseUrl + "-" + user + ".png"
  591. let localFilePath = String(CCUtility.getDirectoryUserData()) + "/" + fileName
  592. if let localImage = UIImage(contentsOfFile: localFilePath) {
  593. return createAvatar(image: localImage, size: 30)
  594. } else if let loadedAvatar = NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName) {
  595. return loadedAvatar
  596. } else if let displayName = displayName, !displayName.isEmpty, let avatarImg = createAvatar(displayName: displayName, size: 30) {
  597. return avatarImg
  598. } else { return getDefaultUserIcon() }
  599. }
  600. func getDefaultUserIcon() -> UIImage {
  601. let config = UIImage.SymbolConfiguration(pointSize: 30)
  602. return NCUtility.shared.loadImage(named: "person.crop.circle", symbolConfiguration: config)
  603. }
  604. @objc func createAvatar(image: UIImage, size: CGFloat) -> UIImage {
  605. var avatarImage = image
  606. let rect = CGRect(x: 0, y: 0, width: size, height: size)
  607. UIGraphicsBeginImageContextWithOptions(rect.size, false, 3.0)
  608. UIBezierPath(roundedRect: rect, cornerRadius: rect.size.height).addClip()
  609. avatarImage.draw(in: rect)
  610. avatarImage = UIGraphicsGetImageFromCurrentImageContext() ?? image
  611. UIGraphicsEndImageContext()
  612. return avatarImage
  613. }
  614. func createAvatar(displayName: String, size: CGFloat) -> UIImage? {
  615. guard let initials = displayName.uppercaseInitials else {
  616. return nil
  617. }
  618. let userColor = NCGlobal.shared.usernameToColor(displayName)
  619. let rect = CGRect(x: 0, y: 0, width: size, height: size)
  620. var avatarImage: UIImage?
  621. UIGraphicsBeginImageContextWithOptions(rect.size, false, 3.0)
  622. let context = UIGraphicsGetCurrentContext()
  623. UIBezierPath(roundedRect: rect, cornerRadius: rect.size.height).addClip()
  624. context?.setFillColor(userColor)
  625. context?.fill(rect)
  626. let textStyle = NSMutableParagraphStyle()
  627. textStyle.alignment = NSTextAlignment.center
  628. let lineHeight = UIFont.systemFont(ofSize: UIFont.systemFontSize).pointSize
  629. NSString(string: initials)
  630. .draw(
  631. in: CGRect(x: 0, y: (size - lineHeight) / 2, width: size, height: lineHeight),
  632. withAttributes: [NSAttributedString.Key.paragraphStyle: textStyle])
  633. avatarImage = UIGraphicsGetImageFromCurrentImageContext()
  634. UIGraphicsEndImageContext()
  635. return avatarImage
  636. }
  637. /*
  638. Facebook's comparison algorithm:
  639. */
  640. func compare(tolerance: Float, expected: Data, observed: Data) throws -> Bool {
  641. enum customError: Error {
  642. case unableToGetUIImageFromData
  643. case unableToGetCGImageFromData
  644. case unableToGetColorSpaceFromCGImage
  645. case imagesHasDifferentSizes
  646. case unableToInitializeContext
  647. }
  648. guard let expectedUIImage = UIImage(data: expected), let observedUIImage = UIImage(data: observed) else {
  649. throw customError.unableToGetUIImageFromData
  650. }
  651. guard let expectedCGImage = expectedUIImage.cgImage, let observedCGImage = observedUIImage.cgImage else {
  652. throw customError.unableToGetCGImageFromData
  653. }
  654. guard let expectedColorSpace = expectedCGImage.colorSpace, let observedColorSpace = observedCGImage.colorSpace else {
  655. throw customError.unableToGetColorSpaceFromCGImage
  656. }
  657. if expectedCGImage.width != observedCGImage.width || expectedCGImage.height != observedCGImage.height {
  658. throw customError.imagesHasDifferentSizes
  659. }
  660. let imageSize = CGSize(width: expectedCGImage.width, height: expectedCGImage.height)
  661. let numberOfPixels = Int(imageSize.width * imageSize.height)
  662. // Checking that our `UInt32` buffer has same number of bytes as image has.
  663. let bytesPerRow = min(expectedCGImage.bytesPerRow, observedCGImage.bytesPerRow)
  664. assert(MemoryLayout<UInt32>.stride == bytesPerRow / Int(imageSize.width))
  665. let expectedPixels = UnsafeMutablePointer<UInt32>.allocate(capacity: numberOfPixels)
  666. let observedPixels = UnsafeMutablePointer<UInt32>.allocate(capacity: numberOfPixels)
  667. let expectedPixelsRaw = UnsafeMutableRawPointer(expectedPixels)
  668. let observedPixelsRaw = UnsafeMutableRawPointer(observedPixels)
  669. let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
  670. guard let expectedContext = CGContext(data: expectedPixelsRaw, width: Int(imageSize.width), height: Int(imageSize.height),
  671. bitsPerComponent: expectedCGImage.bitsPerComponent, bytesPerRow: bytesPerRow,
  672. space: expectedColorSpace, bitmapInfo: bitmapInfo.rawValue) else {
  673. expectedPixels.deallocate()
  674. observedPixels.deallocate()
  675. throw customError.unableToInitializeContext
  676. }
  677. guard let observedContext = CGContext(data: observedPixelsRaw, width: Int(imageSize.width), height: Int(imageSize.height),
  678. bitsPerComponent: observedCGImage.bitsPerComponent, bytesPerRow: bytesPerRow,
  679. space: observedColorSpace, bitmapInfo: bitmapInfo.rawValue) else {
  680. expectedPixels.deallocate()
  681. observedPixels.deallocate()
  682. throw customError.unableToInitializeContext
  683. }
  684. expectedContext.draw(expectedCGImage, in: CGRect(origin: .zero, size: imageSize))
  685. observedContext.draw(observedCGImage, in: CGRect(origin: .zero, size: imageSize))
  686. let expectedBuffer = UnsafeBufferPointer(start: expectedPixels, count: numberOfPixels)
  687. let observedBuffer = UnsafeBufferPointer(start: observedPixels, count: numberOfPixels)
  688. var isEqual = true
  689. if tolerance == 0 {
  690. isEqual = expectedBuffer.elementsEqual(observedBuffer)
  691. } else {
  692. // Go through each pixel in turn and see if it is different
  693. var numDiffPixels = 0
  694. for pixel in 0 ..< numberOfPixels where expectedBuffer[pixel] != observedBuffer[pixel] {
  695. // If this pixel is different, increment the pixel diff count and see if we have hit our limit.
  696. numDiffPixels += 1
  697. let percentage = 100 * Float(numDiffPixels) / Float(numberOfPixels)
  698. if percentage > tolerance {
  699. isEqual = false
  700. break
  701. }
  702. }
  703. }
  704. expectedPixels.deallocate()
  705. observedPixels.deallocate()
  706. return isEqual
  707. }
  708. func stringFromTime(_ time: CMTime) -> String {
  709. let interval = Int(CMTimeGetSeconds(time))
  710. let seconds = interval % 60
  711. let minutes = (interval / 60) % 60
  712. let hours = (interval / 3600)
  713. if hours > 0 {
  714. return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
  715. } else {
  716. return String(format: "%02d:%02d", minutes, seconds)
  717. }
  718. }
  719. func colorNavigationController(_ navigationController: UINavigationController?, backgroundColor: UIColor, titleColor: UIColor, tintColor: UIColor?, withoutShadow: Bool) {
  720. let appearance = UINavigationBarAppearance()
  721. appearance.titleTextAttributes = [.foregroundColor: titleColor]
  722. appearance.largeTitleTextAttributes = [.foregroundColor: titleColor]
  723. if withoutShadow {
  724. appearance.shadowColor = .clear
  725. appearance.shadowImage = UIImage()
  726. }
  727. if let tintColor = tintColor {
  728. navigationController?.navigationBar.tintColor = tintColor
  729. }
  730. navigationController?.view.backgroundColor = backgroundColor
  731. navigationController?.navigationBar.barTintColor = titleColor
  732. navigationController?.navigationBar.standardAppearance = appearance
  733. navigationController?.navigationBar.compactAppearance = appearance
  734. navigationController?.navigationBar.scrollEdgeAppearance = appearance
  735. }
  736. func getEncondingDataType(data: Data) -> String.Encoding? {
  737. if let _ = String(data: data, encoding: .utf8) {
  738. return .utf8
  739. }
  740. if let _ = String(data: data, encoding: .ascii) {
  741. return .ascii
  742. }
  743. if let _ = String(data: data, encoding: .isoLatin1) {
  744. return .isoLatin1
  745. }
  746. if let _ = String(data: data, encoding: .isoLatin2) {
  747. return .isoLatin2
  748. }
  749. if let _ = String(data: data, encoding: .windowsCP1250) {
  750. return .windowsCP1250
  751. }
  752. if let _ = String(data: data, encoding: .windowsCP1251) {
  753. return .windowsCP1251
  754. }
  755. if let _ = String(data: data, encoding: .windowsCP1252) {
  756. return .windowsCP1252
  757. }
  758. if let _ = String(data: data, encoding: .windowsCP1253) {
  759. return .windowsCP1253
  760. }
  761. if let _ = String(data: data, encoding: .windowsCP1254) {
  762. return .windowsCP1254
  763. }
  764. if let _ = String(data: data, encoding: .macOSRoman) {
  765. return .macOSRoman
  766. }
  767. if let _ = String(data: data, encoding: .japaneseEUC) {
  768. return .japaneseEUC
  769. }
  770. if let _ = String(data: data, encoding: .nextstep) {
  771. return .nextstep
  772. }
  773. if let _ = String(data: data, encoding: .nonLossyASCII) {
  774. return .nonLossyASCII
  775. }
  776. if let _ = String(data: data, encoding: .shiftJIS) {
  777. return .shiftJIS
  778. }
  779. if let _ = String(data: data, encoding: .symbol) {
  780. return .symbol
  781. }
  782. if let _ = String(data: data, encoding: .unicode) {
  783. return .unicode
  784. }
  785. if let _ = String(data: data, encoding: .utf16) {
  786. return .utf16
  787. }
  788. if let _ = String(data: data, encoding: .utf16BigEndian) {
  789. return .utf16BigEndian
  790. }
  791. if let _ = String(data: data, encoding: .utf16LittleEndian) {
  792. return .utf16LittleEndian
  793. }
  794. if let _ = String(data: data, encoding: .utf32) {
  795. return .utf32
  796. }
  797. if let _ = String(data: data, encoding: .utf32BigEndian) {
  798. return .utf32BigEndian
  799. }
  800. if let _ = String(data: data, encoding: .utf32LittleEndian) {
  801. return .utf32LittleEndian
  802. }
  803. return nil
  804. }
  805. func SYSTEM_VERSION_LESS_THAN(version: String) -> Bool {
  806. return UIDevice.current.systemVersion.compare(version,
  807. options: NSString.CompareOptions.numeric) == ComparisonResult.orderedAscending
  808. }
  809. func getAvatarFromIconUrl(metadata: tableMetadata) -> String? {
  810. var ownerId: String?
  811. if metadata.iconUrl.contains("http") && metadata.iconUrl.contains("avatar") {
  812. let splitIconUrl = metadata.iconUrl.components(separatedBy: "/")
  813. var found:Bool = false
  814. for item in splitIconUrl {
  815. if found {
  816. ownerId = item
  817. break
  818. }
  819. if item == "avatar" { found = true}
  820. }
  821. }
  822. return ownerId
  823. }
  824. // https://stackoverflow.com/questions/25471114/how-to-validate-an-e-mail-address-in-swift
  825. func isValidEmail(_ email: String) -> Bool {
  826. let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
  827. let emailPred = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
  828. return emailPred.evaluate(with: email)
  829. }
  830. func createFilePreviewImage(ocId: String, etag: String, fileNameView: String, classFile: String, status: Int, createPreviewMedia: Bool) -> UIImage? {
  831. var imagePreview: UIImage?
  832. let filePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileNameView)!
  833. let iconImagePath = CCUtility.getDirectoryProviderStorageIconOcId(ocId, etag: etag)!
  834. if FileManager().fileExists(atPath: iconImagePath) {
  835. imagePreview = UIImage(contentsOfFile: iconImagePath)
  836. } else if !createPreviewMedia {
  837. return nil
  838. } else if createPreviewMedia && status >= NCGlobal.shared.metadataStatusNormal && classFile == NKCommon.typeClassFile.image.rawValue && FileManager().fileExists(atPath: filePath) {
  839. if let image = UIImage(contentsOfFile: filePath), let image = image.resizeImage(size: CGSize(width: NCGlobal.shared.sizeIcon, height: NCGlobal.shared.sizeIcon)), let data = image.jpegData(compressionQuality: 0.5) {
  840. do {
  841. try data.write(to: URL.init(fileURLWithPath: iconImagePath), options: .atomic)
  842. imagePreview = image
  843. } catch { }
  844. }
  845. } else if createPreviewMedia && status >= NCGlobal.shared.metadataStatusNormal && classFile == NKCommon.typeClassFile.video.rawValue && FileManager().fileExists(atPath: filePath) {
  846. if let image = NCUtility.shared.imageFromVideo(url: URL(fileURLWithPath: filePath), at: 0), let image = image.resizeImage(size: CGSize(width: NCGlobal.shared.sizeIcon, height: NCGlobal.shared.sizeIcon)), let data = image.jpegData(compressionQuality: 0.5) {
  847. do {
  848. try data.write(to: URL.init(fileURLWithPath: iconImagePath), options: .atomic)
  849. imagePreview = image
  850. } catch { }
  851. }
  852. }
  853. return imagePreview
  854. }
  855. @discardableResult
  856. func convertDataToImage(data: Data?, size:CGSize, fileNameToWrite: String?) -> UIImage? {
  857. guard let data = data else { return nil }
  858. var returnImage: UIImage?
  859. if let image = UIImage(data: data), let image = image.resizeImage(size: size) {
  860. returnImage = image
  861. } else if let image = SVGKImage(data: data) {
  862. image.size = size
  863. returnImage = image.uiImage
  864. } else {
  865. print("error")
  866. }
  867. if let fileName = fileNameToWrite, let image = returnImage {
  868. do {
  869. let fileNamePath: String = CCUtility.getDirectoryUserData() + "/" + fileName + ".png"
  870. try image.pngData()?.write(to: URL(fileURLWithPath: fileNamePath), options: .atomic)
  871. } catch { }
  872. }
  873. return returnImage
  874. }
  875. func isFolderEncrypted(serverUrl: String, e2eEncrypted: Bool = false, userBase: NCUserBaseUrl) -> Bool {
  876. return isFolderEncrypted(serverUrl: serverUrl, e2eEncrypted: e2eEncrypted, account: userBase.account, urlBase: userBase.urlBase, userId: userBase.userId)
  877. }
  878. func isFolderEncrypted(metadata: tableMetadata) -> Bool {
  879. return isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
  880. }
  881. @objc func isFolderEncrypted(serverUrl: String, e2eEncrypted: Bool = false, account:String, urlBase: String, userId: String) -> Bool {
  882. if e2eEncrypted { return true }
  883. let home = NCUtilityFileSystem.shared.getHomeServer(urlBase: urlBase, userId: userId)
  884. if serverUrl == home || serverUrl == ".." { return false }
  885. var serverUrl = serverUrl
  886. var tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl))
  887. while let directory = tableDirectory, directory.serverUrl != home {
  888. if directory.e2eEncrypted { return true }
  889. if let path = NCUtilityFileSystem.shared.deleteLastPath(serverUrlPath: serverUrl, home: home) {
  890. serverUrl = path
  891. }
  892. tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl))
  893. }
  894. return false
  895. }
  896. }