Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #15621. #15911

Merged
merged 2 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions defaults/src/main/resources/default.properties
Original file line number Diff line number Diff line change
Expand Up @@ -535,8 +535,9 @@ webdav.microsoftiis.header.translate=true
webdav.list.handler.sax=true
webdav.lock.enable=true
webdav.listing.chunksize=20
nextcloud.root.default=/remote.php/dav
owncloud.root.default=/remote.php/dav
nextcloud.root.webdav.default=remote.php/webdav
nextcloud.root.webdav.user=remote.php/dav/{0}/{1}
nextcloud.root.ocs=ocs/v1.php

smb.domain.default=WORKGROUP
# Enable distributed filesystem path resolver
Expand Down Expand Up @@ -644,7 +645,6 @@ sftp.read.maxunconfirmed=64
sftp.write.maxunconfirmed=64
sftp.write.chunksize=32768
sftp.permissions.server.blacklist=OpenSSH_for_Windows
sftp.listing.chunksize=20

archive.default=tar.gz

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,76 +17,161 @@

import ch.cyberduck.core.Host;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathNormalizer;
import ch.cyberduck.core.PathRelativizer;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Home;
import ch.cyberduck.core.preferences.HostPreferences;
import ch.cyberduck.core.shared.AbstractHomeFeature;
import ch.cyberduck.core.shared.DefaultPathHomeFeature;
import ch.cyberduck.core.shared.DelegatingHomeFeature;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.text.MessageFormat;
import java.util.Arrays;
import java.util.EnumSet;

