Fixed 'keep both' action in conflicts dialog (both resulting file were linked to...
[pub/Android/ownCloud.git] / src / com / owncloud / android / operations / UploadFileOperation.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012 Bartek Przybylski
3 *
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.
8 *
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.
13 *
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/>.
16 *
17 */
18
19 package com.owncloud.android.operations;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.HashSet;
24 import java.util.Set;
25 import java.util.concurrent.atomic.AtomicBoolean;
26
27 import org.apache.commons.httpclient.HttpException;
28 import org.apache.commons.httpclient.methods.PutMethod;
29 import org.apache.http.HttpStatus;
30
31 import com.owncloud.android.datamodel.OCFile;
32 import com.owncloud.android.operations.RemoteOperation;
33 import com.owncloud.android.operations.RemoteOperationResult;
34
35 import eu.alefzero.webdav.FileRequestEntity;
36 import eu.alefzero.webdav.OnDatatransferProgressListener;
37 import eu.alefzero.webdav.WebdavClient;
38 import eu.alefzero.webdav.WebdavUtils;
39 import android.accounts.Account;
40 import android.util.Log;
41
42 /**
43 * Remote operation performing the upload of a file to an ownCloud server
44 *
45 * @author David A. Velasco
46 */
47 public class UploadFileOperation extends RemoteOperation {
48
49 private static final String TAG = UploadFileOperation.class.getSimpleName();
50
51 private Account mAccount;
52 private OCFile mFile;
53 private OCFile mOldFile;
54 private String mRemotePath = null;
55 private boolean mIsInstant = false;
56 private boolean mRemoteFolderToBeCreated = false;
57 private boolean mForceOverwrite = false;
58 private boolean mWasRenamed = false;
59 PutMethod mPutMethod = null;
60 private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
61 private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
62
63
64 public UploadFileOperation( Account account,
65 OCFile file,
66 boolean isInstant,
67 boolean forceOverwrite) {
68 if (account == null)
69 throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation creation");
70 if (file == null)
71 throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation");
72 if (file.getStoragePath() == null || file.getStoragePath().length() <= 0 || !(new File(file.getStoragePath()).exists())) {
73 throw new IllegalArgumentException("Illegal file in UploadFileOperation; storage path invalid or file not found: " + file.getStoragePath());
74 }
75
76 mAccount = account;
77 mFile = file;
78 mRemotePath = file.getRemotePath();
79 mIsInstant = isInstant;
80 mForceOverwrite = forceOverwrite;
81 }
82
83
84 public Account getAccount() {
85 return mAccount;
86 }
87
88 public OCFile getFile() {
89 return mFile;
90 }
91
92 public OCFile getOldFile() {
93 return mOldFile;
94 }
95
96 public String getStoragePath() {
97 return mFile.getStoragePath();
98 }
99
100 public String getRemotePath() {
101 return mFile.getRemotePath();
102 }
103
104 public String getMimeType() {
105 return mFile.getMimetype();
106 }
107
108 public boolean isInstant() {
109 return mIsInstant;
110 }
111
112 public boolean isRemoteFolderToBeCreated() {
113 return mRemoteFolderToBeCreated;
114 }
115
116 public void setRemoteFolderToBeCreated() {
117 mRemoteFolderToBeCreated = true;
118 }
119
120 public boolean getForceOverwrite() {
121 return mForceOverwrite;
122 }
123
124 public boolean wasRenamed() {
125 return mWasRenamed;
126 }
127
128 public Set<OnDatatransferProgressListener> getDataTransferListeners() {
129 return mDataTransferListeners;
130 }
131
132 public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
133 mDataTransferListeners.add(listener);
134 }
135
136
137 @Override
138 protected RemoteOperationResult run(WebdavClient client) {
139 RemoteOperationResult result = null;
140 boolean nameCheckPassed = false;
141 try {
142 /// rename the file to upload, if necessary
143 if (!mForceOverwrite) {
144 String remotePath = getAvailableRemotePath(client, mRemotePath);
145 mWasRenamed = !remotePath.equals(mRemotePath);
146 if (mWasRenamed) {
147 createNewOCFile(remotePath);
148 }
149 }
150
151 /// perform the upload
152 nameCheckPassed = true;
153 synchronized(mCancellationRequested) {
154 if (mCancellationRequested.get()) {
155 throw new OperationCancelledException();
156 } else {
157 mPutMethod = new PutMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
158 }
159 }
160 int status = uploadFile(client);
161 result = new RemoteOperationResult(isSuccess(status), status);
162 Log.i(TAG, "Upload of " + mFile.getStoragePath() + " to " + mRemotePath + ": " + result.getLogMessage());
163
164 } catch (Exception e) {
165 // TODO something cleaner
166 if (mCancellationRequested.get()) {
167 result = new RemoteOperationResult(new OperationCancelledException());
168 } else {
169 result = new RemoteOperationResult(e);
170 }
171 Log.e(TAG, "Upload of " + mFile.getStoragePath() + " to " + mRemotePath + ": " + result.getLogMessage() + (nameCheckPassed?"":" (while checking file existence in server)"), result.getException());
172 }
173
174 return result;
175 }
176
177
178 private void createNewOCFile(String newRemotePath) {
179 // a new OCFile instance must be created for a new remote path
180 OCFile newFile = new OCFile(newRemotePath);
181 newFile.setCreationTimestamp(mFile.getCreationTimestamp());
182 newFile.setFileLength(mFile.getFileLength());
183 newFile.setMimetype(mFile.getMimetype());
184 newFile.setModificationTimestamp(mFile.getModificationTimestamp());
185 // newFile.setEtag(mFile.getEtag())
186 newFile.setKeepInSync(mFile.keepInSync());
187 newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
188 newFile.setLastSyncDateForData(mFile.getLastSyncDateForData());
189 newFile.setStoragePath(mFile.getStoragePath());
190 newFile.setParentId(mFile.getParentId());
191 mOldFile = mFile;
192 mFile = newFile;
193 }
194
195
196 public boolean isSuccess(int status) {
197 return ((status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT));
198 }
199
200
201 protected int uploadFile(WebdavClient client) throws HttpException, IOException, OperationCancelledException {
202 int status = -1;
203 try {
204 File f = new File(mFile.getStoragePath());
205 FileRequestEntity entity = new FileRequestEntity(f, getMimeType());
206 entity.addOnDatatransferProgressListeners(mDataTransferListeners);
207 mPutMethod.setRequestEntity(entity);
208 status = client.executeMethod(mPutMethod);
209 client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
210
211 } finally {
212 mPutMethod.releaseConnection(); // let the connection available for other methods
213 }
214 return status;
215 }
216
217 /**
218 * Checks if remotePath does not exist in the server and returns it, or adds a suffix to it in order to avoid the server
219 * file is overwritten.
220 *
221 * @param string
222 * @return
223 */
224 private String getAvailableRemotePath(WebdavClient wc, String remotePath) throws Exception {
225 boolean check = wc.existsFile(remotePath);
226 if (!check) {
227 return remotePath;
228 }
229
230 int pos = remotePath.lastIndexOf(".");
231 String suffix = "";
232 String extension = "";
233 if (pos >= 0) {
234 extension = remotePath.substring(pos+1);
235 remotePath = remotePath.substring(0, pos);
236 }
237 int count = 2;
238 do {
239 suffix = " (" + count + ")";
240 if (pos >= 0)
241 check = wc.existsFile(remotePath + suffix + "." + extension);
242 else
243 check = wc.existsFile(remotePath + suffix);
244 count++;
245 } while (check);
246
247 if (pos >=0) {
248 return remotePath + suffix + "." + extension;
249 } else {
250 return remotePath + suffix;
251 }
252 }
253
254
255 public void cancel() {
256 synchronized(mCancellationRequested) {
257 mCancellationRequested.set(true);
258 if (mPutMethod != null)
259 mPutMethod.abort();
260 }
261 }
262
263
264 }