NCService.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. //
  2. // NCService.swift
  3. // Nextcloud
  4. //
  5. // Created by Marino Faggiana on 14/03/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 NextcloudKit
  26. import RealmSwift
  27. class NCService: NSObject {
  28. let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
  29. let utilityFileSystem = NCUtilityFileSystem()
  30. // MARK: -
  31. public func startRequestServicesServer(account: String, user: String, userId: String) {
  32. guard !appDelegate.account.isEmpty, UIApplication.shared.applicationState != .background else {
  33. NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Service not start request service server with the application in background")
  34. return
  35. }
  36. NCManageDatabase.shared.clearAllAvatarLoaded()
  37. NCPushNotification.shared.pushNotification()
  38. Task {
  39. addInternalTypeIdentifier()
  40. let result = await requestServerStatus(account: account)
  41. if result {
  42. requestServerCapabilities(account: account)
  43. getAvatar(account: account, userId: userId, user: user)
  44. NCNetworkingE2EE().unlockAll(account: account)
  45. sendClientDiagnosticsRemoteOperation(account: account)
  46. synchronize(account: account)
  47. }
  48. }
  49. }
  50. // MARK: -
  51. func addInternalTypeIdentifier() {
  52. // txt
  53. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "text/plain", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorText, iconName: NKCommon.TypeIconFile.document.rawValue, name: "markdown")
  54. // html
  55. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "text/html", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorText, iconName: NKCommon.TypeIconFile.document.rawValue, name: "markdown")
  56. // markdown
  57. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "net.daringfireball.markdown", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorText, iconName: NKCommon.TypeIconFile.document.rawValue, name: "markdown")
  58. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "text/x-markdown", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorText, iconName: NKCommon.TypeIconFile.document.rawValue, name: "markdown")
  59. // document: text
  60. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "org.oasis-open.opendocument.text", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorCollabora, iconName: NKCommon.TypeIconFile.document.rawValue, name: "document")
  61. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "org.openxmlformats.wordprocessingml.document", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorOnlyoffice, iconName: NKCommon.TypeIconFile.document.rawValue, name: "document")
  62. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "com.microsoft.word.doc", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorQuickLook, iconName: NKCommon.TypeIconFile.document.rawValue, name: "document")
  63. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "com.apple.iwork.pages.pages", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorQuickLook, iconName: NKCommon.TypeIconFile.document.rawValue, name: "pages")
  64. // document: sheet
  65. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "org.oasis-open.opendocument.spreadsheet", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorCollabora, iconName: NKCommon.TypeIconFile.xls.rawValue, name: "sheet")
  66. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "org.openxmlformats.spreadsheetml.sheet", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorOnlyoffice, iconName: NKCommon.TypeIconFile.xls.rawValue, name: "sheet")
  67. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "com.microsoft.excel.xls", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorQuickLook, iconName: NKCommon.TypeIconFile.xls.rawValue, name: "sheet")
  68. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "com.apple.iwork.numbers.numbers", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorQuickLook, iconName: NKCommon.TypeIconFile.xls.rawValue, name: "numbers")
  69. // document: presentation
  70. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "org.oasis-open.opendocument.presentation", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorCollabora, iconName: NKCommon.TypeIconFile.ppt.rawValue, name: "presentation")
  71. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "org.openxmlformats.presentationml.presentation", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorOnlyoffice, iconName: NKCommon.TypeIconFile.ppt.rawValue, name: "presentation")
  72. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "com.microsoft.powerpoint.ppt", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorQuickLook, iconName: NKCommon.TypeIconFile.ppt.rawValue, name: "presentation")
  73. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: "com.apple.iwork.keynote.key", classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorQuickLook, iconName: NKCommon.TypeIconFile.ppt.rawValue, name: "keynote")
  74. }
  75. // MARK: -
  76. private func requestServerStatus(account: String) async -> Bool {
  77. switch await NCNetworking.shared.getServerStatus(serverUrl: appDelegate.urlBase, options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) {
  78. case .success(let serverInfo):
  79. if serverInfo.maintenance {
  80. let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_maintenance_mode_")
  81. NCContentPresenter().showWarning(error: error, priority: .max)
  82. return false
  83. } else if serverInfo.productName.lowercased().contains("owncloud") {
  84. let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_warning_owncloud_")
  85. NCContentPresenter().showWarning(error: error, priority: .max)
  86. return false
  87. } else if serverInfo.versionMajor <= NCGlobal.shared.nextcloud_unsupported_version {
  88. let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_warning_unsupported_")
  89. NCContentPresenter().showWarning(error: error, priority: .max)
  90. }
  91. case .failure:
  92. return false
  93. }
  94. let resultUserProfile = await NCNetworking.shared.getUserProfile(account: account, options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue))
  95. if resultUserProfile.error == .success, let userProfile = resultUserProfile.userProfile {
  96. NCManageDatabase.shared.setAccountUserProfile(account: resultUserProfile.account, userProfile: userProfile)
  97. return true
  98. } else if resultUserProfile.error.errorCode == NCGlobal.shared.errorUnauthorized401 || resultUserProfile.error.errorCode == NCGlobal.shared.errorUnauthorized997 {
  99. // Ops the server has Unauthorized
  100. DispatchQueue.main.async {
  101. if UIApplication.shared.applicationState == .active && NCNetworking.shared.networkReachability != NKCommon.TypeReachability.notReachable {
  102. NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] The server has response with Unauthorized go checkRemoteUser \(resultUserProfile.error.errorCode)")
  103. NCNetworkingCheckRemoteUser().checkRemoteUser(account: resultUserProfile.account, error: resultUserProfile.error)
  104. }
  105. }
  106. return false
  107. } else {
  108. NCContentPresenter().showError(error: resultUserProfile.error, priority: .max)
  109. return false
  110. }
  111. }
  112. func synchronize(account: String) {
  113. NextcloudKit.shared.listingFavorites(showHiddenFiles: NCKeychain().showHiddenFiles,
  114. account: account,
  115. options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { account, files, _, error in
  116. guard error == .success else { return }
  117. NCManageDatabase.shared.convertFilesToMetadatas(files, useFirstAsMetadataFolder: false) { _, metadatas in
  118. NCManageDatabase.shared.updateMetadatasFavorite(account: account, metadatas: metadatas)
  119. }
  120. NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Synchronize Favorite")
  121. self.synchronizeOffline(account: account)
  122. }
  123. }
  124. func getAvatar(account: String, userId: String, user: String) {
  125. let fileName = appDelegate.userBaseUrl + "-" + user + ".png"
  126. let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + fileName
  127. let etag = NCManageDatabase.shared.getTableAvatar(fileName: fileName)?.etag
  128. NextcloudKit.shared.downloadAvatar(user: userId,
  129. fileNameLocalPath: fileNameLocalPath,
  130. sizeImage: NCGlobal.shared.avatarSize,
  131. avatarSizeRounded: NCGlobal.shared.avatarSizeRounded,
  132. etag: etag,
  133. account: account,
  134. options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { _, _, _, etag, error in
  135. if let etag = etag, error == .success {
  136. NCManageDatabase.shared.addAvatar(fileName: fileName, etag: etag)
  137. } else if error.errorCode == NCGlobal.shared.errorNotModified {
  138. NCManageDatabase.shared.setAvatarLoaded(fileName: fileName)
  139. }
  140. NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadAvatar, userInfo: ["error": error])
  141. }
  142. }
  143. private func requestServerCapabilities(account: String) {
  144. guard !appDelegate.account.isEmpty else { return }
  145. NextcloudKit.shared.getCapabilities(account: account, options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { account, data, error in
  146. guard error == .success, let data = data else {
  147. NCBrandColor.shared.settingThemingColor(account: account)
  148. NCImageCache.shared.createImagesBrandCache()
  149. return
  150. }
  151. data.printJson()
  152. NCManageDatabase.shared.addCapabilitiesJSon(data, account: account)
  153. NCManageDatabase.shared.setCapabilities(account: account, data: data)
  154. // Theming
  155. if NCGlobal.shared.capabilityThemingColor != NCBrandColor.shared.themingColor || NCGlobal.shared.capabilityThemingColorElement != NCBrandColor.shared.themingColorElement || NCGlobal.shared.capabilityThemingColorText != NCBrandColor.shared.themingColorText {
  156. NCBrandColor.shared.settingThemingColor(account: account)
  157. NCImageCache.shared.createImagesBrandCache()
  158. }
  159. // Text direct editor detail
  160. if NCGlobal.shared.capabilityServerVersionMajor >= NCGlobal.shared.nextcloudVersion18 {
  161. let options = NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
  162. NextcloudKit.shared.NCTextObtainEditorDetails(account: account, options: options) { account, editors, creators, _, error in
  163. if error == .success && account == self.appDelegate.account {
  164. NCManageDatabase.shared.addDirectEditing(account: account, editors: editors, creators: creators)
  165. }
  166. }
  167. }
  168. // External file Server
  169. if NCGlobal.shared.capabilityExternalSites {
  170. NextcloudKit.shared.getExternalSite(account: account, options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { account, externalSites, _, error in
  171. if error == .success && account == self.appDelegate.account {
  172. NCManageDatabase.shared.deleteExternalSites(account: account)
  173. for externalSite in externalSites {
  174. NCManageDatabase.shared.addExternalSites(externalSite, account: account)
  175. }
  176. }
  177. }
  178. } else {
  179. NCManageDatabase.shared.deleteExternalSites(account: account)
  180. }
  181. // User Status
  182. if NCGlobal.shared.capabilityUserStatusEnabled {
  183. NextcloudKit.shared.getUserStatus(account: account, options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { account, clearAt, icon, message, messageId, messageIsPredefined, status, statusIsUserDefined, userId, _, error in
  184. if error == .success && account == self.appDelegate.account && userId == self.appDelegate.userId {
  185. NCManageDatabase.shared.setAccountUserStatus(userStatusClearAt: clearAt, userStatusIcon: icon, userStatusMessage: message, userStatusMessageId: messageId, userStatusMessageIsPredefined: messageIsPredefined, userStatusStatus: status, userStatusStatusIsUserDefined: statusIsUserDefined, account: account)
  186. }
  187. }
  188. }
  189. // Added UTI for Collabora
  190. NCGlobal.shared.capabilityRichDocumentsMimetypes.forEach { mimeType in
  191. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: mimeType, classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorCollabora, iconName: NKCommon.TypeIconFile.document.rawValue, name: "document")
  192. }
  193. // Added UTI for ONLYOFFICE & Text
  194. if let directEditingCreators = NCManageDatabase.shared.getDirectEditingCreators(account: account) {
  195. for directEditing in directEditingCreators {
  196. NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: directEditing.mimetype, classFile: NKCommon.TypeClassFile.document.rawValue, editor: directEditing.editor, iconName: NKCommon.TypeIconFile.document.rawValue, name: "document")
  197. }
  198. }
  199. }
  200. }
  201. // MARK: -
  202. @objc func synchronizeOffline(account: String) {
  203. // Synchronize Directory
  204. Task {
  205. if let directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "account == %@ AND offline == true", account), sorted: "serverUrl", ascending: true) {
  206. for directory: tableDirectory in directories {
  207. await NCNetworking.shared.synchronization(account: account, serverUrl: directory.serverUrl, add: false)
  208. }
  209. }
  210. }
  211. // Synchronize Files
  212. let files = NCManageDatabase.shared.getTableLocalFiles(predicate: NSPredicate(format: "account == %@ AND offline == true", account), sorted: "fileName", ascending: true)
  213. for file: tableLocalFile in files {
  214. guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(file.ocId) else { continue }
  215. if NCNetworking.shared.isSynchronizable(ocId: metadata.ocId, fileName: metadata.fileName, etag: metadata.etag) {
  216. NCManageDatabase.shared.setMetadatasSessionInWaitDownload(metadatas: [metadata],
  217. session: NCNetworking.shared.sessionDownloadBackground,
  218. selector: NCGlobal.shared.selectorSynchronizationOffline)
  219. }
  220. }
  221. }
  222. // MARK: -
  223. func sendClientDiagnosticsRemoteOperation(account: String) {
  224. guard NCGlobal.shared.capabilitySecurityGuardDiagnostics,
  225. NCManageDatabase.shared.existsDiagnostics(account: account) else { return }
  226. struct Issues: Codable {
  227. struct SyncConflicts: Codable {
  228. var count: Int?
  229. var oldest: TimeInterval?
  230. }
  231. struct VirusDetected: Codable {
  232. var count: Int?
  233. var oldest: TimeInterval?
  234. }
  235. struct E2EError: Codable {
  236. var count: Int?
  237. var oldest: TimeInterval?
  238. }
  239. struct Problem: Codable {
  240. struct Error: Codable {
  241. var count: Int
  242. var oldest: TimeInterval
  243. }
  244. var forbidden: Error? // NCGlobal.shared.diagnosticProblemsForbidden
  245. var badResponse: Error? // NCGlobal.shared.diagnosticProblemsBadResponse
  246. var uploadServerError: Error? // NCGlobal.shared.diagnosticProblemsUploadServerError
  247. }
  248. var syncConflicts: SyncConflicts
  249. var virusDetected: VirusDetected
  250. var e2eeErrors: E2EError
  251. var problems: Problem?
  252. enum CodingKeys: String, CodingKey {
  253. case syncConflicts = "sync_conflicts"
  254. case virusDetected = "virus_detected"
  255. case e2eeErrors = "e2ee_errors"
  256. case problems
  257. }
  258. }
  259. var ids: [ObjectId] = []
  260. var syncConflicts: Issues.SyncConflicts = Issues.SyncConflicts()
  261. var virusDetected: Issues.VirusDetected = Issues.VirusDetected()
  262. var e2eeErrors: Issues.E2EError = Issues.E2EError()
  263. var problems: Issues.Problem? = Issues.Problem()
  264. var problemForbidden: Issues.Problem.Error?
  265. var problemBadResponse: Issues.Problem.Error?
  266. var problemUploadServerError: Issues.Problem.Error?
  267. if let result = NCManageDatabase.shared.getDiagnostics(account: account, issue: NCGlobal.shared.diagnosticIssueSyncConflicts)?.first {
  268. syncConflicts = Issues.SyncConflicts(count: result.counter, oldest: result.oldest)
  269. ids.append(result.id)
  270. }
  271. if let result = NCManageDatabase.shared.getDiagnostics(account: account, issue: NCGlobal.shared.diagnosticIssueVirusDetected)?.first {
  272. virusDetected = Issues.VirusDetected(count: result.counter, oldest: result.oldest)
  273. ids.append(result.id)
  274. }
  275. if let result = NCManageDatabase.shared.getDiagnostics(account: account, issue: NCGlobal.shared.diagnosticIssueE2eeErrors)?.first {
  276. e2eeErrors = Issues.E2EError(count: result.counter, oldest: result.oldest)
  277. ids.append(result.id)
  278. }
  279. if let results = NCManageDatabase.shared.getDiagnostics(account: account, issue: NCGlobal.shared.diagnosticIssueProblems) {
  280. for result in results {
  281. switch result.error {
  282. case NCGlobal.shared.diagnosticProblemsForbidden:
  283. if result.counter >= 1 {
  284. problemForbidden = Issues.Problem.Error(count: result.counter, oldest: result.oldest)
  285. ids.append(result.id)
  286. }
  287. case NCGlobal.shared.diagnosticProblemsBadResponse:
  288. if result.counter >= 2 {
  289. problemBadResponse = Issues.Problem.Error(count: result.counter, oldest: result.oldest)
  290. ids.append(result.id)
  291. }
  292. case NCGlobal.shared.diagnosticProblemsUploadServerError:
  293. if result.counter >= 1 {
  294. problemUploadServerError = Issues.Problem.Error(count: result.counter, oldest: result.oldest)
  295. ids.append(result.id)
  296. }
  297. default:
  298. break
  299. }
  300. }
  301. problems = Issues.Problem(forbidden: problemForbidden, badResponse: problemBadResponse, uploadServerError: problemUploadServerError)
  302. }
  303. do {
  304. let issues = Issues(syncConflicts: syncConflicts, virusDetected: virusDetected, e2eeErrors: e2eeErrors, problems: problems)
  305. let data = try JSONEncoder().encode(issues)
  306. data.printJson()
  307. NextcloudKit.shared.sendClientDiagnosticsRemoteOperation(data: data, account: account, options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { _, error in
  308. if error == .success {
  309. NCManageDatabase.shared.deleteDiagnostics(account: account, ids: ids)
  310. }
  311. }
  312. } catch {
  313. print("Error: \(error.localizedDescription)")
  314. }
  315. }
  316. }