Skip to content

Commit

Permalink
Merge pull request #15911 from iterate-ch/bugfix/GH-15621
Browse files Browse the repository at this point in the history
Fix #15621.
  • Loading branch information
dkocher authored May 6, 2024
2 parents 90e7282 + da16a60 commit a2e1792
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 94 deletions.
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

0 comments on commit a2e1792

Please sign in to comment.