/* * Nextcloud Android client application * * @author Tobias Kaminsky * Copyright (C) 2017 Tobias Kaminsky * Copyright (C) 2017 Nextcloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package com.owncloud.android.operations; import android.content.Context; import com.google.gson.reflect.TypeToken; import com.nextcloud.client.account.User; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; import com.owncloud.android.datamodel.DecryptedFolderMetadata; import com.owncloud.android.datamodel.EncryptedFolderMetadata; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.e2ee.GetMetadataRemoteOperation; import com.owncloud.android.lib.resources.e2ee.LockFileRemoteOperation; import com.owncloud.android.lib.resources.e2ee.UnlockFileRemoteOperation; import com.owncloud.android.lib.resources.e2ee.UpdateMetadataRemoteOperation; import com.owncloud.android.utils.EncryptionUtils; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.NameValuePair; import org.apache.jackrabbit.webdav.client.methods.DeleteMethod; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.spec.InvalidKeySpecException; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; /** * Remote operation performing the removal of a remote encrypted file or folder */ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation { private static final String TAG = RemoveRemoteEncryptedFileOperation.class.getSimpleName(); private static final int REMOVE_READ_TIMEOUT = 30000; private static final int REMOVE_CONNECTION_TIMEOUT = 5000; private final String remotePath; private final long parentId; private User user; private final ArbitraryDataProvider arbitraryDataProvider; private final String fileName; /** * Constructor * * @param remotePath RemotePath of the remote file or folder to remove from the server * @param parentId local id of parent folder */ RemoveRemoteEncryptedFileOperation(String remotePath, long parentId, User user, Context context, String fileName) { this.remotePath = remotePath; this.parentId = parentId; this.user = user; this.fileName = fileName; arbitraryDataProvider = new ArbitraryDataProviderImpl(context); } /** * Performs the remove operation. */ @Override protected RemoteOperationResult run(OwnCloudClient client) { RemoteOperationResult result; DeleteMethod delete = null; String token = null; DecryptedFolderMetadata metadata; String privateKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PRIVATE_KEY); String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY); try { // Lock folder RemoteOperationResult lockFileOperationResult = new LockFileRemoteOperation(parentId).execute(client); if (lockFileOperationResult.isSuccess()) { token = (String) lockFileOperationResult.getData().get(0); } else if (lockFileOperationResult.getHttpCode() == HttpStatus.SC_FORBIDDEN) { throw new RemoteOperationFailedException("Forbidden! Please try again later.)"); } else { throw new RemoteOperationFailedException("Unknown error!"); } // refresh metadata RemoteOperationResult getMetadataOperationResult = new GetMetadataRemoteOperation(parentId).execute(client); if (getMetadataOperationResult.isSuccess()) { // decrypt metadata String serializedEncryptedMetadata = (String) getMetadataOperationResult.getData().get(0); EncryptedFolderMetadata encryptedFolderMetadata = EncryptionUtils.deserializeJSON( serializedEncryptedMetadata, new TypeToken() { }); metadata = EncryptionUtils.decryptFolderMetaData(encryptedFolderMetadata, privateKey, arbitraryDataProvider, user, parentId); } else { throw new RemoteOperationFailedException("No Metadata found!"); } // delete file remote delete = new DeleteMethod(client.getFilesDavUri(remotePath)); delete.setQueryString(new NameValuePair[]{new NameValuePair(E2E_TOKEN, token)}); int status = client.executeMethod(delete, REMOVE_READ_TIMEOUT, REMOVE_CONNECTION_TIMEOUT); delete.getResponseBodyAsString(); // exhaust the response, although not interesting result = new RemoteOperationResult(delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, delete); Log_OC.i(TAG, "Remove " + remotePath + ": " + result.getLogMessage()); // remove file from metadata metadata.getFiles().remove(fileName); EncryptedFolderMetadata encryptedFolderMetadata = EncryptionUtils.encryptFolderMetadata( metadata, privateKey, publicKey, arbitraryDataProvider, user, parentId); String serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata); // upload metadata RemoteOperationResult uploadMetadataOperationResult = new UpdateMetadataRemoteOperation(parentId, serializedFolderMetadata, token).execute(client); if (!uploadMetadataOperationResult.isSuccess()) { throw new RemoteOperationFailedException("Metadata not uploaded!"); } // return success return result; } catch (NoSuchAlgorithmException | IOException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeySpecException | CertificateException e) { result = new RemoteOperationResult(e); Log_OC.e(TAG, "Remove " + remotePath + ": " + result.getLogMessage(), e); } finally { if (delete != null) { delete.releaseConnection(); } // unlock file if (token != null) { RemoteOperationResult unlockFileOperationResult = new UnlockFileRemoteOperation(parentId, token) .execute(client); if (!unlockFileOperationResult.isSuccess()) { Log_OC.e(TAG, "Failed to unlock " + parentId); } } } return result; } }