Skip to content

Commit

Permalink
[apacheGH-322] Converted internal hardwired assumptions to user-contr…
Browse files Browse the repository at this point in the history
…olled ones
  • Loading branch information
Lyor Goldstein committed Mar 23, 2023
1 parent a87697b commit 6697b3d
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ public SftpCommandMain(SftpClient client) {
ValidateUtils.checkTrue(map.put(name, e) == null, "Multiple commands named '%s'", name);
}
commandsMap = Collections.unmodifiableMap(map);
cwdLocal = System.getProperty("user.dir");

Path cwdPath = OsUtils.getCurrentWorkingDirectory();
cwdLocal = Objects.toString(cwdPath, null);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,22 @@ public static Throwable peelException(Throwable t) {
if (target != null) {
return peelException(target);
}
} else if (t instanceof ReflectionException) {
Throwable target = ((ReflectionException) t).getTargetException();
if (target != null) {
return peelException(target);
}
} else if (t instanceof ExecutionException) {
return peelException(resolveExceptionCause(t));
} else if (t instanceof MBeanException) {
Throwable target = ((MBeanException) t).getTargetException();
if (target != null) {
return peelException(target);
}

// Android does not have these classes
if (!OsUtils.isAndroid()) {
if (t instanceof ReflectionException) {
Throwable target = ((ReflectionException) t).getTargetException();
if (target != null) {
return peelException(target);
}
} else if (t instanceof MBeanException) {
Throwable target = ((MBeanException) t).getTargetException();
if (target != null) {
return peelException(target);
}
}
}

Expand Down Expand Up @@ -155,5 +160,4 @@ public static RuntimeException toRuntimeException(Throwable t, boolean peelThrow
public static RuntimeException toRuntimeException(Throwable t) {
return toRuntimeException(t, true);
}

}
178 changes: 176 additions & 2 deletions sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@
*/
package org.apache.sshd.common.util;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
* Operating system dependent utility methods.
Expand All @@ -48,6 +53,22 @@ public final class OsUtils {
*/
public static final String OS_TYPE_OVERRIDE_PROP = "org.apache.sshd.osType";

/**
* Property that can be used to override the reported value from {@link #isAndroid()}. If not set then
* {@link #ANDROID_DETECTION_PROPERTIES} are used to determine its value. Otherwise, it must contain
* the string "android" (case-insensitive)
*
* @see #ANDROID_PROPERTY_VALUE_MATCHER
*/
public static final String ANDROID_MODE_OVERRIDE_PROP = "org.apache.sshd.androidMode";

/**
* Property that can be used to override the reported value from {@link #isDalvikMachine()}. If not set then
* {@link #DALVIK_DETECTION_PROPERTIES} are used to determine its value. Otherwise, it must contain the
* string "dalvik" (case-insensitive)
*/
public static final String DALVIK_MACHINE_OVERRIDE_PROP = "org.apache.sshd.dalvikMachine";

public static final String WINDOWS_SHELL_COMMAND_NAME = "cmd.exe";
public static final String LINUX_SHELL_COMMAND_NAME = "/bin/sh";

Expand All @@ -58,16 +79,101 @@ public final class OsUtils {
public static final List<String> WINDOWS_COMMAND
= Collections.unmodifiableList(Collections.singletonList(WINDOWS_SHELL_COMMAND_NAME));

/**
* System properties consulted in order to detect {@link #isAndroid() Android O/S}.
*
* @see <A HREF="https://developer.android.com/reference/java/lang/System#getProperties()">Android Developer</A>
*/
public static final List<String> ANDROID_DETECTION_PROPERTIES
= Collections.unmodifiableList(
Arrays.asList(
"java.vendor",
"java.specification.vendor",
"java.vm.vendor",
"java.vm.specification.vendor"));

public static final Predicate<String> ANDROID_PROPERTY_VALUE_MATCHER
= v -> GenericUtils.trimToEmpty(v).toLowerCase().contains("android");

/**
* System properties consulted in order to detect {@link #isDalvikMachine() Dalvik machine}.
*
* @see <A HREF="https://developer.android.com/reference/java/lang/System#getProperties()">Android Developer</A>
*/
public static final List<String> DALVIK_DETECTION_PROPERTIES
= Collections.unmodifiableList(
Arrays.asList(
"java.specification.name",
"java.vm.name",
"java.vm.specification.name"));

public static final Predicate<String> DALVIK_PROPERTY_VALUE_MATCHER
= v -> GenericUtils.trimToEmpty(v).toLowerCase().contains("dalvik");

private static final AtomicReference<String> CURRENT_USER_HOLDER = new AtomicReference<>(null);
private static final AtomicReference<VersionInfo> JAVA_VERSION_HOLDER = new AtomicReference<>(null);
private static final AtomicReference<String> OS_TYPE_HOLDER = new AtomicReference<>(null);

private static final AtomicReference<Boolean> ANDROID_HOLDER = new AtomicReference<>(null);
private static final AtomicReference<Boolean> DALVIK_HOLDER = new AtomicReference<>(null);

private static final AtomicReference<Supplier<? extends Path>> CWD_PROVIDER_HOLDER = new AtomicReference<>();

private OsUtils() {
throw new UnsupportedOperationException("No instance allowed");
}

/**
* @return true if the host is a UNIX system (and not Windows).
* @return {@code true} if currently running on Android. <U>Note:</U> {@link #isUNIX()} is also
* probably {@code true} as well, so special care must be taken in code that consults these values
* @see #ANDROID_DETECTION_PROPERTIES
* @see #ANDROID_MODE_OVERRIDE_PROP
* @see #ANDROID_PROPERTY_VALUE_MATCHER
*/
public static boolean isAndroid() {
return resolveAndroidSettingFlag(
ANDROID_HOLDER, ANDROID_MODE_OVERRIDE_PROP, ANDROID_DETECTION_PROPERTIES, ANDROID_PROPERTY_VALUE_MATCHER);
}

/**
* Override the value returned by {@link #isAndroid()} programmatically
*
* @param value Value to set if {@code null} then value is auto-detected
*/
public static void setAndroid(Boolean value) {
synchronized (ANDROID_HOLDER) {
ANDROID_HOLDER.set(value);
}
}

/**
* @return {@code true} if currently running on a Dalvik machine. <U>Note:</U> {@link #isUNIX()}
* and/or {@link #isAndroid()} are also probably {@code true} as well, so special care must be taken in
* code that consults these values
* @see #DALVIK_DETECTION_PROPERTIES
* @see #DALVIK_MACHINE_OVERRIDE_PROP
* @see #DALVIK_PROPERTY_VALUE_MATCHER
*/
public static boolean isDalvikMachine() {
return resolveAndroidSettingFlag(
DALVIK_HOLDER, DALVIK_MACHINE_OVERRIDE_PROP, DALVIK_DETECTION_PROPERTIES, DALVIK_PROPERTY_VALUE_MATCHER);
}

/**
* Override the value returned by {@link #isDalvikMachine()} programmatically
*
* @param value Value to set if {@code null} then value is auto-detected
*/
public static void setDalvikMachine(Boolean value) {
synchronized (DALVIK_HOLDER) {
DALVIK_HOLDER.set(value);
}
}

/**
* @return true if the host is a UNIX system (and not Windows). <U>Note:</U> this
* does <B>not</B> preclude {@link #isAndroid()} or {@link #isDalvikMachine()} from
* being {@code true} as well.
*/
public static boolean isUNIX() {
return !isWin32() && !isOSX();
Expand Down Expand Up @@ -103,6 +209,34 @@ public static void setOS(String os) {
}
}

private static boolean resolveAndroidSettingFlag(
AtomicReference<Boolean> flagHolder, String overrideProp,
Collection<String> detectionProps, Predicate<? super String> detector) {
synchronized (flagHolder) {
Boolean value = flagHolder.get();
if (value != null) {
return value.booleanValue();
}

String propValue = System.getProperty(overrideProp);
if (detector.test(propValue)) {
flagHolder.set(Boolean.TRUE);
return true;
}

for (String p : detectionProps) {
if (detector.test(propValue)) {
flagHolder.set(Boolean.TRUE);
return true;
}
}

flagHolder.set(Boolean.FALSE);
}

return false;
}

/**
* @return The resolved O/S type string if not already set (lowercase)
*/
Expand Down Expand Up @@ -142,14 +276,54 @@ public static List<String> resolveDefaultInteractiveCommandElements(boolean winO
}
}

/**
* @return The (C)urrent (W)orking (D)irectory {@link Path} - {@code null} if cannot resolve it. Resolution
* occurs as follows:
* <UL>
* <LI>
* Consult any currently registered {@link #setCurrentWorkingDirectoryResolver(Supplier) resolver}.
* </LI>
*
* <LI>
* If no resolver registered, then &quot;user.dir&quot; system property is consulted.
* </LI>
* </UL>
* @see #setCurrentWorkingDirectoryResolver(Supplier)
*/
public static Path getCurrentWorkingDirectory() {
Supplier<? extends Path> cwdProvider;
synchronized (CWD_PROVIDER_HOLDER) {
cwdProvider = CWD_PROVIDER_HOLDER.get();
}

if (cwdProvider != null) {
return cwdProvider.get();
}

String cwdLocal = System.getProperty("user.dir");
return GenericUtils.isBlank(cwdLocal) ? null : Paths.get(cwdLocal);
}

/**
* Allows the user to &quot;plug-in&quot; a resolver for the {@link #getCurrentWorkingDirectory()} method
*
* @param cwdProvider The {@link Supplier} of the (C)urrent (W)orking (D)irectory {@link Path} - if {@code null}
* then &quot;user.dir&quot; system property is consulted
*/
public static void setCurrentWorkingDirectoryResolver(Supplier<? extends Path> cwdProvider) {
synchronized (CWD_PROVIDER_HOLDER) {
CWD_PROVIDER_HOLDER.set(cwdProvider);
}
}

/**
* Get current user name
*
* @return Current user
* @see #CURRENT_USER_OVERRIDE_PROP
*/
public static String getCurrentUser() {
String username = null;
String username;
synchronized (CURRENT_USER_HOLDER) {
username = CURRENT_USER_HOLDER.get();
if (username != null) { // have we already resolved it ?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
Expand All @@ -46,6 +47,8 @@ public final class PathUtils {
public static final UnaryEquator<Path> EQ_CASE_SENSITIVE_FILENAME
= (p1, p2) -> BY_CASE_SENSITIVE_FILENAME.compare(p1, p2) == 0;

private static final AtomicReference<Path> USER_HOME_HOLDER = new AtomicReference<>();

private static final class LazyDefaultUserHomeFolderHolder {
private static final Path PATH
= Paths.get(ValidateUtils.checkNotNullAndNotEmpty(System.getProperty("user.home"), "No user home"))
Expand Down Expand Up @@ -118,10 +121,28 @@ public static String normalizePath(String path) {

/**
* @return The {@link Path} to the currently running user home
* @see #setUserHomeFolder(Path)
*/
@SuppressWarnings("synthetic-access")
public static Path getUserHomeFolder() {
return LazyDefaultUserHomeFolderHolder.PATH;
Path path;
synchronized (USER_HOME_HOLDER) {
path = USER_HOME_HOLDER.get();
}

return (path == null) ? LazyDefaultUserHomeFolderHolder.PATH : path;
}

/**
* Set the reported value from {@link #getUserHomeFolder()}
*
* @param path The {@link Path} to report - if {@code null} then &quot;user.home&quot; system
* property will be used
*/
public static void setUserHomeFolder(Path path) {
synchronized (USER_HOME_HOLDER) {
USER_HOME_HOLDER.set(path);
}
}

public static StringBuilder appendUserHome(StringBuilder sb) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.security.Provider;
import java.security.Security;

import org.apache.sshd.common.util.OsUtils;
import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar;

/**
Expand All @@ -42,7 +43,7 @@ public AndroidOpenSSLSecurityProviderRegistrar() {
public boolean isSupported() {
// Check that we are running on Android
// https://developer.android.com/reference/java/lang/System#getProperties()
return "The Android Project".equals(System.getProperty("java.specification.vendor"));
return OsUtils.isAndroid();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.apache.sshd.scp.common;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
Expand All @@ -31,7 +32,6 @@
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
Expand All @@ -43,6 +43,7 @@
import org.apache.sshd.common.SshException;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.OsUtils;
import org.apache.sshd.common.util.SelectorUtils;
import org.apache.sshd.common.util.io.DirectoryScanner;
import org.apache.sshd.common.util.io.IoUtils;
Expand Down Expand Up @@ -132,8 +133,10 @@ default Iterable<Path> getMatchingFilesToSend(Session session, Path basedir, Str
}

if (basedir == null) {
String cwdLocal = System.getProperty("user.dir");
Path cwdPath = Paths.get(cwdLocal);
Path cwdPath = OsUtils.getCurrentWorkingDirectory();
if (cwdPath == null) {
throw new FileNotFoundException("No CWD value available");
}
basedir = cwdPath.toAbsolutePath();
}

Expand Down

0 comments on commit 6697b3d

Please sign in to comment.