Merge pull request #740 from owncloud/revert-669-master
[pub/Android/ownCloud.git] / src / com / owncloud / android / operations / UploadFileOperation.java
index 5bf9cc0..1536a60 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   it under the terms of the GNU General Public License version 2,
+ *   as published by the Free Software Foundation.
  *
  *   This program is distributed in the hope that it will be useful,
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  *
  *   This program is distributed in the hope that it will be useful,
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -25,25 +24,34 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.HashSet;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import org.apache.commons.httpclient.HttpException;
 import org.apache.commons.httpclient.methods.PutMethod;
 import org.apache.commons.httpclient.methods.PutMethod;
-import org.apache.http.HttpStatus;
+import org.apache.commons.httpclient.methods.RequestEntity;
 
 import android.accounts.Account;
 
 import android.accounts.Account;
-import android.util.Log;
+import android.content.Context;
+import android.net.Uri;
 
 
+import com.owncloud.android.MainApp;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileUploader;
-import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
+import com.owncloud.android.lib.common.network.ProgressiveDataTransferer;
+import com.owncloud.android.lib.common.operations.OperationCancelledException;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.files.ChunkedUploadRemoteFileOperation;
+import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation;
+import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation;
 import com.owncloud.android.utils.FileStorageUtils;
 import com.owncloud.android.utils.FileStorageUtils;