public class NextcloudHomeFeature extends AbstractHomeFeature {
private static final Logger log = LogManager.getLogger(NextcloudHomeFeature.class);

private final Home delegate;
private final Host bookmark;
private final String root;

public NextcloudHomeFeature(final Host bookmark) {
this(new DefaultPathHomeFeature(bookmark), bookmark);
}

public NextcloudHomeFeature(final Home delegate, final Host bookmark) {
this(delegate, bookmark, new HostPreferences(bookmark).getProperty("nextcloud.root.default"));
}

/**
* @param root WebDAV root
*/
public NextcloudHomeFeature(final Home delegate, final Host bookmark, final String root) {
this.delegate = delegate;
this.bookmark = bookmark;
this.root = root;
}

@Override
public Path find() throws BackgroundException {
return this.find(Context.files);
}

public Path find(final Context files) throws BackgroundException {
final String username = bookmark.getCredentials().getUsername();
if(StringUtils.isBlank(username)) {
if(log.isWarnEnabled()) {
log.warn(String.format("Missing username for %s", bookmark));
public <T> Path find(final Context context) throws BackgroundException {
return context.workdir(bookmark).find();
}

public enum Context {
ocs {
@Override
public Home home(final Host bookmark) {
return () -> new Path(new HostPreferences(bookmark).getProperty("nextcloud.root.ocs"), EnumSet.of(Path.Type.directory));
}
},
files {
@Override
public Home home(final Host bookmark) throws BackgroundException {
return new DelegatingHomeFeature(new UserDavRoot(bookmark, this), new DefaultDavRoot(bookmark));
}

@Override
public Home workdir(final Host bookmark) throws BackgroundException {
final Path workdir = super.workdir(bookmark).find();
return new DelegatingHomeFeature(new DefaultPathSuffix(bookmark, workdir), () -> workdir);
}
return delegate.find();
},
versions,
meta;

public Home home(final Host bookmark) throws BackgroundException {
return new UserDavRoot(bookmark, this);
}
// Custom path setting
final Path workdir;
final Path defaultpath = new DelegatingHomeFeature(delegate).find();
if(!defaultpath.isRoot() && StringUtils.isNotBlank(StringUtils.removeStart(defaultpath.getAbsolute(), root))) {
workdir = new Path(new Path(String.format("%s/%s/%s", root, files.name(), username), EnumSet.of(Path.Type.directory)),
StringUtils.removeStart(defaultpath.getAbsolute(), root), EnumSet.of(Path.Type.directory));

public Home workdir(final Host bookmark) throws BackgroundException {
final Path home = this.home(bookmark).find();
return new DelegatingHomeFeature(new WebDavRootPrefix(bookmark, home), () -> home);
}
else {
workdir = new Path(new Path(String.format("%s/%s", root, files.name()), EnumSet.of(Path.Type.directory)),
username, EnumSet.of(Path.Type.directory));
}

private static final class UserDavRoot implements Home {
private final Host bookmark;
private final Context context;

public UserDavRoot(final Host bookmark, final Context context) {
this.bookmark = bookmark;
this.context = context;
}
if(log.isDebugEnabled()) {
log.debug(String.format("Use home directory %s", workdir));

@Override
public Path find() {
final String username = bookmark.getCredentials().getUsername();
if(StringUtils.isBlank(username)) {
return null;
}
return new Path(MessageFormat.format(
new HostPreferences(bookmark).getProperty("nextcloud.root.webdav.user"), context.name(), username), EnumSet.of(Path.Type.directory));
}
return workdir;
}

public enum Context {
files,
versions,
meta
private static final class DefaultDavRoot implements Home {
private final Host bookmark;

public DefaultDavRoot(final Host bookmark) {
this.bookmark = bookmark;
}

@Override
public Path find() {
return new Path(new HostPreferences(bookmark).getProperty("nextcloud.root.webdav.default"), EnumSet.of(Path.Type.directory));
}
}

private static final class WebDavRootPrefix implements Home {
private final Host bookmark;
private final Path home;

public WebDavRootPrefix(final Host bookmark, final Path home) {
this.bookmark = bookmark;
this.home = home;
}

@Override
public Path find() {
if(StringUtils.isNotBlank(bookmark.getDefaultPath())) {
for(String s : Arrays.asList(home.getAbsolute(), new HostPreferences(bookmark).getProperty("nextcloud.root.webdav.default"),
MessageFormat.format(new HostPreferences(bookmark).getProperty("nextcloud.root.webdav.user"), Context.files.name(),
bookmark.getCredentials().getUsername()))) {
if(StringUtils.contains(bookmark.getDefaultPath(), PathNormalizer.normalize(s))) {
final String prefix = StringUtils.substringBefore(bookmark.getDefaultPath(), PathNormalizer.normalize(s));
if(StringUtils.isBlank((prefix))) {
return null;
}
return PathNormalizer.compose(new Path(prefix, EnumSet.of(Path.Type.directory)),
PathRelativizer.relativize(String.valueOf(Path.DELIMITER), home.getAbsolute()));
}
}
}
return null;
}
}

private static final class DefaultPathSuffix implements Home {
private final Host bookmark;
private final Path home;

public DefaultPathSuffix(final Host bookmark, final Path home) {
this.bookmark = bookmark;
this.home = home;
}

@Override
public Path find() {
if(StringUtils.isNotBlank(bookmark.getDefaultPath())) {
for(String s : Arrays.asList(home.getAbsolute(), new HostPreferences(bookmark).getProperty("nextcloud.root.webdav.default"),
MessageFormat.format(new HostPreferences(bookmark).getProperty("nextcloud.root.webdav.user"), Context.files.name(),
bookmark.getCredentials().getUsername()))) {
if(StringUtils.contains(bookmark.getDefaultPath(), s)) {
final String suffix = StringUtils.substringAfter(bookmark.getDefaultPath(), s);
if(StringUtils.isBlank((suffix))) {
return null;
}
return PathNormalizer.compose(home, PathRelativizer.relativize(String.valueOf(Path.DELIMITER), suffix));
}
}
return PathNormalizer.compose(home, PathRelativizer.relativize(String.valueOf(Path.DELIMITER),
PathNormalizer.normalize(bookmark.getDefaultPath())));
}
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ public Set<Sharee> getSharees(final Type type) throws BackgroundException {
return Collections.singleton(Sharee.world);
default:
final Host bookmark = session.getHost();
final StringBuilder request = new StringBuilder(String.format("https://%s/ocs/v1.php/apps/files_sharing/api/v1/sharees?lookup=true&shareType=%d&itemType=file",
bookmark.getHostname(),
final StringBuilder request = new StringBuilder(String.format("https://%s%s/apps/files_sharing/api/v1/sharees?lookup=true&shareType=%d&itemType=file",
bookmark.getHostname(), new NextcloudHomeFeature(bookmark).find(NextcloudHomeFeature.Context.ocs).getAbsolute(),
SHARE_TYPE_USER // User
));
final HttpGet resource = new HttpGet(request.toString());
Expand Down Expand Up @@ -120,10 +120,9 @@ public Set<Sharee> getSharees(final Type type) throws BackgroundException {
@Override
public DescriptiveUrl toDownloadUrl(final Path file, final Sharee sharee, final Object options, final PasswordCallback callback) throws BackgroundException {
final Host bookmark = session.getHost();
final StringBuilder request = new StringBuilder(String.format("https://%s/ocs/v1.php/apps/files_sharing/api/v1/shares?path=%s&shareType=%d&shareWith=%s",
bookmark.getHostname(),
URIEncoder.encode(PathRelativizer.relativize(bookmark.getProtocol().getDefaultPath(),
PathRelativizer.relativize(new NextcloudHomeFeature(bookmark).find().getAbsolute(), file.getAbsolute()))),
final StringBuilder request = new StringBuilder(String.format("https://%s%s/apps/files_sharing/api/v1/shares?path=%s&shareType=%d&shareWith=%s",
bookmark.getHostname(), new NextcloudHomeFeature(bookmark).find(NextcloudHomeFeature.Context.ocs).getAbsolute(),
URIEncoder.encode(PathRelativizer.relativize(NextcloudHomeFeature.Context.files.home(bookmark).find().getAbsolute(), file.getAbsolute())),
Sharee.world.equals(sharee) ? SHARE_TYPE_PUBLIC_LINK : SHARE_TYPE_USER,
Sharee.world.equals(sharee) ? StringUtils.EMPTY : sharee.getIdentifier()
));
Expand Down Expand Up @@ -152,10 +151,9 @@ public DescriptiveUrl toDownloadUrl(final Path file, final Sharee sharee, final
@Override
public DescriptiveUrl toUploadUrl(final Path file, final Sharee sharee, final Object options, final PasswordCallback callback) throws BackgroundException {
final Host bookmark = session.getHost();
final StringBuilder request = new StringBuilder(String.format("https://%s/ocs/v1.php/apps/files_sharing/api/v1/shares?path=%s&shareType=%d&permissions=%d",
bookmark.getHostname(),
URIEncoder.encode(PathRelativizer.relativize(bookmark.getProtocol().getDefaultPath(),
PathRelativizer.relativize(new NextcloudHomeFeature(bookmark).find().getAbsolute(), file.getAbsolute()))),
final StringBuilder request = new StringBuilder(String.format("https://%s%s/apps/files_sharing/api/v1/shares?path=%s&shareType=%d&permissions=%d",
bookmark.getHostname(), new NextcloudHomeFeature(bookmark).find(NextcloudHomeFeature.Context.ocs).getAbsolute(),
URIEncoder.encode(PathRelativizer.relativize(NextcloudHomeFeature.Context.files.home(bookmark).find().getAbsolute(), file.getAbsolute())),
Sharee.world.equals(sharee) ? SHARE_TYPE_PUBLIC_LINK : SHARE_TYPE_USER,
SHARE_PERMISSIONS_CREATE
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,18 @@
*/

import ch.cyberduck.core.Host;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.nextcloud.NextcloudHomeFeature;

import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.ContentType;

public class OcsCapabilitiesRequest extends HttpGet {

public OcsCapabilitiesRequest(final Host host) {
super(new StringBuilder(String.format("https://%s/ocs/v1.php/cloud/capabilities",
host.getHostname()
public OcsCapabilitiesRequest(final Host host) throws BackgroundException {
super(new StringBuilder(String.format("https://%s%s/cloud/capabilities",
host.getHostname(), new NextcloudHomeFeature(host).find(NextcloudHomeFeature.Context.ocs).getAbsolute()
)).toString());
this.setHeader("OCS-APIRequest", "true");
this.setHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_XML.getMimeType());
Expand Down
Loading