diff --git a/src/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.java b/src/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.java index 21545222..39fab768 100644 --- a/src/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.java +++ b/src/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.java @@ -147,7 +147,7 @@ public class CreateRemoteShareOperation extends RemoteOperation { // retrieve more info - POST only returns the index of the new share OCShare emptyShare = (OCShare) result.getData().get(0); GetRemoteShareOperation getInfo = new GetRemoteShareOperation( - emptyShare.getIdRemoteShared() + emptyShare.getRemoteId() ); result = getInfo.execute(client); } diff --git a/src/com/owncloud/android/lib/resources/shares/OCShare.java b/src/com/owncloud/android/lib/resources/shares/OCShare.java index 644955cc..347bd1ae 100644 --- a/src/com/owncloud/android/lib/resources/shares/OCShare.java +++ b/src/com/owncloud/android/lib/resources/shares/OCShare.java @@ -61,7 +61,7 @@ public class OCShare implements Parcelable, Serializable { private String mSharedWithDisplayName; private boolean mIsFolder; private long mUserId; - private long mIdRemoteShared; + private long mRemoteId; private String mShareLink; public OCShare() { @@ -86,17 +86,17 @@ public class OCShare implements Parcelable, Serializable { mFileSource = 0; mItemSource = 0; mShareType = ShareType.NO_SHARED; - mShareWith = null; - mPath = null; + mShareWith = ""; + mPath = ""; mPermissions = -1; mSharedDate = 0; mExpirationDate = 0; - mToken = null; - mSharedWithDisplayName = null; + mToken = ""; + mSharedWithDisplayName = ""; mIsFolder = false; mUserId = -1; - mIdRemoteShared = -1; - mShareLink = null; + mRemoteId = -1; + mShareLink = ""; } /// Getters and Setters @@ -138,7 +138,7 @@ public class OCShare implements Parcelable, Serializable { } public void setShareWith(String shareWith) { - this.mShareWith = shareWith; + this.mShareWith = (shareWith != null) ? shareWith : ""; } public String getPath() { @@ -146,7 +146,7 @@ public class OCShare implements Parcelable, Serializable { } public void setPath(String path) { - this.mPath = path; + this.mPath = (path != null) ? path : ""; } public int getPermissions() { @@ -178,7 +178,7 @@ public class OCShare implements Parcelable, Serializable { } public void setToken(String token) { - this.mToken = token; + this.mToken = (token != null) ? token : ""; } public String getSharedWithDisplayName() { @@ -186,7 +186,7 @@ public class OCShare implements Parcelable, Serializable { } public void setSharedWithDisplayName(String sharedWithDisplayName) { - this.mSharedWithDisplayName = sharedWithDisplayName; + this.mSharedWithDisplayName = (sharedWithDisplayName != null) ? sharedWithDisplayName : ""; } public boolean isFolder() { @@ -205,12 +205,12 @@ public class OCShare implements Parcelable, Serializable { this.mUserId = userId; } - public long getIdRemoteShared() { - return mIdRemoteShared; + public long getRemoteId() { + return mRemoteId; } - public void setIdRemoteShared(long idRemoteShared) { - this.mIdRemoteShared = idRemoteShared; + public void setIdRemoteShared(long remoteId) { + this.mRemoteId = remoteId; } public String getShareLink() { @@ -218,7 +218,11 @@ public class OCShare implements Parcelable, Serializable { } public void setShareLink(String shareLink) { - this.mShareLink = shareLink; + this.mShareLink = (shareLink != null) ? shareLink : ""; + } + + public boolean isPasswordProtected() { + return ShareType.PUBLIC_LINK.equals(mShareType) && mShareWith.length() > 0; } /** @@ -264,7 +268,7 @@ public class OCShare implements Parcelable, Serializable { mSharedWithDisplayName = source.readString(); mIsFolder = source.readInt() == 0; mUserId = source.readLong(); - mIdRemoteShared = source.readLong(); + mRemoteId = source.readLong(); mShareLink = source.readString(); } @@ -290,7 +294,7 @@ public class OCShare implements Parcelable, Serializable { dest.writeString(mSharedWithDisplayName); dest.writeInt(mIsFolder ? 1 : 0); dest.writeLong(mUserId); - dest.writeLong(mIdRemoteShared); + dest.writeLong(mRemoteId); dest.writeString(mShareLink); } diff --git a/src/com/owncloud/android/lib/resources/shares/ShareToRemoteOperationResultParser.java b/src/com/owncloud/android/lib/resources/shares/ShareToRemoteOperationResultParser.java index c59ba8ed..8105f146 100644 --- a/src/com/owncloud/android/lib/resources/shares/ShareToRemoteOperationResultParser.java +++ b/src/com/owncloud/android/lib/resources/shares/ShareToRemoteOperationResultParser.java @@ -90,7 +90,8 @@ public class ShareToRemoteOperationResultParser { resultData.add(share); // build the share link if not in the response (only received when the share is created) if (share.getShareType() == ShareType.PUBLIC_LINK && - share.getShareLink() == null && + (share.getShareLink() == null || + share.getShareLink().length() <= 0) && share.getToken().length() > 0 ) { if (mServerBaseUri != null) { diff --git a/src/com/owncloud/android/lib/resources/shares/ShareXMLParser.java b/src/com/owncloud/android/lib/resources/shares/ShareXMLParser.java index e672deeb..f8b8cb00 100644 --- a/src/com/owncloud/android/lib/resources/shares/ShareXMLParser.java +++ b/src/com/owncloud/android/lib/resources/shares/ShareXMLParser.java @@ -82,7 +82,7 @@ public class ShareXMLParser { private static final String TYPE_FOLDER = "folder"; private static final int SUCCESS = 100; - private static final int ERROR_WRONG_PARAMETER = 403; + private static final int ERROR_WRONG_PARAMETER = 400; private static final int ERROR_FORBIDDEN = 403; private static final int ERROR_NOT_FOUND = 404; @@ -368,7 +368,7 @@ public class ShareXMLParser { } private boolean isValidShare(OCShare share) { - return (share.getIdRemoteShared() > -1); + return (share.getRemoteId() > -1); } private void fixPathForFolder(OCShare share) { diff --git a/src/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.java b/src/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.java new file mode 100644 index 00000000..aa6ae537 --- /dev/null +++ b/src/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.java @@ -0,0 +1,200 @@ +/* ownCloud Android Library is available under MIT license + * @author David A. Velasco + * Copyright (C) 2015 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.resources.shares; + + import android.net.Uri; + import android.util.Pair; + + import com.owncloud.android.lib.common.OwnCloudClient; + import com.owncloud.android.lib.common.operations.RemoteOperation; + import com.owncloud.android.lib.common.operations.RemoteOperationResult; + import com.owncloud.android.lib.common.utils.Log_OC; + + import org.apache.commons.httpclient.methods.PutMethod; + import org.apache.commons.httpclient.methods.StringRequestEntity; + import org.apache.http.HttpStatus; + + import java.text.DateFormat; + import java.text.SimpleDateFormat; + import java.util.ArrayList; + import java.util.Calendar; + import java.util.List; + + +/** + * Updates parameters of an existing Share resource, known its remote ID. + * + * Allow updating several parameters, triggering a request to the server per parameter. + */ + +public class UpdateRemoteShareOperation extends RemoteOperation { + + private static final String TAG = GetRemoteShareOperation.class.getSimpleName(); + + private static final String PARAM_PASSWORD = "password"; + private static final String PARAM_EXPIRATION_DATE = "expireDate"; + private static final String FORMAT_EXPIRATION_DATE = "yyyy-MM-dd"; + private static final String ENTITY_CONTENT_TYPE = "application/x-www-form-urlencoded"; + private static final String ENTITY_CHARSET = "UTF-8"; + + + /** Identifier of the share to update */ + private long mRemoteId; + + /** Password to set for the public link */ + private String mPassword; + + /** Expiration date to set for the public link */ + private long mExpirationDateInMillis; + + + /** + * Constructor. No update is initialized by default, need to be applied with setters below. + * + * @param remoteId Identifier of the share to update. + */ + public UpdateRemoteShareOperation(long remoteId) { + mRemoteId = remoteId; + mPassword = null; // no update + mExpirationDateInMillis = 0; // no update + } + + + /** + * Set password to update in Share resource. + * + * @param password Password to set to the target share. + * Empty string clears the current password. + * Null results in no update applied to the password. + */ + public void setPassword(String password) { + mPassword = password; + } + + + /** + * Set expiration date to update in Share resource. + * + * @param expirationDateInMillis Expiration date to set to the public link. + * A negative value clears the current expiration date. + * Zero value (start-of-epoch) results in no update done on + * the expiration date. + */ + public void setExpirationDate(long expirationDateInMillis) { + mExpirationDateInMillis = expirationDateInMillis; + } + + + @Override + protected RemoteOperationResult run(OwnCloudClient client) { + RemoteOperationResult result = null; + int status = -1; + + /// prepare array of parameters to update + List> parametersToUpdate = new ArrayList>(); + if (mPassword != null) { + parametersToUpdate.add(new Pair(PARAM_PASSWORD, mPassword)); + } + if (mExpirationDateInMillis < 0) { + // clear expiration date + parametersToUpdate.add(new Pair(PARAM_EXPIRATION_DATE, "")); + + } else if (mExpirationDateInMillis > 0) { + // set expiration date + DateFormat dateFormat = new SimpleDateFormat(FORMAT_EXPIRATION_DATE); + Calendar expirationDate = Calendar.getInstance(); + expirationDate.setTimeInMillis(mExpirationDateInMillis); + String formattedExpirationDate = dateFormat.format(expirationDate.getTime()); + parametersToUpdate.add(new Pair(PARAM_EXPIRATION_DATE, formattedExpirationDate)); + + } // else, ignore - no update + + /* TODO complete rest of parameters + if (mPermissions > 0) { + parametersToUpdate.add(new Pair("permissions", Integer.toString(mPermissions))); + } + if (mPublicUpload != null) { + parametersToUpdate.add(new Pair("publicUpload", mPublicUpload.toString()); + } + */ + + /// perform required PUT requests + PutMethod put = null; + String uriString = null; + + try{ + Uri requestUri = client.getBaseUri(); + Uri.Builder uriBuilder = requestUri.buildUpon(); + uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH.substring(1)); + uriBuilder.appendEncodedPath(Long.toString(mRemoteId)); + uriString = uriBuilder.build().toString(); + + for (Pair parameter : parametersToUpdate) { + if (put != null) { + put.releaseConnection(); + } + put = new PutMethod(uriString); + put.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE); + put.setRequestEntity(new StringRequestEntity( + parameter.first + "=" + parameter.second, + ENTITY_CONTENT_TYPE, + ENTITY_CHARSET + )); + + status = client.executeMethod(put); + + if (status == HttpStatus.SC_OK) { + String response = put.getResponseBodyAsString(); + + // Parse xml response + ShareToRemoteOperationResultParser parser = new ShareToRemoteOperationResultParser( + new ShareXMLParser() + ); + parser.setOwnCloudVersion(client.getOwnCloudVersion()); + parser.setServerBaseUri(client.getBaseUri()); + result = parser.parse(response); + + } else { + result = new RemoteOperationResult(false, status, put.getResponseHeaders()); + } + } + + } catch (Exception e) { + result = new RemoteOperationResult(e); + Log_OC.e(TAG, "Exception while updating remote share ", e); + if (put != null) { + put.releaseConnection(); + } + + } finally { + if (put != null) { + put.releaseConnection(); + } + } + return result; + } + +} diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/CreateShareTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/CreateShareTest.java index 8d08dd81..ccc25089 100644 --- a/test_client/tests/src/com/owncloud/android/lib/test_project/test/CreateShareTest.java +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/CreateShareTest.java @@ -132,7 +132,7 @@ public class CreateShareTest extends RemoteTest { 31); assertFalse(result.isSuccess()); assertEquals( - RemoteOperationResult.ResultCode.SHARE_WRONG_PARAMETER, + RemoteOperationResult.ResultCode.SHARE_FORBIDDEN, result.getCode() ); assertTrue( // error message from server as part of the result @@ -189,7 +189,7 @@ public class CreateShareTest extends RemoteTest { 31); assertFalse(result.isSuccess()); assertEquals( - RemoteOperationResult.ResultCode.SHARE_WRONG_PARAMETER, + RemoteOperationResult.ResultCode.SHARE_FORBIDDEN, result.getCode() ); assertTrue( // error message from server as part of the result diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/RemoveShareTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/RemoveShareTest.java index 61a5ecd0..64653861 100644 --- a/test_client/tests/src/com/owncloud/android/lib/test_project/test/RemoveShareTest.java +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/RemoveShareTest.java @@ -65,7 +65,7 @@ public class RemoveShareTest extends RemoteTest { Utils.logAndThrow(LOG_TAG, result); } else { OCShare created = (OCShare) result.getData().get(0); - mShareId = created.getIdRemoteShared(); + mShareId = created.getRemoteId(); } } diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/UpdateShareTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/UpdateShareTest.java new file mode 100644 index 00000000..389146b5 --- /dev/null +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/UpdateShareTest.java @@ -0,0 +1,241 @@ +/* ownCloud Android Library is available under MIT license + * @author masensio + * Copyright (C) 2015 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.test_project.test; + +import java.io.File; +import java.security.GeneralSecurityException; +import java.util.Calendar; + +import junit.framework.AssertionFailedError; + +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; + +import android.content.Context; +import android.net.Uri; +import android.util.Log; + +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudClientFactory; +import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.network.NetworkUtils; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.lib.resources.shares.OCShare; +import com.owncloud.android.lib.resources.shares.RemoveRemoteShareOperation; +import com.owncloud.android.lib.resources.shares.ShareType; +import com.owncloud.android.lib.resources.shares.UpdateRemoteShareOperation; +import com.owncloud.android.lib.test_project.R; +import com.owncloud.android.lib.test_project.SelfSignedConfidentSslSocketFactory; +import com.owncloud.android.lib.test_project.TestActivity; + +/** + * Class to test UpdateRemoteShareOperation + * + */ +public class UpdateShareTest extends RemoteTest { + private static final String LOG_TAG = UpdateShareTest.class.getCanonicalName(); + + /* File to share and update.*/ + private static final String FILE_TO_SHARE = "/fileToShare.txt"; + + // Data for tests + private static final String PASSWORD = "password"; + private static final String PASS_SPECIAL_CHARS = "p@sswórd"; + + private String mFullPath2FileToShare; + + private OCShare mShare; + + String mServerUri, mUser, mPass; + OwnCloudClient mClient = null; + + public UpdateShareTest(){ + super(); + + Protocol pr = Protocol.getProtocol("https"); + if (pr == null || !(pr.getSocketFactory() instanceof SelfSignedConfidentSslSocketFactory)) { + try { + ProtocolSocketFactory psf = new SelfSignedConfidentSslSocketFactory(); + Protocol.registerProtocol( + "https", + new Protocol("https", psf, 443)); + + } catch (GeneralSecurityException e) { + throw new AssertionFailedError( + "Self-signed confident SSL context could not be loaded"); + } + } + } + + protected Context getContext() { + return getActivity(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + // Next initialization cannot be done in the constructor because getContext() is not + // ready yet, returns NULL. + initAccessToServer(getContext()); + + Log.v(LOG_TAG, "Setting up the remote fixture..."); + + // Upload the files + mFullPath2FileToShare = mBaseFolderPath + FILE_TO_SHARE; + + File textFile = getActivity().extractAsset(TestActivity.ASSETS__TEXT_FILE_NAME); + RemoteOperationResult result = getActivity().uploadFile( + textFile.getAbsolutePath(), + mFullPath2FileToShare, + "txt/plain"); + if (!result.isSuccess()) { + Utils.logAndThrow(LOG_TAG, result); + } + + // Share the file + result = getActivity().createShare( + mFullPath2FileToShare, + ShareType.PUBLIC_LINK, + "", + false, + "", + 1); + + if (result.isSuccess()){ + mShare = (OCShare) result.getData().get(0); + } else{ + mShare = null; + } + + Log.v(LOG_TAG, "Remote fixture created."); + + } + + + public void testUpdateRemoteShareOperation() { + Log.v(LOG_TAG, "testUpdateShare in"); + + if (mShare != null) { + // successful tests + // Update Share with password + UpdateRemoteShareOperation updateShare = new UpdateRemoteShareOperation(mShare.getRemoteId()); + updateShare.setPassword(PASSWORD); + RemoteOperationResult result = updateShare.execute(mClient); + assertTrue(result.isSuccess()); + + // Update Share with password with special characters + updateShare = new UpdateRemoteShareOperation(mShare.getRemoteId()); + updateShare.setPassword(PASS_SPECIAL_CHARS); + result = updateShare.execute(mClient); + assertTrue(result.isSuccess()); + + // Update Share with expiration date + updateShare = new UpdateRemoteShareOperation(mShare.getRemoteId()); + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DAY_OF_MONTH, 7); + long expirationDateInMillis = calendar.getTimeInMillis() ; + updateShare.setExpirationDate(expirationDateInMillis); + result = updateShare.execute(mClient); + assertTrue(result.isSuccess()); + + // unsuccessful test + // Update Share with expiration date in the past + updateShare = new UpdateRemoteShareOperation(mShare.getRemoteId()); + calendar.set(Calendar.YEAR, 2014); + expirationDateInMillis = calendar.getTimeInMillis() ; + updateShare.setExpirationDate(expirationDateInMillis); + result = updateShare.execute(mClient); + assertFalse(result.isSuccess()); + + // Unshare the file before the unsuccessful tests + RemoveRemoteShareOperation unshare = new RemoveRemoteShareOperation((int) mShare.getRemoteId()); + result = unshare.execute(mClient); + + if (result.isSuccess()) { + // Update Share with password on unknown share + UpdateRemoteShareOperation updateNoShare = new UpdateRemoteShareOperation(mShare.getRemoteId()); + updateNoShare.setPassword(PASSWORD); + result = updateNoShare.execute(mClient); + assertFalse(result.isSuccess()); + + // Update Share with expiration date on unknown share + updateNoShare = new UpdateRemoteShareOperation(mShare.getRemoteId()); + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DAY_OF_MONTH, 7); + expirationDateInMillis = cal.getTimeInMillis() ; + updateNoShare.setExpirationDate(expirationDateInMillis); + result = updateNoShare.execute(mClient); + assertFalse(result.isSuccess()); + + } + + } + + } + + @Override + protected void tearDown() throws Exception { + Log.v(LOG_TAG, "Deleting remote fixture..."); + if (mFullPath2FileToShare != null) { + RemoteOperationResult removeResult = getActivity().removeFile(mFullPath2FileToShare); + if (!removeResult.isSuccess() && removeResult.getCode() != ResultCode.TIMEOUT) { + Utils.logAndThrow(LOG_TAG, removeResult); + } + } + super.tearDown(); + Log.v(LOG_TAG, "Remote fixture delete."); + } + + + private void initAccessToServer(Context context) { + Log.v(LOG_TAG, "Setting up client instance to access OC server..."); + + mServerUri = context.getString(R.string.server_base_url); + mUser = context.getString(R.string.username); + mPass = context.getString(R.string.password); + + mClient = new OwnCloudClient( + Uri.parse(mServerUri), + NetworkUtils.getMultiThreadedConnManager() + ); + mClient.setDefaultTimeouts( + OwnCloudClientFactory.DEFAULT_DATA_TIMEOUT, + OwnCloudClientFactory.DEFAULT_CONNECTION_TIMEOUT); + mClient.setFollowRedirects(true); + mClient.setCredentials( + OwnCloudCredentialsFactory.newBasicCredentials( + mUser, + mPass + ) + ); + + Log.v(LOG_TAG, "Client instance set up."); + + } + +}