+import com.owncloud.android.utils.UriUtils;
 
 
-import eu.alefzero.webdav.FileRequestEntity;
-import eu.alefzero.webdav.OnDatatransferProgressListener;
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
 
 /**
  * Remote operation performing the upload of a file to an ownCloud server
 
 /**
  * Remote operation performing the upload of a file to an ownCloud server
@@ -58,6 +66,7 @@ public class UploadFileOperation extends RemoteOperation {
     private OCFile mFile;
     private OCFile mOldFile;
     private String mRemotePath = null;
     private OCFile mFile;
     private OCFile mOldFile;
     private String mRemotePath = null;
+    private boolean mChunked = false;
     private boolean mIsInstant = false;
     private boolean mRemoteFolderToBeCreated = false;
     private boolean mForceOverwrite = false;
     private boolean mIsInstant = false;
     private boolean mRemoteFolderToBeCreated = false;
     private boolean mForceOverwrite = false;
@@ -68,28 +77,40 @@ public class UploadFileOperation extends RemoteOperation {
     PutMethod mPutMethod = null;
     private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
     private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
     PutMethod mPutMethod = null;
     private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
     private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
-
-    public UploadFileOperation(Account account, OCFile file, boolean isInstant, boolean forceOverwrite,
-            int localBehaviour) {
+    private Context mContext;
+    
+    private UploadRemoteFileOperation mUploadOperation;
+
+    protected RequestEntity mEntity = null;
+
+    
+    public UploadFileOperation( Account account,
+                                OCFile file,
+                                boolean chunked,
+                                boolean isInstant, 
+                                boolean forceOverwrite,
+                                int localBehaviour, 
+                                Context context) {
         if (account == null)
             throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation creation");
         if (file == null)
             throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation");
         if (account == null)
             throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation creation");
         if (file == null)
             throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation");
-        if (file.getStoragePath() == null || file.getStoragePath().length() <= 0
-                || !(new File(file.getStoragePath()).exists())) {
+        if (file.getStoragePath() == null || file.getStoragePath().length() <= 0) {
             throw new IllegalArgumentException(
             throw new IllegalArgumentException(
-                    "Illegal file in UploadFileOperation; storage path invalid or file not found: "
+                    "Illegal file in UploadFileOperation; storage path invalid: "
                             + file.getStoragePath());
         }
 
         mAccount = account;
         mFile = file;
         mRemotePath = file.getRemotePath();
                             + file.getStoragePath());
         }
 
         mAccount = account;
         mFile = file;
         mRemotePath = file.getRemotePath();
+        mChunked = chunked;
         mIsInstant = isInstant;
         mForceOverwrite = forceOverwrite;
         mLocalBehaviour = localBehaviour;
         mOriginalStoragePath = mFile.getStoragePath();
         mOriginalFileName = mFile.getFileName();
         mIsInstant = isInstant;
         mForceOverwrite = forceOverwrite;
         mLocalBehaviour = localBehaviour;
         mOriginalStoragePath = mFile.getStoragePath();
         mOriginalFileName = mFile.getFileName();
+        mContext = context;
     }
 
     public Account getAccount() {
     }
 
     public Account getAccount() {
@@ -147,13 +168,27 @@ public class UploadFileOperation extends RemoteOperation {
     public Set<OnDatatransferProgressListener> getDataTransferListeners() {
         return mDataTransferListeners;
     }
     public Set<OnDatatransferProgressListener> getDataTransferListeners() {
         return mDataTransferListeners;
     }
-
-    public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
-        mDataTransferListeners.add(listener);
+    
+    public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
+        synchronized (mDataTransferListeners) {
+            mDataTransferListeners.add(listener);
+        }
+        if (mEntity != null) {
+            ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListener(listener);
+        }
+    }
+    
+    public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
+        synchronized (mDataTransferListeners) {
+            mDataTransferListeners.remove(listener);
+        }
+        if (mEntity != null) {
+            ((ProgressiveDataTransferer)mEntity).removeDatatransferProgressListener(listener);
+        }
     }
 
     @Override
     }
 
     @Override
-    protected RemoteOperationResult run(WebdavClient client) {
+    protected RemoteOperationResult run(OwnCloudClient client) {
         RemoteOperationResult result = null;
         boolean localCopyPassed = false, nameCheckPassed = false;
         File temporalFile = null, originalFile = new File(mOriginalStoragePath), expectedFile = null;
         RemoteOperationResult result = null;
         boolean localCopyPassed = false, nameCheckPassed = false;
         File temporalFile = null, originalFile = new File(mOriginalStoragePath), expectedFile = null;
@@ -175,7 +210,7 @@ public class UploadFileOperation extends RemoteOperation {
                                                                                                 // !!!
             expectedFile = new File(expectedPath);
 
                                                                                                 // !!!
             expectedFile = new File(expectedPath);
 
-            // check location of local file; if not the expected, copy to a
+            // check location of local file; if not the expected, copy to a
             // temporal file before upload (if COPY is the expected behaviour)
             if (!mOriginalStoragePath.equals(expectedPath) && mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY) {
 
             // temporal file before upload (if COPY is the expected behaviour)
             if (!mOriginalStoragePath.equals(expectedPath) && mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY) {
 
@@ -185,72 +220,99 @@ public class UploadFileOperation extends RemoteOperation {
                                    // copied
 
                 } else {
                                    // copied
 
                 } else {
+
                     String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
                     mFile.setStoragePath(temporalPath);
                     temporalFile = new File(temporalPath);
                     String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
                     mFile.setStoragePath(temporalPath);
                     temporalFile = new File(temporalPath);
-                    if (!mOriginalStoragePath.equals(temporalPath)) { // preventing
-                                                                      // weird
-                                                                      // but
-                                                                      // possible
-                                                                      // situation
-                        InputStream in = null;
-                        OutputStream out = null;
-                        try {
-                            File temporalParent = temporalFile.getParentFile();
-                            temporalParent.mkdirs();
-                            if (!temporalParent.isDirectory()) {
-                                throw new IOException("Unexpected error: parent directory could not be created");
-                            }
-                            temporalFile.createNewFile();
-                            if (!temporalFile.isFile()) {
-                                throw new IOException("Unexpected error: target file could not be created");
-                            }
-                            in = new FileInputStream(originalFile);
+
+                    File temporalParent = temporalFile.getParentFile();
+                    temporalParent.mkdirs();
+                    if (!temporalParent.isDirectory()) {
+                        throw new IOException("Unexpected error: parent directory could not be created");
+                    }
+                    temporalFile.createNewFile();
+                    if (!temporalFile.isFile()) {
+                        throw new IOException("Unexpected error: target file could not be created");
+                    }
+
+                    InputStream in = null;
+                    OutputStream out = null;
+
+                    try {
+
+                        // In case document provider schema as 'content://'
+                        if (mOriginalStoragePath.startsWith(UriUtils.URI_CONTENT_SCHEME)) {
+
+                            Uri uri = Uri.parse(mOriginalStoragePath);
+
+                            in = MainApp.getAppContext().getContentResolver().openInputStream(uri);
                             out = new FileOutputStream(temporalFile);
                             out = new FileOutputStream(temporalFile);
-                            byte[] buf = new byte[1024];
-                            int len;
-                            while ((len = in.read(buf)) > 0) {
-                                out.write(buf, 0, len);
-                            }
 
 
-                        } catch (Exception e) {
-                            result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_COPIED);
-                            return result;
-
-                        } finally {
-                            try {
-                                if (in != null)
-                                    in.close();
-                            } catch (Exception e) {
-                                Log.d(TAG, "Weird exception while closing input stream for " + mOriginalStoragePath
-                                        + " (ignoring)", e);
+                            int nRead;
+                            byte[] data = new byte[16384];
+
+                            while ((nRead = in.read(data, 0, data.length)) != -1) {
+                                out.write(data, 0, nRead);
                             }
                             }
-                            try {
-                                if (out != null)
-                                    out.close();
-                            } catch (Exception e) {
-                                Log.d(TAG, "Weird exception while closing output stream for " + expectedPath
-                                        + " (ignoring)", e);
+
+                            out.flush();
+
+                        } else {
+                            if (!mOriginalStoragePath.equals(temporalPath)) { // preventing
+                                                                          // weird
+                                                                          // but
+                                                                          // possible
+                                                                          // situation
+
+                                in = new FileInputStream(originalFile);
+                                out = new FileOutputStream(temporalFile);
+                                byte[] buf = new byte[1024];
+                                int len;
+                                while ((len = in.read(buf)) > 0) {
+                                    out.write(buf, 0, len);
+                                }
                             }
                         }
                             }
                         }
+
+                    } catch (Exception e) {
+                        result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_COPIED);
+                        return result;
+
+                    } finally {
+                        try {
+                            if (in != null)
+                                in.close();
+                        } catch (Exception e) {
+                            Log_OC.d(TAG, "Weird exception while closing input stream for " + mOriginalStoragePath + " (ignoring)", e);
+                        }
+                        try {
+                            if (out != null)
+                                out.close();
+                        } catch (Exception e) {
+                            Log_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
+                        }
                     }
                 }
             }
             localCopyPassed = true;
 
                     }
                 }
             }
             localCopyPassed = true;
 
-            // / perform the upload
-            synchronized (mCancellationRequested) {
-                if (mCancellationRequested.get()) {
-                    throw new OperationCancelledException();
-                } else {
-                    mPutMethod = new PutMethod(client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()));
-                }
+            /// perform the upload
+            if ( mChunked && (new File(mFile.getStoragePath())).length() > ChunkedUploadRemoteFileOperation.CHUNK_SIZE ) {
+                mUploadOperation = new ChunkedUploadRemoteFileOperation(mFile.getStoragePath(), mFile.getRemotePath(), 
+                        mFile.getMimetype());
+            } else {
+                mUploadOperation = new UploadRemoteFileOperation(mFile.getStoragePath(), mFile.getRemotePath(), 
+                        mFile.getMimetype());
+            }
+            Iterator <OnDatatransferProgressListener> listener = mDataTransferListeners.iterator();
+            while (listener.hasNext()) {
+                mUploadOperation.addDatatransferProgressListener(listener.next());
             }
             }
-            int status = uploadFile(client);
+            result = mUploadOperation.execute(client);
 
 
-            // / move local temporal file or original file to its corresponding
+            /// move local temporal file or original file to its corresponding
             // location in the ownCloud local folder
             // location in the ownCloud local folder
-            if (isSuccess(status)) {
+            if (result.isSuccess()) {
                 if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
                     mFile.setStoragePath(null);
 
                 if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
                     mFile.setStoragePath(null);
 
@@ -283,8 +345,6 @@ public class UploadFileOperation extends RemoteOperation {
                 }
             }
 
                 }
             }
 
-            result = new RemoteOperationResult(isSuccess(status), status);
-
         } catch (Exception e) {
             // TODO something cleaner with cancellations
             if (mCancellationRequested.get()) {
         } catch (Exception e) {
             // TODO something cleaner with cancellations
             if (mCancellationRequested.get()) {
@@ -298,8 +358,7 @@ public class UploadFileOperation extends RemoteOperation {
                 temporalFile.delete();
             }
             if (result.isSuccess()) {
                 temporalFile.delete();
             }
             if (result.isSuccess()) {
-                Log.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
-
+                Log_OC.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
             } else {
                 if (result.getException() != null) {
                     String complement = "";
             } else {
                 if (result.getException() != null) {
                     String complement = "";
@@ -309,12 +368,9 @@ public class UploadFileOperation extends RemoteOperation {
                         complement = " (while copying local file to " + FileStorageUtils.getSavePath(mAccount.name)
                                 + ")";
                     }
                         complement = " (while copying local file to " + FileStorageUtils.getSavePath(mAccount.name)
                                 + ")";
                     }
-                    Log.e(TAG,
-                            "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage()
-                                    + complement, result.getException());
+                    Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage() + complement, result.getException());
                 } else {
                 } else {
-                    Log.e(TAG,
-                            "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
+                    Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
                 }
             }
         }
                 }
             }
         }
@@ -340,27 +396,6 @@ public class UploadFileOperation extends RemoteOperation {
         mFile = newFile;
     }
 
         mFile = newFile;
     }
 
-    public boolean isSuccess(int status) {
-        return ((status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT));
-    }
-
-    protected int uploadFile(WebdavClient client) throws HttpException, IOException, OperationCancelledException {
-        int status = -1;
-        try {
-            File f = new File(mFile.getStoragePath());
-            FileRequestEntity entity = new FileRequestEntity(f, getMimeType());
-            entity.addOnDatatransferProgressListeners(mDataTransferListeners);
-            mPutMethod.setRequestEntity(entity);
-            status = client.executeMethod(mPutMethod);
-            client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
-
-        } finally {
-            mPutMethod.releaseConnection(); // let the connection available for
-                                            // other methods
-        }
-        return status;
-    }
-
     /**
      * Checks if remotePath does not exist in the server and returns it, or adds
      * a suffix to it in order to avoid the server file is overwritten.
     /**
      * Checks if remotePath does not exist in the server and returns it, or adds
      * a suffix to it in order to avoid the server file is overwritten.
@@ -368,8 +403,8 @@ public class UploadFileOperation extends RemoteOperation {
      * @param string
      * @return
      */
      * @param string
      * @return
      */
