UploadFileOperation.java 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401
  1. /*
  2. * ownCloud Android client application
  3. *
  4. * @author David A. Velasco
  5. * Copyright (C) 2016 ownCloud GmbH.
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2,
  9. * as published by the Free Software Foundation.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.owncloud.android.operations;
  20. import android.accounts.Account;
  21. import android.annotation.SuppressLint;
  22. import android.content.Context;
  23. import android.net.Uri;
  24. import android.os.Build;
  25. import android.support.annotation.RequiresApi;
  26. import com.evernote.android.job.JobRequest;
  27. import com.evernote.android.job.util.Device;
  28. import com.google.gson.reflect.TypeToken;
  29. import com.owncloud.android.datamodel.ArbitraryDataProvider;
  30. import com.owncloud.android.datamodel.DecryptedFolderMetadata;
  31. import com.owncloud.android.datamodel.EncryptedFolderMetadata;
  32. import com.owncloud.android.datamodel.FileDataStorageManager;
  33. import com.owncloud.android.datamodel.OCFile;
  34. import com.owncloud.android.datamodel.ThumbnailsCacheManager;
  35. import com.owncloud.android.datamodel.UploadsStorageManager;
  36. import com.owncloud.android.db.OCUpload;
  37. import com.owncloud.android.files.services.FileUploader;
  38. import com.owncloud.android.lib.common.OwnCloudClient;
  39. import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
  40. import com.owncloud.android.lib.common.network.ProgressiveDataTransferer;
  41. import com.owncloud.android.lib.common.operations.OperationCancelledException;
  42. import com.owncloud.android.lib.common.operations.RemoteOperation;
  43. import com.owncloud.android.lib.common.operations.RemoteOperationResult;
  44. import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
  45. import com.owncloud.android.lib.common.utils.Log_OC;
  46. import com.owncloud.android.lib.resources.files.ChunkedUploadRemoteFileOperation;
  47. import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation;
  48. import com.owncloud.android.lib.resources.files.GetMetadataOperation;
  49. import com.owncloud.android.lib.resources.files.LockFileOperation;
  50. import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
  51. import com.owncloud.android.lib.resources.files.RemoteFile;
  52. import com.owncloud.android.lib.resources.files.StoreMetadataOperation;
  53. import com.owncloud.android.lib.resources.files.UnlockFileOperation;
  54. import com.owncloud.android.lib.resources.files.UpdateMetadataOperation;
  55. import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation;
  56. import com.owncloud.android.operations.common.SyncOperation;
  57. import com.owncloud.android.utils.ConnectivityUtils;
  58. import com.owncloud.android.utils.EncryptionUtils;
  59. import com.owncloud.android.utils.FileStorageUtils;
  60. import com.owncloud.android.utils.MimeType;
  61. import com.owncloud.android.utils.MimeTypeUtil;
  62. import com.owncloud.android.utils.PowerUtils;
  63. import com.owncloud.android.utils.UriUtils;
  64. import org.apache.commons.httpclient.HttpStatus;
  65. import org.apache.commons.httpclient.methods.RequestEntity;
  66. import org.lukhnos.nnio.file.Files;
  67. import org.lukhnos.nnio.file.Paths;
  68. import java.io.File;
  69. import java.io.FileInputStream;
  70. import java.io.FileNotFoundException;
  71. import java.io.FileOutputStream;
  72. import java.io.IOException;
  73. import java.io.InputStream;
  74. import java.io.OutputStream;
  75. import java.io.RandomAccessFile;
  76. import java.nio.channels.FileChannel;
  77. import java.nio.channels.FileLock;
  78. import java.nio.channels.OverlappingFileLockException;
  79. import java.util.HashMap;
  80. import java.util.HashSet;
  81. import java.util.Iterator;
  82. import java.util.Set;
  83. import java.util.UUID;
  84. import java.util.concurrent.atomic.AtomicBoolean;
  85. /**
  86. * Operation performing the update in the ownCloud server
  87. * of a file that was modified locally.
  88. */
  89. public class UploadFileOperation extends SyncOperation {
  90. private static final String TAG = UploadFileOperation.class.getSimpleName();
  91. public static final int CREATED_BY_USER = 0;
  92. public static final int CREATED_AS_INSTANT_PICTURE = 1;
  93. public static final int CREATED_AS_INSTANT_VIDEO = 2;
  94. /**
  95. * OCFile which is to be uploaded.
  96. */
  97. private OCFile mFile;
  98. /**
  99. * Original OCFile which is to be uploaded in case file had to be renamed
  100. * (if forceOverwrite==false and remote file already exists).
  101. */
  102. private OCFile mOldFile;
  103. private String mRemotePath = null;
  104. private String mFolderUnlockToken;
  105. private boolean mChunked = false;
  106. private boolean mRemoteFolderToBeCreated = false;
  107. private boolean mForceOverwrite = false;
  108. private int mLocalBehaviour = FileUploader.LOCAL_BEHAVIOUR_COPY;
  109. private int mCreatedBy = CREATED_BY_USER;
  110. private boolean mOnWifiOnly = false;
  111. private boolean mWhileChargingOnly = false;
  112. private boolean mIgnoringPowerSaveMode = false;
  113. private boolean mWasRenamed = false;
  114. private long mOCUploadId = -1;
  115. /**
  116. * Local path to file which is to be uploaded (before any possible renaming or moving).
  117. */
  118. private String mOriginalStoragePath = null;
  119. private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
  120. private OnRenameListener mRenameUploadListener;
  121. private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
  122. private final AtomicBoolean mUploadStarted = new AtomicBoolean(false);
  123. private Context mContext;
  124. private UploadRemoteFileOperation mUploadOperation;
  125. protected RequestEntity mEntity = null;
  126. private Account mAccount;
  127. private OCUpload mUpload;
  128. private UploadsStorageManager uploadsStorageManager;
  129. private boolean encryptedAncestor;
  130. public static OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType) {
  131. // MIME type
  132. if (mimeType == null || mimeType.length() <= 0) {
  133. mimeType = MimeTypeUtil.getBestMimeTypeByFilename(localPath);
  134. }
  135. OCFile newFile = new OCFile(remotePath);
  136. newFile.setStoragePath(localPath);
  137. newFile.setLastSyncDateForProperties(0);
  138. newFile.setLastSyncDateForData(0);
  139. // size
  140. if (localPath != null && localPath.length() > 0) {
  141. File localFile = new File(localPath);
  142. newFile.setFileLength(localFile.length());
  143. newFile.setLastSyncDateForData(localFile.lastModified());
  144. } // don't worry about not assigning size, the problems with localPath
  145. // are checked when the UploadFileOperation instance is created
  146. newFile.setMimetype(mimeType);
  147. return newFile;
  148. }
  149. public UploadFileOperation(Account account,
  150. OCFile file,
  151. OCUpload upload,
  152. boolean chunked,
  153. boolean forceOverwrite,
  154. int localBehaviour,
  155. Context context,
  156. boolean onWifiOnly,
  157. boolean whileChargingOnly
  158. ) {
  159. if (account == null) {
  160. throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation " + "creation");
  161. }
  162. if (upload == null) {
  163. throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation");
  164. }
  165. if (upload.getLocalPath() == null || upload.getLocalPath().length() <= 0) {
  166. throw new IllegalArgumentException(
  167. "Illegal file in UploadFileOperation; storage path invalid: "
  168. + upload.getLocalPath());
  169. }
  170. mAccount = account;
  171. mUpload = upload;
  172. if (file == null) {
  173. mFile = obtainNewOCFileToUpload(
  174. upload.getRemotePath(),
  175. upload.getLocalPath(),
  176. upload.getMimeType()
  177. );
  178. } else {
  179. mFile = file;
  180. }
  181. mOnWifiOnly = onWifiOnly;
  182. mWhileChargingOnly = whileChargingOnly;
  183. mRemotePath = upload.getRemotePath();
  184. mChunked = chunked;
  185. mForceOverwrite = forceOverwrite;
  186. mLocalBehaviour = localBehaviour;
  187. mOriginalStoragePath = mFile.getStoragePath();
  188. mContext = context;
  189. mOCUploadId = upload.getUploadId();
  190. mCreatedBy = upload.getCreadtedBy();
  191. mRemoteFolderToBeCreated = upload.isCreateRemoteFolder();
  192. // Ignore power save mode only if user explicitly created this upload
  193. mIgnoringPowerSaveMode = (mCreatedBy == CREATED_BY_USER);
  194. mFolderUnlockToken = upload.getFolderUnlockToken();
  195. }
  196. public boolean getIsWifiRequired() {
  197. return mOnWifiOnly;
  198. }
  199. public boolean getIsChargingRequired() {
  200. return mWhileChargingOnly;
  201. }
  202. public boolean getIsIgnoringPowerSaveMode() { return mIgnoringPowerSaveMode; }
  203. public Account getAccount() {
  204. return mAccount;
  205. }
  206. public String getFileName() {
  207. return (mFile != null) ? mFile.getFileName() : null;
  208. }
  209. public OCFile getFile() {
  210. return mFile;
  211. }
  212. /**
  213. * If remote file was renamed, return original OCFile which was uploaded. Is
  214. * null is file was not renamed.
  215. */
  216. public OCFile getOldFile() {
  217. return mOldFile;
  218. }
  219. public String getOriginalStoragePath() {
  220. return mOriginalStoragePath;
  221. }
  222. public String getStoragePath() {
  223. return mFile.getStoragePath();
  224. }
  225. public String getRemotePath() {
  226. return mFile.getRemotePath();
  227. }
  228. public String getDecryptedRemotePath() {
  229. return mFile.getDecryptedRemotePath();
  230. }
  231. public String getMimeType() {
  232. return mFile.getMimetype();
  233. }
  234. public int getLocalBehaviour() {
  235. return mLocalBehaviour;
  236. }
  237. public void setRemoteFolderToBeCreated() {
  238. mRemoteFolderToBeCreated = true;
  239. }
  240. public boolean wasRenamed() {
  241. return mWasRenamed;
  242. }
  243. public void setCreatedBy(int createdBy) {
  244. mCreatedBy = createdBy;
  245. if (createdBy < CREATED_BY_USER || CREATED_AS_INSTANT_VIDEO < createdBy) {
  246. mCreatedBy = CREATED_BY_USER;
  247. }
  248. }
  249. public int getCreatedBy() {
  250. return mCreatedBy;
  251. }
  252. public boolean isInstantPicture() {
  253. return mCreatedBy == CREATED_AS_INSTANT_PICTURE;
  254. }
  255. public boolean isInstantVideo() {
  256. return mCreatedBy == CREATED_AS_INSTANT_VIDEO;
  257. }
  258. public void setOCUploadId(long id) {
  259. mOCUploadId = id;
  260. }
  261. public long getOCUploadId() {
  262. return mOCUploadId;
  263. }
  264. public Set<OnDatatransferProgressListener> getDataTransferListeners() {
  265. return mDataTransferListeners;
  266. }
  267. public void addDataTransferProgressListener(OnDatatransferProgressListener listener) {
  268. synchronized (mDataTransferListeners) {
  269. mDataTransferListeners.add(listener);
  270. }
  271. if (mEntity != null) {
  272. ((ProgressiveDataTransferer) mEntity).addDatatransferProgressListener(listener);
  273. }
  274. if (mUploadOperation != null) {
  275. mUploadOperation.addDatatransferProgressListener(listener);
  276. }
  277. }
  278. public void removeDataTransferProgressListener(OnDatatransferProgressListener listener) {
  279. synchronized (mDataTransferListeners) {
  280. mDataTransferListeners.remove(listener);
  281. }
  282. if (mEntity != null) {
  283. ((ProgressiveDataTransferer) mEntity).removeDatatransferProgressListener(listener);
  284. }
  285. if (mUploadOperation != null) {
  286. mUploadOperation.removeDatatransferProgressListener(listener);
  287. }
  288. }
  289. public void addRenameUploadListener(OnRenameListener listener) {
  290. mRenameUploadListener = listener;
  291. }
  292. public boolean isChunkedUploadSupported() {
  293. return mChunked;
  294. }
  295. public Context getContext() {
  296. return mContext;
  297. }
  298. @Override
  299. @SuppressWarnings("PMD.AvoidDuplicateLiterals")
  300. protected RemoteOperationResult run(OwnCloudClient client) {
  301. mCancellationRequested.set(false);
  302. mUploadStarted.set(true);
  303. uploadsStorageManager = new UploadsStorageManager(mContext.getContentResolver(), mContext);
  304. for (OCUpload ocUpload : uploadsStorageManager.getAllStoredUploads()) {
  305. if (ocUpload.getUploadId() == getOCUploadId()) {
  306. ocUpload.setFileSize(0);
  307. uploadsStorageManager.updateUpload(ocUpload);
  308. break;
  309. }
  310. }
  311. // check the existence of the parent folder for the file to upload
  312. String remoteParentPath = new File(getRemotePath()).getParent();
  313. remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ?
  314. remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR;
  315. /*RemoteOperationResult result = grantFolderExistence(remoteParentPath, client);
  316. if (!result.isSuccess()) {
  317. return result;
  318. }*/
  319. OCFile parent = getStorageManager().getFileByPath(remoteParentPath);
  320. mFile.setParentId(parent.getFileId());
  321. // check if any parent is encrypted
  322. encryptedAncestor = FileStorageUtils.checkEncryptionStatus(parent, getStorageManager());
  323. mFile.setEncrypted(encryptedAncestor);
  324. // try to unlock folder with stored token, e.g. when upload needs to be resumed or app crashed
  325. if (encryptedAncestor && !mFolderUnlockToken.isEmpty()) {
  326. UnlockFileOperation unlockFileOperation = new UnlockFileOperation(parent.getLocalId(), mFolderUnlockToken);
  327. RemoteOperationResult unlockFileOperationResult = unlockFileOperation.execute(client, true);
  328. if (!unlockFileOperationResult.isSuccess()) {
  329. return unlockFileOperationResult;
  330. }
  331. }
  332. if (encryptedAncestor) {
  333. Log_OC.d(TAG, "encrypted upload");
  334. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
  335. return encryptedUpload(client, parent);
  336. } else {
  337. Log_OC.e(TAG, "Encrypted upload on old Android API");
  338. return new RemoteOperationResult(ResultCode.OLD_ANDROID_API);
  339. }
  340. } else {
  341. Log_OC.d(TAG, "normal upload");
  342. return normalUpload(client);
  343. }
  344. }
  345. @SuppressLint("AndroidLintUseSparseArrays") // gson cannot handle sparse arrays easily, therefore use hashmap
  346. @RequiresApi(api = Build.VERSION_CODES.KITKAT)
  347. private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile parentFile) {
  348. RemoteOperationResult result = null;
  349. File temporalFile = null;
  350. File originalFile = new File(mOriginalStoragePath);
  351. File expectedFile = null;
  352. FileLock fileLock = null;
  353. long size;
  354. boolean metadataExists = false;
  355. String token = null;
  356. ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContext().getContentResolver());
  357. String privateKey = arbitraryDataProvider.getValue(getAccount().name, EncryptionUtils.PRIVATE_KEY);
  358. String publicKey = arbitraryDataProvider.getValue(getAccount().name, EncryptionUtils.PUBLIC_KEY);
  359. try {
  360. // check conditions
  361. result = checkConditions(originalFile);
  362. /***** E2E *****/
  363. // Lock folder
  364. LockFileOperation lockFileOperation = new LockFileOperation(parentFile.getLocalId());
  365. RemoteOperationResult lockFileOperationResult = lockFileOperation.execute(client, true);
  366. if (lockFileOperationResult.isSuccess()) {
  367. token = (String) lockFileOperationResult.getData().get(0);
  368. // immediately store it
  369. mUpload.setFolderUnlockToken(token);
  370. uploadsStorageManager.updateUpload(mUpload);
  371. } else if (lockFileOperationResult.getHttpCode() == HttpStatus.SC_FORBIDDEN) {
  372. throw new Exception("Forbidden! Please try again later.)");
  373. } else {
  374. throw new Exception("Unknown error!");
  375. }
  376. // Update metadata
  377. GetMetadataOperation getMetadataOperation = new GetMetadataOperation(parentFile.getLocalId());
  378. RemoteOperationResult getMetadataOperationResult = getMetadataOperation.execute(client, true);
  379. DecryptedFolderMetadata metadata;
  380. if (getMetadataOperationResult.isSuccess()) {
  381. metadataExists = true;
  382. // decrypt metadata
  383. String serializedEncryptedMetadata = (String) getMetadataOperationResult.getData().get(0);
  384. EncryptedFolderMetadata encryptedFolderMetadata = EncryptionUtils.deserializeJSON(
  385. serializedEncryptedMetadata, new TypeToken<EncryptedFolderMetadata>() {
  386. });
  387. metadata = EncryptionUtils.decryptFolderMetaData(encryptedFolderMetadata, privateKey);
  388. } else if (getMetadataOperationResult.getHttpCode() == HttpStatus.SC_NOT_FOUND) {
  389. // new metadata
  390. metadata = new DecryptedFolderMetadata();
  391. metadata.setMetadata(new DecryptedFolderMetadata.Metadata());
  392. metadata.getMetadata().setMetadataKeys(new HashMap<>());
  393. String metadataKey = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey());
  394. String encryptedMetadataKey = EncryptionUtils.encryptStringAsymmetric(metadataKey, publicKey);
  395. metadata.getMetadata().getMetadataKeys().put(0, encryptedMetadataKey);
  396. } else {
  397. // TODO error
  398. throw new Exception("something wrong");
  399. }
  400. /***** E2E *****/
  401. // check name collision
  402. checkNameCollision(client, metadata, parentFile.isEncrypted());
  403. String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
  404. expectedFile = new File(expectedPath);
  405. result = copyFile(originalFile, expectedPath);
  406. if (result != null) {
  407. return result;
  408. }
  409. // Get the last modification date of the file from the file system
  410. Long timeStampLong = originalFile.lastModified() / 1000;
  411. String timeStamp = timeStampLong.toString();
  412. /***** E2E *****/
  413. // Key, always generate new one
  414. byte[] key = EncryptionUtils.generateKey();
  415. // IV, always generate new one
  416. byte[] iv = EncryptionUtils.randomBytes(EncryptionUtils.ivLength);
  417. EncryptionUtils.EncryptedFile encryptedFile = EncryptionUtils.encryptFile(mFile, key, iv);
  418. // new random file name, check if it exists in metadata
  419. String encryptedFileName = UUID.randomUUID().toString().replaceAll("-", "");
  420. while (metadata.getFiles().get(encryptedFileName) != null) {
  421. encryptedFileName = UUID.randomUUID().toString().replaceAll("-", "");
  422. }
  423. mFile.setEncryptedFileName(encryptedFileName);
  424. File encryptedTempFile = File.createTempFile("encFile", encryptedFileName);
  425. FileOutputStream fileOutputStream = new FileOutputStream(encryptedTempFile);
  426. fileOutputStream.write(encryptedFile.encryptedBytes);
  427. fileOutputStream.close();
  428. /***** E2E *****/
  429. FileChannel channel = null;
  430. try {
  431. channel = new RandomAccessFile(mFile.getStoragePath(), "rw").getChannel();
  432. fileLock = channel.tryLock();
  433. } catch (FileNotFoundException e) {
  434. // this basically means that the file is on SD card
  435. // try to copy file to temporary dir if it doesn't exist
  436. String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
  437. mFile.setStoragePath(temporalPath);
  438. temporalFile = new File(temporalPath);
  439. Files.deleteIfExists(Paths.get(temporalPath));
  440. result = copy(originalFile, temporalFile);
  441. if (result == null) {
  442. if (temporalFile.length() == originalFile.length()) {
  443. channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").getChannel();
  444. fileLock = channel.tryLock();
  445. } else {
  446. result = new RemoteOperationResult(ResultCode.LOCK_FAILED);
  447. }
  448. }
  449. }
  450. try {
  451. size = channel.size();
  452. } catch (IOException e1) {
  453. size = new File(mFile.getStoragePath()).length();
  454. }
  455. for (OCUpload ocUpload : uploadsStorageManager.getAllStoredUploads()) {
  456. if (ocUpload.getUploadId() == getOCUploadId()) {
  457. ocUpload.setFileSize(size);
  458. uploadsStorageManager.updateUpload(ocUpload);
  459. break;
  460. }
  461. }
  462. /// perform the upload
  463. if (mChunked && (size > ChunkedUploadRemoteFileOperation.CHUNK_SIZE)) {
  464. mUploadOperation = new ChunkedUploadRemoteFileOperation(mContext, encryptedTempFile.getAbsolutePath(),
  465. mFile.getParentRemotePath() + encryptedFileName, mFile.getMimetype(),
  466. mFile.getEtagInConflict(), timeStamp);
  467. } else {
  468. mUploadOperation = new UploadRemoteFileOperation(encryptedTempFile.getAbsolutePath(),
  469. mFile.getParentRemotePath() + encryptedFileName, mFile.getMimetype(),
  470. mFile.getEtagInConflict(), timeStamp);
  471. }
  472. Iterator<OnDatatransferProgressListener> listener = mDataTransferListeners.iterator();
  473. while (listener.hasNext()) {
  474. mUploadOperation.addDatatransferProgressListener(listener.next());
  475. }
  476. if (mCancellationRequested.get()) {
  477. throw new OperationCancelledException();
  478. }
  479. // FileChannel channel = null;
  480. // try {
  481. // channel = new RandomAccessFile(ocFile.getStoragePath(), "rw").getChannel();
  482. // fileLock = channel.tryLock();
  483. // } catch (FileNotFoundException e) {
  484. // if (temporalFile == null) {
  485. // String temporalPath = FileStorageUtils.getTemporalPath(account.name) + ocFile.getRemotePath();
  486. // ocFile.setStoragePath(temporalPath);
  487. // temporalFile = new File(temporalPath);
  488. //
  489. // result = copy(originalFile, temporalFile);
  490. //
  491. // if (result != null) {
  492. // return result;
  493. // } else {
  494. // if (temporalFile.length() == originalFile.length()) {
  495. // channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").getChannel();
  496. // fileLock = channel.tryLock();
  497. // } else {
  498. // while (temporalFile.length() != originalFile.length()) {
  499. // Files.deleteIfExists(Paths.get(temporalPath));
  500. // result = copy(originalFile, temporalFile);
  501. //
  502. // if (result != null) {
  503. // return result;
  504. // } else {
  505. // channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").
  506. // getChannel();
  507. // fileLock = channel.tryLock();
  508. // }
  509. // }
  510. // }
  511. // }
  512. // } else {
  513. // channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").getChannel();
  514. // fileLock = channel.tryLock();
  515. // }
  516. // }
  517. // boolean test = true;
  518. // if (test) {
  519. // throw new Exception("test");
  520. // }
  521. result = mUploadOperation.execute(client);
  522. // if (result == null || result.isSuccess() && mUploadOperation != null) {
  523. // result = mUploadOperation.execute(client);
  524. /// move local temporal file or original file to its corresponding
  525. // location in the Nextcloud local folder
  526. if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) {
  527. result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
  528. }
  529. // }
  530. if (result.isSuccess()) {
  531. // upload metadata
  532. DecryptedFolderMetadata.DecryptedFile decryptedFile = new DecryptedFolderMetadata.DecryptedFile();
  533. DecryptedFolderMetadata.Data data = new DecryptedFolderMetadata.Data();
  534. data.setFilename(mFile.getFileName());
  535. data.setMimetype(mFile.getMimetype());
  536. data.setKey(EncryptionUtils.encodeBytesToBase64String(key));
  537. decryptedFile.setEncrypted(data);
  538. decryptedFile.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(iv));
  539. decryptedFile.setAuthenticationTag(encryptedFile.authenticationTag);
  540. metadata.getFiles().put(encryptedFileName, decryptedFile);
  541. EncryptedFolderMetadata encryptedFolderMetadata = EncryptionUtils.encryptFolderMetadata(metadata,
  542. privateKey);
  543. String serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata);
  544. // upload metadata
  545. RemoteOperationResult uploadMetadataOperationResult;
  546. if (metadataExists) {
  547. // update metadata
  548. UpdateMetadataOperation storeMetadataOperation = new UpdateMetadataOperation(parentFile.getLocalId(),
  549. serializedFolderMetadata, token);
  550. uploadMetadataOperationResult = storeMetadataOperation.execute(client, true);
  551. } else {
  552. // store metadata
  553. StoreMetadataOperation storeMetadataOperation = new StoreMetadataOperation(parentFile.getLocalId(),
  554. serializedFolderMetadata);
  555. uploadMetadataOperationResult = storeMetadataOperation.execute(client, true);
  556. }
  557. if (!uploadMetadataOperationResult.isSuccess()) {
  558. throw new Exception();
  559. }
  560. }
  561. } catch (FileNotFoundException e) {
  562. Log_OC.d(TAG, mFile.getStoragePath() + " not exists anymore");
  563. result = new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND);
  564. } catch (OverlappingFileLockException e) {
  565. Log_OC.d(TAG, "Overlapping file lock exception");
  566. result = new RemoteOperationResult(ResultCode.LOCK_FAILED);
  567. } catch (Exception e) {
  568. result = new RemoteOperationResult(e);
  569. } finally {
  570. mUploadStarted.set(false);
  571. if (fileLock != null) {
  572. try {
  573. fileLock.release();
  574. } catch (IOException e) {
  575. Log_OC.e(TAG, "Failed to unlock file with path " + mFile.getStoragePath());
  576. }
  577. }
  578. if (temporalFile != null && !originalFile.equals(temporalFile)) {
  579. temporalFile.delete();
  580. }
  581. if (result == null) {
  582. result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR);
  583. }
  584. if (result.isSuccess()) {
  585. Log_OC.i(TAG, "Upload of " + mFile.getStoragePath() + " to " + mFile.getRemotePath() + ": " +
  586. result.getLogMessage());
  587. } else {
  588. if (result.getException() != null) {
  589. if (result.isCancelled()) {
  590. Log_OC.w(TAG, "Upload of " + mFile.getStoragePath() + " to " + mFile.getRemotePath() +
  591. ": " + result.getLogMessage());
  592. } else {
  593. Log_OC.e(TAG, "Upload of " + mFile.getStoragePath() + " to " + mFile.getRemotePath() +
  594. ": " + result.getLogMessage(), result.getException());
  595. }
  596. } else {
  597. Log_OC.e(TAG, "Upload of " + mFile.getStoragePath() + " to " + mFile.getRemotePath() +
  598. ": " + result.getLogMessage());
  599. }
  600. }
  601. }
  602. if (result.isSuccess()) {
  603. handleSuccessfulUpload(temporalFile, expectedFile, originalFile, client);
  604. RemoteOperationResult unlockFolderResult = unlockFolder(parentFile, client, token);
  605. if (!unlockFolderResult.isSuccess()) {
  606. return unlockFolderResult;
  607. }
  608. } else if (result.getCode() == ResultCode.SYNC_CONFLICT) {
  609. getStorageManager().saveConflict(mFile, mFile.getEtagInConflict());
  610. }
  611. return result;
  612. }
  613. private RemoteOperationResult unlockFolder(OCFile parentFolder, OwnCloudClient client, String token) {
  614. if (token != null) {
  615. UnlockFileOperation unlockFileOperation = new UnlockFileOperation(parentFolder.getLocalId(), token);
  616. RemoteOperationResult unlockFileOperationResult = unlockFileOperation.execute(client, true);
  617. return unlockFileOperationResult;
  618. } else
  619. return new RemoteOperationResult(new Exception("No token available"));
  620. }
  621. private RemoteOperationResult checkConditions(File originalFile) {
  622. RemoteOperationResult remoteOperationResult = null;
  623. // check that internet is not behind walled garden
  624. if (Device.getNetworkType(mContext).equals(JobRequest.NetworkType.ANY) ||
  625. ConnectivityUtils.isInternetWalled(mContext)) {
  626. remoteOperationResult = new RemoteOperationResult(ResultCode.NO_NETWORK_CONNECTION);
  627. return remoteOperationResult;
  628. }
  629. JobRequest.NetworkType type = Device.getNetworkType(mContext);
  630. // check that connectivity conditions are met and delays the upload otherwise
  631. if (mOnWifiOnly && !Device.getNetworkType(mContext).equals(JobRequest.NetworkType.UNMETERED)) {
  632. Log_OC.d(TAG, "Upload delayed until WiFi is available: " + getRemotePath());
  633. remoteOperationResult = new RemoteOperationResult(ResultCode.DELAYED_FOR_WIFI);
  634. return remoteOperationResult;
  635. }
  636. // check if charging conditions are met and delays the upload otherwise
  637. if (mWhileChargingOnly && (!Device.getBatteryStatus(mContext).isCharging() && Device.getBatteryStatus(mContext)
  638. .getBatteryPercent() < 1)) {
  639. Log_OC.d(TAG, "Upload delayed until the device is charging: " + getRemotePath());
  640. remoteOperationResult = new RemoteOperationResult(ResultCode.DELAYED_FOR_CHARGING);
  641. return remoteOperationResult;
  642. }
  643. // check that device is not in power save mode
  644. if (!mIgnoringPowerSaveMode && PowerUtils.isPowerSaveMode(mContext)) {
  645. Log_OC.d(TAG, "Upload delayed because device is in power save mode: " + getRemotePath());
  646. remoteOperationResult = new RemoteOperationResult(ResultCode.DELAYED_IN_POWER_SAVE_MODE);
  647. return remoteOperationResult;
  648. }
  649. // check if the file continues existing before schedule the operation
  650. if (!originalFile.exists()) {
  651. Log_OC.d(TAG, mOriginalStoragePath + " not exists anymore");
  652. remoteOperationResult = new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND);
  653. return remoteOperationResult;
  654. }
  655. return remoteOperationResult;
  656. }
  657. private RemoteOperationResult normalUpload(OwnCloudClient client) {
  658. RemoteOperationResult result = null;
  659. File temporalFile = null;
  660. File originalFile = new File(mOriginalStoragePath);
  661. File expectedFile = null;
  662. FileLock fileLock = null;
  663. long size = 0;
  664. try {
  665. // check conditions
  666. result = checkConditions(originalFile);
  667. // check name collision
  668. checkNameCollision(client, null, false);
  669. String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
  670. expectedFile = new File(expectedPath);
  671. result = copyFile(originalFile, expectedPath);
  672. if (result != null) {
  673. return result;
  674. }
  675. // Get the last modification date of the file from the file system
  676. Long timeStampLong = originalFile.lastModified() / 1000;
  677. String timeStamp = timeStampLong.toString();
  678. FileChannel channel = null;
  679. try {
  680. channel = new RandomAccessFile(mFile.getStoragePath(), "rw").getChannel();
  681. fileLock = channel.tryLock();
  682. } catch (FileNotFoundException e) {
  683. // this basically means that the file is on SD card
  684. // try to copy file to temporary dir if it doesn't exist
  685. String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
  686. mFile.setStoragePath(temporalPath);
  687. temporalFile = new File(temporalPath);
  688. Files.deleteIfExists(Paths.get(temporalPath));
  689. result = copy(originalFile, temporalFile);
  690. if (result == null) {
  691. if (temporalFile.length() == originalFile.length()) {
  692. channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").getChannel();
  693. fileLock = channel.tryLock();
  694. } else {
  695. result = new RemoteOperationResult(ResultCode.LOCK_FAILED);
  696. }
  697. }
  698. }
  699. try {
  700. size = channel.size();
  701. } catch (IOException e1) {
  702. size = new File(mFile.getStoragePath()).length();
  703. }
  704. for (OCUpload ocUpload : uploadsStorageManager.getAllStoredUploads()) {
  705. if (ocUpload.getUploadId() == getOCUploadId()) {
  706. ocUpload.setFileSize(size);
  707. uploadsStorageManager.updateUpload(ocUpload);
  708. break;
  709. }
  710. }
  711. // perform the upload
  712. if (mChunked && (size > ChunkedUploadRemoteFileOperation.CHUNK_SIZE)) {
  713. mUploadOperation = new ChunkedUploadRemoteFileOperation(mContext, mFile.getStoragePath(),
  714. mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict(), timeStamp);
  715. } else {
  716. mUploadOperation = new UploadRemoteFileOperation(mFile.getStoragePath(),
  717. mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict(), timeStamp);
  718. }
  719. Iterator<OnDatatransferProgressListener> listener = mDataTransferListeners.iterator();
  720. while (listener.hasNext()) {
  721. mUploadOperation.addDatatransferProgressListener(listener.next());
  722. }
  723. if (mCancellationRequested.get()) {
  724. throw new OperationCancelledException();
  725. }
  726. if (result == null || result.isSuccess() && mUploadOperation != null) {
  727. result = mUploadOperation.execute(client, mFile.isEncrypted());
  728. /// move local temporal file or original file to its corresponding
  729. // location in the Nextcloud local folder
  730. if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) {
  731. result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
  732. }
  733. }
  734. } catch (FileNotFoundException e) {
  735. Log_OC.d(TAG, mOriginalStoragePath + " not exists anymore");
  736. result = new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND);
  737. } catch (OverlappingFileLockException e) {
  738. Log_OC.d(TAG, "Overlapping file lock exception");
  739. result = new RemoteOperationResult(ResultCode.LOCK_FAILED);
  740. } catch (Exception e) {
  741. result = new RemoteOperationResult(e);
  742. } finally {
  743. mUploadStarted.set(false);
  744. if (fileLock != null) {
  745. try {
  746. fileLock.release();
  747. } catch (IOException e) {
  748. Log_OC.e(TAG, "Failed to unlock file with path " + mOriginalStoragePath);
  749. }
  750. }
  751. if (temporalFile != null && !originalFile.equals(temporalFile)) {
  752. temporalFile.delete();
  753. }
  754. if (result == null) {
  755. result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR);
  756. }
  757. if (result.isSuccess()) {
  758. Log_OC.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " +
  759. result.getLogMessage());
  760. } else {
  761. if (result.getException() != null) {
  762. if (result.isCancelled()) {
  763. Log_OC.w(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath +
  764. ": " + result.getLogMessage());
  765. } else {
  766. Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath +
  767. ": " + result.getLogMessage(), result.getException());
  768. }
  769. } else {
  770. Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath +
  771. ": " + result.getLogMessage());
  772. }
  773. }
  774. }
  775. if (result.isSuccess()) {
  776. handleSuccessfulUpload(temporalFile, expectedFile, originalFile, client);
  777. } else if (result.getCode() == ResultCode.SYNC_CONFLICT) {
  778. getStorageManager().saveConflict(mFile, mFile.getEtagInConflict());
  779. }
  780. return result;
  781. }
  782. private RemoteOperationResult copyFile(File originalFile, String expectedPath) throws OperationCancelledException,
  783. IOException {
  784. RemoteOperationResult result = null;
  785. if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY && !mOriginalStoragePath.equals(expectedPath)) {
  786. String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
  787. mFile.setStoragePath(temporalPath);
  788. File temporalFile = new File(temporalPath);
  789. result = copy(originalFile, temporalFile);
  790. }
  791. if (mCancellationRequested.get()) {
  792. throw new OperationCancelledException();
  793. }
  794. return result;
  795. }
  796. private void checkNameCollision(OwnCloudClient client, DecryptedFolderMetadata metadata, boolean encrypted)
  797. throws OperationCancelledException {
  798. /// automatic rename of file to upload in case of name collision in server
  799. Log_OC.d(TAG, "Checking name collision in server");
  800. if (!mForceOverwrite) {
  801. String remotePath = getAvailableRemotePath(client, mRemotePath, metadata, encrypted);
  802. mWasRenamed = !remotePath.equals(mRemotePath);
  803. if (mWasRenamed) {
  804. createNewOCFile(remotePath);
  805. Log_OC.d(TAG, "File renamed as " + remotePath);
  806. }
  807. mRemotePath = remotePath;
  808. mRenameUploadListener.onRenameUpload();
  809. }
  810. if (mCancellationRequested.get()) {
  811. throw new OperationCancelledException();
  812. }
  813. }
  814. private void handleSuccessfulUpload(File temporalFile, File expectedFile, File originalFile,
  815. OwnCloudClient client) {
  816. switch (mLocalBehaviour) {
  817. case FileUploader.LOCAL_BEHAVIOUR_FORGET:
  818. default:
  819. String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
  820. if (mOriginalStoragePath.equals(temporalPath)) {
  821. // delete local file is was pre-copied in temporary folder (see .ui.helpers.UriUploader)
  822. temporalFile = new File(temporalPath);
  823. temporalFile.delete();
  824. }
  825. mFile.setStoragePath("");
  826. saveUploadedFile(client);
  827. break;
  828. case FileUploader.LOCAL_BEHAVIOUR_DELETE:
  829. Log_OC.d(TAG, "Delete source file");
  830. originalFile.delete();
  831. getStorageManager().deleteFileInMediaScan(originalFile.getAbsolutePath());
  832. saveUploadedFile(client);
  833. break;
  834. case FileUploader.LOCAL_BEHAVIOUR_COPY:
  835. if (temporalFile != null) {
  836. try {
  837. move(temporalFile, expectedFile);
  838. } catch (IOException e) {
  839. Log_OC.e(TAG, e.getMessage());
  840. }
  841. }
  842. mFile.setStoragePath(expectedFile.getAbsolutePath());
  843. saveUploadedFile(client);
  844. FileDataStorageManager.triggerMediaScan(expectedFile.getAbsolutePath());
  845. break;
  846. case FileUploader.LOCAL_BEHAVIOUR_MOVE:
  847. String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
  848. File newFile = new File(expectedPath);
  849. try {
  850. move(originalFile, newFile);
  851. } catch (IOException e) {
  852. e.printStackTrace();
  853. }
  854. getStorageManager().deleteFileInMediaScan(originalFile.getAbsolutePath());
  855. mFile.setStoragePath(newFile.getAbsolutePath());
  856. saveUploadedFile(client);
  857. FileDataStorageManager.triggerMediaScan(newFile.getAbsolutePath());
  858. break;
  859. }
  860. }
  861. /**
  862. * Checks the existence of the folder where the current file will be uploaded both
  863. * in the remote server and in the local database.
  864. * <p/>
  865. * If the upload is set to enforce the creation of the folder, the method tries to
  866. * create it both remote and locally.
  867. *
  868. * @param pathToGrant Full remote path whose existence will be granted.
  869. * @return An {@link OCFile} instance corresponding to the folder where the file
  870. * will be uploaded.
  871. */
  872. private RemoteOperationResult grantFolderExistence(String pathToGrant, OwnCloudClient client) {
  873. RemoteOperation operation = new ExistenceCheckRemoteOperation(pathToGrant, mContext, false);
  874. RemoteOperationResult result = operation.execute(client, mFile.isEncrypted());
  875. if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mRemoteFolderToBeCreated) {
  876. SyncOperation syncOp = new CreateFolderOperation(pathToGrant, true);
  877. result = syncOp.execute(client, getStorageManager());
  878. }
  879. if (result.isSuccess()) {
  880. OCFile parentDir = getStorageManager().getFileByPath(pathToGrant);
  881. if (parentDir == null) {
  882. parentDir = createLocalFolder(pathToGrant);
  883. }
  884. if (parentDir != null) {
  885. result = new RemoteOperationResult(ResultCode.OK);
  886. } else {
  887. result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR);
  888. }
  889. }
  890. return result;
  891. }
  892. private OCFile createLocalFolder(String remotePath) {
  893. String parentPath = new File(remotePath).getParent();
  894. parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ?
  895. parentPath : parentPath + OCFile.PATH_SEPARATOR;
  896. OCFile parent = getStorageManager().getFileByPath(parentPath);
  897. if (parent == null) {
  898. parent = createLocalFolder(parentPath);
  899. }
  900. if (parent != null) {
  901. OCFile createdFolder = new OCFile(remotePath);
  902. createdFolder.setMimetype(MimeType.DIRECTORY);
  903. createdFolder.setParentId(parent.getFileId());
  904. getStorageManager().saveFile(createdFolder);
  905. return createdFolder;
  906. }
  907. return null;
  908. }
  909. /**
  910. * Create a new OCFile mFile with new remote path. This is required if forceOverwrite==false.
  911. * New file is stored as mFile, original as mOldFile.
  912. *
  913. * @param newRemotePath new remote path
  914. */
  915. private void createNewOCFile(String newRemotePath) {
  916. // a new OCFile instance must be created for a new remote path
  917. OCFile newFile = new OCFile(newRemotePath);
  918. newFile.setCreationTimestamp(mFile.getCreationTimestamp());
  919. newFile.setFileLength(mFile.getFileLength());
  920. newFile.setMimetype(mFile.getMimetype());
  921. newFile.setModificationTimestamp(mFile.getModificationTimestamp());
  922. newFile.setModificationTimestampAtLastSyncForData(
  923. mFile.getModificationTimestampAtLastSyncForData()
  924. );
  925. newFile.setEtag(mFile.getEtag());
  926. newFile.setAvailableOffline(mFile.isAvailableOffline());
  927. newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
  928. newFile.setLastSyncDateForData(mFile.getLastSyncDateForData());
  929. newFile.setStoragePath(mFile.getStoragePath());
  930. newFile.setParentId(mFile.getParentId());
  931. mOldFile = mFile;
  932. mFile = newFile;
  933. }
  934. /**
  935. * Checks if remotePath does not exist in the server and returns it, or adds
  936. * a suffix to it in order to avoid the server file is overwritten.
  937. *
  938. * @param client OwnCloud client
  939. * @param remotePath remote path of the file
  940. * @param metadata metadata of encrypted folder
  941. * @return new remote path
  942. */
  943. private String getAvailableRemotePath(OwnCloudClient client, String remotePath, DecryptedFolderMetadata metadata,
  944. boolean encrypted) {
  945. boolean check = existsFile(client, remotePath, metadata, encrypted);
  946. if (!check) {
  947. return remotePath;
  948. }
  949. int pos = remotePath.lastIndexOf('.');
  950. String suffix;
  951. String extension = "";
  952. String remotePathWithoutExtension = "";
  953. if (pos >= 0) {
  954. extension = remotePath.substring(pos + 1);
  955. remotePathWithoutExtension = remotePath.substring(0, pos);
  956. }
  957. int count = 2;
  958. do {
  959. suffix = " (" + count + ")";
  960. if (pos >= 0) {
  961. check = existsFile(client, remotePathWithoutExtension + suffix + "." + extension, metadata, encrypted);
  962. } else {
  963. check = existsFile(client, remotePath + suffix, metadata, encrypted);
  964. }
  965. count++;
  966. } while (check);
  967. if (pos >= 0) {
  968. return remotePathWithoutExtension + suffix + "." + extension;
  969. } else {
  970. return remotePath + suffix;
  971. }
  972. }
  973. private boolean existsFile(OwnCloudClient client, String remotePath, DecryptedFolderMetadata metadata,
  974. boolean encrypted) {
  975. if (encrypted) {
  976. String fileName = new File(remotePath).getName();
  977. for (DecryptedFolderMetadata.DecryptedFile file : metadata.getFiles().values()) {
  978. if (file.getEncrypted().getFilename().equalsIgnoreCase(fileName)) {
  979. return true;
  980. }
  981. }
  982. return false;
  983. } else {
  984. ExistenceCheckRemoteOperation existsOperation = new ExistenceCheckRemoteOperation(remotePath, mContext,
  985. false);
  986. RemoteOperationResult result = existsOperation.execute(client);
  987. return result.isSuccess();
  988. }
  989. }
  990. /**
  991. * Allows to cancel the actual upload operation. If actual upload operating
  992. * is in progress it is cancelled, if upload preparation is being performed
  993. * upload will not take place.
  994. */
  995. public void cancel() {
  996. if (mUploadOperation == null) {
  997. if (mUploadStarted.get()) {
  998. Log_OC.d(TAG, "Cancelling upload during upload preparations.");
  999. mCancellationRequested.set(true);
  1000. } else {
  1001. Log_OC.e(TAG, "No upload in progress. This should not happen.");
  1002. }
  1003. } else {
  1004. Log_OC.d(TAG, "Cancelling upload during actual upload operation.");
  1005. mUploadOperation.cancel();
  1006. }
  1007. }
  1008. /**
  1009. * As soon as this method return true, upload can be cancel via cancel().
  1010. */
  1011. public boolean isUploadInProgress() {
  1012. return mUploadStarted.get();
  1013. }
  1014. /**
  1015. * TODO rewrite with homogeneous fail handling, remove dependency on {@link RemoteOperationResult},
  1016. * TODO use Exceptions instead
  1017. *
  1018. * @param sourceFile Source file to copy.
  1019. * @param targetFile Target location to copy the file.
  1020. * @return {@link RemoteOperationResult}
  1021. * @throws IOException exception if file cannot be accessed
  1022. */
  1023. private RemoteOperationResult copy(File sourceFile, File targetFile) throws IOException {
  1024. Log_OC.d(TAG, "Copying local file");
  1025. RemoteOperationResult result = null;
  1026. if (FileStorageUtils.getUsableSpace(mAccount.name) < sourceFile.length()) {
  1027. result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_FULL);
  1028. return result; // error condition when the file should be copied
  1029. } else {
  1030. Log_OC.d(TAG, "Creating temporal folder");
  1031. File temporalParent = targetFile.getParentFile();
  1032. temporalParent.mkdirs();
  1033. if (!temporalParent.isDirectory()) {
  1034. throw new IOException(
  1035. "Unexpected error: parent directory could not be created");
  1036. }
  1037. Log_OC.d(TAG, "Creating temporal file");
  1038. targetFile.createNewFile();
  1039. if (!targetFile.isFile()) {
  1040. throw new IOException(
  1041. "Unexpected error: target file could not be created");
  1042. }
  1043. Log_OC.d(TAG, "Copying file contents");
  1044. InputStream in = null;
  1045. OutputStream out = null;
  1046. try {
  1047. if (!mOriginalStoragePath.equals(targetFile.getAbsolutePath())) {
  1048. // In case document provider schema as 'content://'
  1049. if (mOriginalStoragePath.startsWith(UriUtils.URI_CONTENT_SCHEME)) {
  1050. Uri uri = Uri.parse(mOriginalStoragePath);
  1051. in = mContext.getContentResolver().openInputStream(uri);
  1052. } else {
  1053. in = new FileInputStream(sourceFile);
  1054. }
  1055. out = new FileOutputStream(targetFile);
  1056. int nRead;
  1057. byte[] buf = new byte[4096];
  1058. while (!mCancellationRequested.get() &&
  1059. (nRead = in.read(buf)) > -1) {
  1060. out.write(buf, 0, nRead);
  1061. }
  1062. out.flush();
  1063. } // else: weird but possible situation, nothing to copy
  1064. if (mCancellationRequested.get()) {
  1065. result = new RemoteOperationResult(new OperationCancelledException());
  1066. return result;
  1067. }
  1068. } catch (Exception e) {
  1069. result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_COPIED);
  1070. return result;
  1071. } finally {
  1072. try {
  1073. if (in != null) {
  1074. in.close();
  1075. }
  1076. } catch (Exception e) {
  1077. Log_OC.d(TAG, "Weird exception while closing input stream for " +
  1078. mOriginalStoragePath + " (ignoring)", e);
  1079. }
  1080. try {
  1081. if (out != null) {
  1082. out.close();
  1083. }
  1084. } catch (Exception e) {
  1085. Log_OC.d(TAG, "Weird exception while closing output stream for " +
  1086. targetFile.getAbsolutePath() + " (ignoring)", e);
  1087. }
  1088. }
  1089. }
  1090. return result;
  1091. }
  1092. /**
  1093. * TODO rewrite with homogeneous fail handling, remove dependency on {@link RemoteOperationResult},
  1094. * TODO use Exceptions instead
  1095. * <p>
  1096. * TODO refactor both this and 'copy' in a single method
  1097. *
  1098. * @param sourceFile Source file to move.
  1099. * @param targetFile Target location to move the file.
  1100. * @return {@link RemoteOperationResult} result from remote operation
  1101. * @throws IOException exception if file cannot be read/wrote
  1102. */
  1103. private void move(File sourceFile, File targetFile) throws IOException {
  1104. if (!targetFile.equals(sourceFile)) {
  1105. File expectedFolder = targetFile.getParentFile();
  1106. expectedFolder.mkdirs();
  1107. if (expectedFolder.isDirectory()) {
  1108. if (!sourceFile.renameTo(targetFile)) {
  1109. // try to copy and then delete
  1110. targetFile.createNewFile();
  1111. FileChannel inChannel = new FileInputStream(sourceFile).getChannel();
  1112. FileChannel outChannel = new FileOutputStream(targetFile).getChannel();
  1113. try {
  1114. inChannel.transferTo(0, inChannel.size(), outChannel);
  1115. sourceFile.delete();
  1116. } catch (Exception e) {
  1117. mFile.setStoragePath(""); // forget the local file
  1118. // by now, treat this as a success; the file was uploaded
  1119. // the best option could be show a warning message
  1120. } finally {
  1121. if (inChannel != null) {
  1122. inChannel.close();
  1123. }
  1124. if (outChannel != null) {
  1125. outChannel.close();
  1126. }
  1127. }
  1128. }
  1129. } else {
  1130. mFile.setStoragePath("");
  1131. }
  1132. }
  1133. }
  1134. /**
  1135. * Saves a OC File after a successful upload.
  1136. * <p>
  1137. * A PROPFIND is necessary to keep the props in the local database
  1138. * synchronized with the server, specially the modification time and Etag
  1139. * (where available)
  1140. */
  1141. private void saveUploadedFile(OwnCloudClient client) {
  1142. OCFile file = mFile;
  1143. if (file.fileExists()) {
  1144. file = getStorageManager().getFileById(file.getFileId());
  1145. }
  1146. long syncDate = System.currentTimeMillis();
  1147. file.setLastSyncDateForData(syncDate);
  1148. // new PROPFIND to keep data consistent with server
  1149. // in theory, should return the same we already have
  1150. // TODO from the appropriate OC server version, get data from last PUT response headers, instead
  1151. // TODO of a new PROPFIND; the latter may fail, specially for chunked uploads
  1152. String path;
  1153. if (encryptedAncestor) {
  1154. path = file.getParentRemotePath() + mFile.getEncryptedFileName();
  1155. } else {
  1156. path = getRemotePath();
  1157. }
  1158. ReadRemoteFileOperation operation = new ReadRemoteFileOperation(path);
  1159. RemoteOperationResult result = operation.execute(client, mFile.isEncrypted());
  1160. if (result.isSuccess()) {
  1161. updateOCFile(file, (RemoteFile) result.getData().get(0));
  1162. file.setLastSyncDateForProperties(syncDate);
  1163. } else {
  1164. Log_OC.e(TAG, "Error reading properties of file after successful upload; this is gonna hurt...");
  1165. }
  1166. if (mWasRenamed) {
  1167. OCFile oldFile = getStorageManager().getFileByPath(mOldFile.getRemotePath());
  1168. if (oldFile != null) {
  1169. oldFile.setStoragePath(null);
  1170. getStorageManager().saveFile(oldFile);
  1171. getStorageManager().saveConflict(oldFile, null);
  1172. }
  1173. // else: it was just an automatic renaming due to a name
  1174. // coincidence; nothing else is needed, the storagePath is right
  1175. // in the instance returned by mCurrentUpload.getFile()
  1176. }
  1177. file.setNeedsUpdateThumbnail(true);
  1178. getStorageManager().saveFile(file);
  1179. getStorageManager().saveConflict(file, null);
  1180. FileDataStorageManager.triggerMediaScan(file.getStoragePath());
  1181. // generate new Thumbnail
  1182. final ThumbnailsCacheManager.ThumbnailGenerationTask task =
  1183. new ThumbnailsCacheManager.ThumbnailGenerationTask(getStorageManager(), mAccount);
  1184. task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, file.getRemoteId()));
  1185. }
  1186. private void updateOCFile(OCFile file, RemoteFile remoteFile) {
  1187. file.setCreationTimestamp(remoteFile.getCreationTimestamp());
  1188. file.setFileLength(remoteFile.getLength());
  1189. file.setMimetype(remoteFile.getMimeType());
  1190. file.setModificationTimestamp(remoteFile.getModifiedTimestamp());
  1191. file.setModificationTimestampAtLastSyncForData(remoteFile.getModifiedTimestamp());
  1192. file.setEtag(remoteFile.getEtag());
  1193. file.setRemoteId(remoteFile.getRemoteId());
  1194. }
  1195. public interface OnRenameListener {
  1196. void onRenameUpload();
  1197. }
  1198. }