RenameFileOperation.java 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /*
  2. * ownCloud Android client application
  3. *
  4. * @author David A. Velasco
  5. * @author masensio
  6. * Copyright (C) 2015 ownCloud Inc.
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2,
  10. * as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. package com.owncloud.android.operations;
  22. import android.text.TextUtils;
  23. import com.owncloud.android.datamodel.FileDataStorageManager;
  24. import com.owncloud.android.datamodel.OCFile;
  25. import com.owncloud.android.lib.common.OwnCloudClient;
  26. import com.owncloud.android.lib.common.operations.RemoteOperationResult;
  27. import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
  28. import com.owncloud.android.lib.common.utils.Log_OC;
  29. import com.owncloud.android.lib.resources.files.RenameFileRemoteOperation;
  30. import com.owncloud.android.operations.common.SyncOperation;
  31. import com.owncloud.android.utils.FileStorageUtils;
  32. import com.owncloud.android.utils.MimeTypeUtil;
  33. import java.io.File;
  34. import java.io.IOException;
  35. /**
  36. * Remote operation performing the rename of a remote file (or folder?) in the ownCloud server.
  37. */
  38. public class RenameFileOperation extends SyncOperation {
  39. private static final String TAG = RenameFileOperation.class.getSimpleName();
  40. private OCFile file;
  41. private String remotePath;
  42. private String newName;
  43. /**
  44. * Constructor
  45. *
  46. * @param remotePath RemotePath of the OCFile instance describing the remote file or
  47. * folder to rename
  48. * @param newName New name to set as the name of file.
  49. */
  50. public RenameFileOperation(String remotePath, String newName) {
  51. this.remotePath = remotePath;
  52. this.newName = newName;
  53. }
  54. /**
  55. * Performs the rename operation.
  56. *
  57. * @param client Client object to communicate with the remote ownCloud server.
  58. */
  59. @Override
  60. protected RemoteOperationResult run(OwnCloudClient client) {
  61. RemoteOperationResult result = null;
  62. String newRemotePath = null;
  63. file = getStorageManager().getFileByPath(remotePath);
  64. // check if the new name is valid in the local file system
  65. try {
  66. if (!isValidNewName()) {
  67. return new RemoteOperationResult(ResultCode.INVALID_LOCAL_FILE_NAME);
  68. }
  69. String parent = new File(file.getRemotePath()).getParent();
  70. parent = parent.endsWith(OCFile.PATH_SEPARATOR) ? parent : parent + OCFile.PATH_SEPARATOR;
  71. newRemotePath = parent + newName;
  72. if (file.isFolder()) {
  73. newRemotePath += OCFile.PATH_SEPARATOR;
  74. }
  75. // check local overwrite
  76. if (getStorageManager().getFileByPath(newRemotePath) != null) {
  77. return new RemoteOperationResult(ResultCode.INVALID_OVERWRITE);
  78. }
  79. result = new RenameFileRemoteOperation(file.getFileName(),
  80. file.getRemotePath(),
  81. newName,
  82. file.isFolder())
  83. .execute(client);
  84. if (result.isSuccess()) {
  85. if (file.isFolder()) {
  86. getStorageManager().moveLocalFile(file, newRemotePath, parent);
  87. //saveLocalDirectory();
  88. } else {
  89. saveLocalFile();
  90. }
  91. }
  92. } catch (IOException e) {
  93. Log_OC.e(TAG, "Rename " + file.getRemotePath() + " to " + ((newRemotePath == null) ?
  94. newName : newRemotePath) + ": " +
  95. (result!= null ? result.getLogMessage() : ""), e);
  96. }
  97. return result;
  98. }
  99. private void saveLocalFile() {
  100. file.setFileName(newName);
  101. // try to rename the local copy of the file
  102. if (file.isDown()) {
  103. String oldPath = file.getStoragePath();
  104. File f = new File(oldPath);
  105. String parentStoragePath = f.getParent();
  106. if (!parentStoragePath.endsWith(File.separator)) {
  107. parentStoragePath += File.separator;
  108. }
  109. if (f.renameTo(new File(parentStoragePath + newName))) {
  110. String newPath = parentStoragePath + newName;
  111. file.setStoragePath(newPath);
  112. // notify MediaScanner about removed file
  113. getStorageManager().deleteFileInMediaScan(oldPath);
  114. // notify to scan about new file, if it is a media file
  115. if (MimeTypeUtil.isMedia(file.getMimeType())) {
  116. FileDataStorageManager.triggerMediaScan(newPath);
  117. }
  118. }
  119. // else - NOTHING: the link to the local file is kept although the local name
  120. // can't be updated
  121. // TODO - study conditions when this could be a problem
  122. }
  123. getStorageManager().saveFile(file);
  124. }
  125. /**
  126. * Checks if the new name to set is valid in the file system
  127. *
  128. * The only way to be sure is trying to create a file with that name. It's made in the
  129. * temporal directory for downloads, out of any account, and then removed.
  130. *
  131. * IMPORTANT: The test must be made in the same file system where files are download.
  132. * The internal storage could be formatted with a different file system.
  133. *
  134. * TODO move this method, and maybe FileDownload.get***Path(), to a class with utilities
  135. * specific for the interactions with the file system
  136. *
  137. * @return 'True' if a temporal file named with the name to set could be
  138. * created in the file system where local files are stored.
  139. * @throws IOException When the temporal folder can not be created.
  140. */
  141. private boolean isValidNewName() throws IOException {
  142. // check tricky names
  143. if (TextUtils.isEmpty(newName) || newName.contains(File.separator)) {
  144. return false;
  145. }
  146. // create a test file
  147. String tmpFolderName = FileStorageUtils.getTemporalPath("");
  148. File testFile = new File(tmpFolderName + newName);
  149. File tmpFolder = testFile.getParentFile();
  150. if (! tmpFolder.mkdirs()) {
  151. Log_OC.e(TAG, "Unable to create parent folder " + tmpFolder.getAbsolutePath());
  152. }
  153. if (!tmpFolder.isDirectory()) {
  154. throw new IOException("Unexpected error: temporal directory could not be created");
  155. }
  156. try {
  157. testFile.createNewFile(); // return value is ignored; it could be 'false' because
  158. // the file already existed, that doesn't invalidate the name
  159. } catch (IOException e) {
  160. Log_OC.i(TAG, "Test for validity of name " + newName + " in the file system failed");
  161. return false;
  162. }
  163. boolean result = testFile.exists() && testFile.isFile();
  164. // cleaning ; result is ignored, since there is not much we could do in case of failure,
  165. // but repeat and repeat...
  166. testFile.delete();
  167. return result;
  168. }
  169. public OCFile getFile() {
  170. return this.file;
  171. }
  172. }