-    private String getAvailableRemotePath(WebdavClient wc, String remotePath) throws Exception {
-        boolean check = wc.existsFile(remotePath);
+    private String getAvailableRemotePath(OwnCloudClient wc, String remotePath) throws Exception {
+        boolean check = existsFile(wc, remotePath);
         if (!check) {
             return remotePath;
         }
         if (!check) {
             return remotePath;
         }
@@ -384,10 +419,12 @@ public class UploadFileOperation extends RemoteOperation {
         int count = 2;
         do {
             suffix = " (" + count + ")";
         int count = 2;
         do {
             suffix = " (" + count + ")";
-            if (pos >= 0)
-                check = wc.existsFile(remotePath + suffix + "." + extension);
-            else
-                check = wc.existsFile(remotePath + suffix);
+            if (pos >= 0) {
+                check = existsFile(wc, remotePath + suffix + "." + extension);
+            }
+            else {
+                check = existsFile(wc, remotePath + suffix);
+            }
             count++;
         } while (check);
 
             count++;
         } while (check);
 
@@ -398,12 +435,13 @@ public class UploadFileOperation extends RemoteOperation {
         }
     }
 
         }
     }
 
+    private boolean existsFile(OwnCloudClient client, String remotePath){
+        ExistenceCheckRemoteOperation existsOperation = new ExistenceCheckRemoteOperation(remotePath, mContext, false);
+        RemoteOperationResult result = existsOperation.execute(client);
+        return result.isSuccess();
+    }
+    
     public void cancel() {
     public void cancel() {
-        synchronized (mCancellationRequested) {
-            mCancellationRequested.set(true);
-            if (mPutMethod != null)
-                mPutMethod.abort();
-        }
+        mUploadOperation.cancel();
     }
     }
-
 }
 }