AccountRemovalWork.kt 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Tobias Kaminsky
  5. * @author Chris Narkiewicz
  6. *
  7. * Copyright (C) 2017 Tobias Kaminsky
  8. * Copyright (C) 2017 Nextcloud GmbH.
  9. * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
  10. *
  11. * This program is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU Affero General Public License as published by
  13. * the Free Software Foundation, either version 3 of the License, or
  14. * at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU Affero General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Affero General Public License
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. */
  24. package com.nextcloud.client.jobs
  25. import android.accounts.Account
  26. import android.content.Context
  27. import android.os.Build
  28. import android.provider.DocumentsContract
  29. import android.text.TextUtils
  30. import androidx.work.Worker
  31. import androidx.work.WorkerParameters
  32. import com.google.gson.Gson
  33. import com.nextcloud.client.account.User
  34. import com.nextcloud.client.account.UserAccountManager
  35. import com.nextcloud.client.core.Clock
  36. import com.nextcloud.client.preferences.AppPreferencesImpl
  37. import com.nextcloud.java.util.Optional
  38. import com.owncloud.android.MainApp
  39. import com.owncloud.android.R
  40. import com.owncloud.android.datamodel.ArbitraryDataProvider
  41. import com.owncloud.android.datamodel.FileDataStorageManager
  42. import com.owncloud.android.datamodel.FilesystemDataProvider
  43. import com.owncloud.android.datamodel.PushConfigurationState
  44. import com.owncloud.android.datamodel.SyncedFolderProvider
  45. import com.owncloud.android.datamodel.UploadsStorageManager
  46. import com.owncloud.android.lib.common.OwnCloudClient
  47. import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
  48. import com.owncloud.android.lib.common.utils.Log_OC
  49. import com.owncloud.android.lib.resources.users.RemoteWipeSuccessRemoteOperation
  50. import com.owncloud.android.ui.activity.ContactsPreferenceActivity
  51. import com.owncloud.android.ui.activity.ManageAccountsActivity
  52. import com.owncloud.android.ui.events.AccountRemovedEvent
  53. import com.owncloud.android.utils.EncryptionUtils
  54. import com.owncloud.android.utils.FileStorageUtils
  55. import com.owncloud.android.utils.PushUtils
  56. import org.greenrobot.eventbus.EventBus
  57. import java.io.File
  58. import java.util.ArrayList
  59. /**
  60. * Removes account and all local files
  61. */
  62. @Suppress("LongParameterList") // legacy code
  63. class AccountRemovalWork(
  64. private val context: Context,
  65. params: WorkerParameters,
  66. private val uploadsStorageManager: UploadsStorageManager,
  67. private val userAccountManager: UserAccountManager,
  68. private val backgroundJobManager: BackgroundJobManager,
  69. private val clock: Clock,
  70. private val eventBus: EventBus
  71. ) : Worker(context, params) {
  72. companion object {
  73. const val TAG = "AccountRemovalJob"
  74. const val ACCOUNT = "account"
  75. const val REMOTE_WIPE = "remote_wipe"
  76. }
  77. @Suppress("ReturnCount") // legacy code
  78. override fun doWork(): Result {
  79. val accountName = inputData.getString(ACCOUNT) ?: ""
  80. if (TextUtils.isEmpty(accountName)) { // didn't receive account to delete
  81. return Result.failure()
  82. }
  83. val optionalUser = userAccountManager.getUser(accountName)
  84. if (!optionalUser.isPresent) { // trying to delete non-existing user
  85. return Result.failure()
  86. }
  87. val remoteWipe = inputData.getBoolean(REMOTE_WIPE, false)
  88. val arbitraryDataProvider = ArbitraryDataProvider(context.contentResolver)
  89. val user = optionalUser.get()
  90. backgroundJobManager.cancelPeriodicContactsBackup(user)
  91. val userRemoved = userAccountManager.removeUser(user)
  92. if (userRemoved) {
  93. eventBus.post(AccountRemovedEvent())
  94. }
  95. val storageManager = FileDataStorageManager(user.toPlatformAccount(), context.contentResolver)
  96. // remove all files
  97. removeFiles(user, storageManager)
  98. // delete all database entries
  99. storageManager.deleteAllFiles()
  100. // disable daily backup
  101. arbitraryDataProvider.storeOrUpdateKeyValue(
  102. user.accountName,
  103. ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP,
  104. "false"
  105. )
  106. // unregister push notifications
  107. unregisterPushNotifications(context, user, arbitraryDataProvider)
  108. // remove pending account removal
  109. arbitraryDataProvider.deleteKeyForAccount(user.accountName, ManageAccountsActivity.PENDING_FOR_REMOVAL)
  110. // remove synced folders set for account
  111. remoceSyncedFolders(context, user.toPlatformAccount(), clock)
  112. // delete all uploads for account
  113. uploadsStorageManager.removeAccountUploads(user.toPlatformAccount())
  114. // delete stored E2E keys
  115. arbitraryDataProvider.deleteKeyForAccount(user.accountName, EncryptionUtils.PRIVATE_KEY)
  116. arbitraryDataProvider.deleteKeyForAccount(user.accountName, EncryptionUtils.PUBLIC_KEY)
  117. if (remoteWipe) {
  118. val optionalClient = createClient(user)
  119. if (optionalClient.isPresent) {
  120. val client = optionalClient.get()
  121. val authToken = client.credentials.authToken
  122. RemoteWipeSuccessRemoteOperation(authToken).execute(client)
  123. }
  124. }
  125. // notify Document Provider
  126. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
  127. val authority = context.resources.getString(R.string.document_provider_authority)
  128. val rootsUri = DocumentsContract.buildRootsUri(authority)
  129. context.contentResolver.notifyChange(rootsUri, null)
  130. }
  131. return Result.success()
  132. }
  133. private fun unregisterPushNotifications(
  134. context: Context,
  135. user: User,
  136. arbitraryDataProvider: ArbitraryDataProvider
  137. ) {
  138. val arbitraryDataPushString = arbitraryDataProvider.getValue(user.toPlatformAccount(),
  139. PushUtils.KEY_PUSH)
  140. val pushServerUrl = context.resources.getString(R.string.push_server_url)
  141. if (!TextUtils.isEmpty(arbitraryDataPushString) && !TextUtils.isEmpty(pushServerUrl)) {
  142. val gson = Gson()
  143. val pushArbitraryData = gson.fromJson(arbitraryDataPushString,
  144. PushConfigurationState::class.java)
  145. pushArbitraryData.isShouldBeDeleted = true
  146. arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, PushUtils.KEY_PUSH,
  147. gson.toJson(pushArbitraryData))
  148. PushUtils.pushRegistrationToServer(userAccountManager, pushArbitraryData.getPushToken())
  149. }
  150. }
  151. private fun remoceSyncedFolders(context: Context, account: Account, clock: Clock) {
  152. val syncedFolderProvider = SyncedFolderProvider(context.contentResolver,
  153. AppPreferencesImpl.fromContext(context),
  154. clock)
  155. val syncedFolders = syncedFolderProvider.syncedFolders
  156. val syncedFolderIds: MutableList<Long> = ArrayList()
  157. for (syncedFolder in syncedFolders) {
  158. if (syncedFolder.account == account.name) {
  159. syncedFolderIds.add(syncedFolder.id)
  160. }
  161. }
  162. syncedFolderProvider.deleteSyncFoldersForAccount(account)
  163. val filesystemDataProvider = FilesystemDataProvider(context.contentResolver)
  164. for (syncedFolderId in syncedFolderIds) {
  165. filesystemDataProvider.deleteAllEntriesForSyncedFolder(java.lang.Long.toString(syncedFolderId))
  166. }
  167. }
  168. private fun removeFiles(user: User, storageManager: FileDataStorageManager) {
  169. val tempDir = File(FileStorageUtils.getTemporalPath(user.accountName))
  170. val saveDir = File(FileStorageUtils.getSavePath(user.accountName))
  171. FileStorageUtils.deleteRecursively(tempDir, storageManager)
  172. FileStorageUtils.deleteRecursively(saveDir, storageManager)
  173. }
  174. private fun createClient(user: User): Optional<OwnCloudClient> {
  175. @Suppress("TooGenericExceptionCaught") // needs migration to newer api to get rid of exceptions
  176. return try {
  177. val context = MainApp.getAppContext()
  178. val factory = OwnCloudClientManagerFactory.getDefaultSingleton()
  179. val client = factory.getClientFor(user.toOwnCloudAccount(), context)
  180. Optional.of(client)
  181. } catch (e: Exception) {
  182. Log_OC.e(this, "Could not create client", e)
  183. Optional.empty()
  184. }
  185. }
  186. }