1 /* ownCloud Android client application 
   2  *   Copyright (C) 2012 Bartek Przybylski 
   4  *   This program is free software: you can redistribute it and/or modify 
   5  *   it under the terms of the GNU General Public License as published by 
   6  *   the Free Software Foundation, either version 3 of the License, or 
   7  *   (at your option) any later version. 
   9  *   This program is distributed in the hope that it will be useful, 
  10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of 
  11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  12  *   GNU General Public License for more details. 
  14  *   You should have received a copy of the GNU General Public License 
  15  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  19 package com
.owncloud
.android
.operations
; 
  22 import java
.io
.FileInputStream
; 
  23 import java
.io
.FileOutputStream
; 
  24 import java
.io
.IOException
; 
  25 import java
.io
.InputStream
; 
  26 import java
.io
.OutputStream
; 
  27 import java
.util
.HashSet
; 
  29 import java
.util
.concurrent
.atomic
.AtomicBoolean
; 
  31 import org
.apache
.commons
.httpclient
.HttpException
; 
  32 import org
.apache
.commons
.httpclient
.methods
.PutMethod
; 
  33 import org
.apache
.http
.HttpStatus
; 
  35 import com
.owncloud
.android
.datamodel
.OCFile
; 
  36 import com
.owncloud
.android
.operations
.RemoteOperation
; 
  37 import com
.owncloud
.android
.operations
.RemoteOperationResult
; 
  38 import com
.owncloud
.android
.operations
.RemoteOperationResult
.ResultCode
; 
  39 import com
.owncloud
.android
.utils
.FileStorageUtils
; 
  41 import eu
.alefzero
.webdav
.FileRequestEntity
; 
  42 import eu
.alefzero
.webdav
.OnDatatransferProgressListener
; 
  43 import eu
.alefzero
.webdav
.WebdavClient
; 
  44 import eu
.alefzero
.webdav
.WebdavUtils
; 
  45 import android
.accounts
.Account
; 
  46 import android
.util
.Log
; 
  49  * Remote operation performing the upload of a file to an ownCloud server 
  51  * @author David A. Velasco 
  53 public class UploadFileOperation 
extends RemoteOperation 
{ 
  55     private static final String TAG 
= UploadFileOperation
.class.getSimpleName(); 
  57     private Account mAccount
; 
  59     private OCFile mOldFile
; 
  60     private String mRemotePath 
= null
; 
  61     private boolean mIsInstant 
= false
; 
  62     private boolean mRemoteFolderToBeCreated 
= false
; 
  63     private boolean mForceOverwrite 
= false
; 
  64     private boolean mMoveLocalFile 
= false
; 
  65     private boolean mWasRenamed 
= false
; 
  66     PutMethod mPutMethod 
= null
; 
  67     private Set
<OnDatatransferProgressListener
> mDataTransferListeners 
= new HashSet
<OnDatatransferProgressListener
>(); 
  68     private final AtomicBoolean mCancellationRequested 
= new AtomicBoolean(false
); 
  71     public UploadFileOperation( Account account
, 
  74                                 boolean forceOverwrite
, 
  75                                 boolean moveLocalFile
) { 
  77             throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation creation"); 
  79             throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation"); 
  80         if (file
.getStoragePath() == null 
|| file
.getStoragePath().length() <= 0 || !(new File(file
.getStoragePath()).exists())) { 
  81             throw new IllegalArgumentException("Illegal file in UploadFileOperation; storage path invalid or file not found: " + file
.getStoragePath()); 
  86         mRemotePath 
= file
.getRemotePath(); 
  87         mIsInstant 
= isInstant
; 
  88         mForceOverwrite 
= forceOverwrite
; 
  89         mMoveLocalFile 
= moveLocalFile
; 
  93     public Account 
getAccount() { 
  97     public OCFile 
getFile() { 
 101     public OCFile 
getOldFile() { 
 105     public String 
getStoragePath() { 
 106         return mFile
.getStoragePath(); 
 109     public String 
getRemotePath() { 
 110         return mFile
.getRemotePath();  
 113     public String 
getMimeType() { 
 114         return mFile
.getMimetype(); 
 117     public boolean isInstant() { 
 121     public boolean isRemoteFolderToBeCreated() { 
 122         return mRemoteFolderToBeCreated
; 
 125     public void setRemoteFolderToBeCreated() { 
 126         mRemoteFolderToBeCreated 
= true
; 
 129     public boolean getForceOverwrite() { 
 130         return mForceOverwrite
; 
 133     public boolean wasRenamed() { 
 137     public Set
<OnDatatransferProgressListener
> getDataTransferListeners() { 
 138         return mDataTransferListeners
; 
 141     public void addDatatransferProgressListener (OnDatatransferProgressListener listener
) { 
 142         mDataTransferListeners
.add(listener
); 
 147     protected RemoteOperationResult 
run(WebdavClient client
) { 
 148         RemoteOperationResult result 
= null
; 
 149         boolean localCopyPassed 
= false
, nameCheckPassed 
= false
; 
 150         File temporalFile 
= null
, originalFile 
= null
; 
 151         String originalStoragePath 
= mFile
.getStoragePath(); 
 153             /// rename the file to upload, if necessary 
 154             if (!mForceOverwrite
) { 
 155                 String remotePath 
= getAvailableRemotePath(client
, mRemotePath
); 
 156                 mWasRenamed 
= !remotePath
.equals(mRemotePath
); 
 158                    createNewOCFile(remotePath
); 
 161             nameCheckPassed 
= true
; 
 163             /// check location of local file, and copy to a temporal file to upload it if not in its corresponding directory 
 164             String targetLocalPath 
=  FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, mFile
);             
 165             if (!originalStoragePath
.equals(targetLocalPath
)) {   
 166                 File ocLocalFolder 
= new File(FileStorageUtils
.getSavePath(mAccount
.name
)); 
 167                 originalFile 
= new File(originalStoragePath
); 
 168                 if (!mMoveLocalFile
) { 
 169                     // the file must be copied to the ownCloud local folder 
 171                     if (ocLocalFolder
.getUsableSpace() < originalFile
.length()) { 
 172                         result 
= new RemoteOperationResult(ResultCode
.LOCAL_STORAGE_FULL
); 
 176                         String temporalPath 
= FileStorageUtils
.getTemporalPath(mAccount
.name
) + mFile
.getRemotePath(); 
 177                         mFile
.setStoragePath(temporalPath
); 
 178                         temporalFile 
= new File(temporalPath
); 
 179                         if (!originalStoragePath
.equals(temporalPath
)) {   // preventing weird but possible situation 
 180                             InputStream 
in = new FileInputStream(originalFile
); 
 181                             OutputStream out 
= new FileOutputStream(temporalFile
); 
 182                             byte[] buf 
= new byte[1024]; 
 184                             while ((len 
= in.read(buf
)) > 0){ 
 185                                 out
.write(buf
, 0, len
); 
 191                 }   // else - the file will be MOVED to the corresponding directory AFTER the upload finishes 
 193             localCopyPassed 
= true
; 
 195             /// perform the upload 
 196             synchronized(mCancellationRequested
) { 
 197                 if (mCancellationRequested
.get()) { 
 198                     throw new OperationCancelledException(); 
 200                     mPutMethod 
= new PutMethod(client
.getBaseUri() + WebdavUtils
.encodePath(mFile
.getRemotePath())); 
 203             int status 
= uploadFile(client
); 
 206             /// move local temporal file or original file to its corresponding location in the ownCloud local folder 
 207             if (isSuccess(status
)) { 
 208                 File fileToMove 
= null
; 
 209                 if (temporalFile 
!= null
) {  
 210                     fileToMove 
= temporalFile
; 
 211                 } else if (originalFile 
!= null
) { 
 212                     fileToMove 
= originalFile
; 
 214                 if (fileToMove 
!= null
) { 
 215                     mFile
.setStoragePath(FileStorageUtils
.getDefaultSavePathFor(mAccount
.name
, mFile
)); 
 216                     File finalFile 
= new File(mFile
.getStoragePath()); 
 217                     if (!fileToMove
.renameTo(finalFile
)) { 
 218                         result 
= new RemoteOperationResult(ResultCode
.LOCAL_STORAGE_NOT_MOVED
); 
 224                 result 
= new RemoteOperationResult(isSuccess(status
), status
); 
 227         } catch (Exception e
) { 
 228             if (mCancellationRequested
.get()) { 
 229                 result 
= new RemoteOperationResult(new OperationCancelledException()); 
 231                 result 
= new RemoteOperationResult(e
); 
 236             if (temporalFile 
!= null 
&& !originalFile
.equals(temporalFile
)) { 
 237                 temporalFile
.delete(); 
 239             if (result
.isSuccess()) { 
 240                 Log
.i(TAG
, "Upload of " + originalStoragePath 
+ " to " + mRemotePath 
+ ": " + result
.getLogMessage()); 
 243                 if (result
.getException() != null
) { 
 244                     String complement 
= ""; 
 245                     if (!nameCheckPassed
) { 
 246                         complement 
= " (while checking file existence in server)"; 
 247                     } else if (!localCopyPassed
) { 
 248                         complement 
= " (while copying local file to " + FileStorageUtils
.getSavePath(mAccount
.name
) + ")"; 
 250                     Log
.e(TAG
, "Upload of " + originalStoragePath 
+ " to " + mRemotePath 
+ ": " + result
.getLogMessage() + complement
, result
.getException()); 
 252                     Log
.e(TAG
, "Upload of " + originalStoragePath 
+ " to " + mRemotePath 
+ ": " + result
.getLogMessage()); 
 261     private void createNewOCFile(String newRemotePath
) { 
 262         // a new OCFile instance must be created for a new remote path 
 263         OCFile newFile 
= new OCFile(newRemotePath
); 
 264         newFile
.setCreationTimestamp(mFile
.getCreationTimestamp()); 
 265         newFile
.setFileLength(mFile
.getFileLength()); 
 266         newFile
.setMimetype(mFile
.getMimetype()); 
 267         newFile
.setModificationTimestamp(mFile
.getModificationTimestamp()); 
 268         // newFile.setEtag(mFile.getEtag()) 
 269         newFile
.setKeepInSync(mFile
.keepInSync()); 
 270         newFile
.setLastSyncDateForProperties(mFile
.getLastSyncDateForProperties()); 
 271         newFile
.setLastSyncDateForData(mFile
.getLastSyncDateForData()); 
 272         newFile
.setStoragePath(mFile
.getStoragePath()); 
 273         newFile
.setParentId(mFile
.getParentId()); 
 279     public boolean isSuccess(int status
) { 
 280         return ((status 
== HttpStatus
.SC_OK 
|| status 
== HttpStatus
.SC_CREATED 
|| status 
== HttpStatus
.SC_NO_CONTENT
)); 
 284     protected int uploadFile(WebdavClient client
) throws HttpException
, IOException
, OperationCancelledException 
{ 
 287             File f 
= new File(mFile
.getStoragePath()); 
 288             FileRequestEntity entity 
= new FileRequestEntity(f
, getMimeType()); 
 289             entity
.addOnDatatransferProgressListeners(mDataTransferListeners
); 
 290             mPutMethod
.setRequestEntity(entity
); 
 291             status 
= client
.executeMethod(mPutMethod
); 
 292             client
.exhaustResponse(mPutMethod
.getResponseBodyAsStream()); 
 295             mPutMethod
.releaseConnection();    // let the connection available for other methods 
 301      * Checks if remotePath does not exist in the server and returns it, or adds a suffix to it in order to avoid the server 
 302      * file is overwritten. 
 307     private String 
getAvailableRemotePath(WebdavClient wc
, String remotePath
) throws Exception 
{ 
 308         boolean check 
= wc
.existsFile(remotePath
); 
 313         int pos 
= remotePath
.lastIndexOf("."); 
 315         String extension 
= ""; 
 317             extension 
= remotePath
.substring(pos
+1); 
 318             remotePath 
= remotePath
.substring(0, pos
); 
 322             suffix 
= " (" + count 
+ ")"; 
 324                 check 
= wc
.existsFile(remotePath 
+ suffix 
+ "." + extension
); 
 326                 check 
= wc
.existsFile(remotePath 
+ suffix
); 
 331             return remotePath 
+ suffix 
+ "." + extension
; 
 333             return remotePath 
+ suffix
; 
 338     public void cancel() { 
 339         synchronized(mCancellationRequested
) { 
 340             mCancellationRequested
.set(true
); 
 341             if (mPutMethod 
!= null
)