diff --git a/app/src/main/java/io/xpipe/app/storage/ContextualFileReference.java b/app/src/main/java/io/xpipe/app/storage/ContextualFileReference.java index 2688f935d..e4d203cd0 100644 --- a/app/src/main/java/io/xpipe/app/storage/ContextualFileReference.java +++ b/app/src/main/java/io/xpipe/app/storage/ContextualFileReference.java @@ -76,7 +76,7 @@ public class ContextualFileReference { var start = getDataDir(); var normalizedPath = FilePath.of(path).normalize().toUnix(); if (normalizedPath.startsWith(start) && !normalizedPath.equals(start)) { - return "" + "/" + start.relativize(normalizedPath); + return "" + "/" + normalizedPath.relativize(start); } return path; } diff --git a/app/src/main/java/io/xpipe/app/storage/DataStorageNode.java b/app/src/main/java/io/xpipe/app/storage/DataStorageNode.java index 311489018..a5d2bd984 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorageNode.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorageNode.java @@ -133,11 +133,17 @@ public class DataStorageNode { } public boolean hasAccess() { - return !perUser || availableForUser; + // In this case the loading failed + // We have access to it, we just can't read it + if (!perUser && !readableForUser) { + return true; + } + + return !perUser || readableForUser; } JsonNode contentNode; boolean perUser; - boolean availableForUser; + boolean readableForUser; boolean encrypted; } diff --git a/app/src/main/java/io/xpipe/app/storage/DataStoreEntry.java b/app/src/main/java/io/xpipe/app/storage/DataStoreEntry.java index d552aefdd..d248fd2c2 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStoreEntry.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStoreEntry.java @@ -1,5 +1,6 @@ package io.xpipe.app.storage; +import com.fasterxml.jackson.core.JacksonException; import io.xpipe.app.ext.DataStoreProvider; import io.xpipe.app.ext.DataStoreProviders; import io.xpipe.app.ext.UserScopeStore; @@ -285,8 +286,15 @@ public class DataStoreEntry extends StorageElement { notes = null; } - var fileNode = mapper.readTree(storeFile.toFile()); - var node = DataStorageNode.readPossiblyEncryptedNode(fileNode); + DataStorageNode node = null; + try { + var fileNode = mapper.readTree(storeFile.toFile()); + node = DataStorageNode.readPossiblyEncryptedNode(fileNode); + } catch (JacksonException ex) { + ErrorEvent.fromThrowable(ex).omit().expected().handle(); + node = DataStorageNode.fail(); + } + var store = node.parseStore(); return Optional.of(new DataStoreEntry( dir, @@ -481,7 +489,7 @@ public class DataStoreEntry extends StorageElement { var notesNode = JsonNodeFactory.instance.objectNode(); notesNode.put("markdown", notes); var storageNode = DataStorageNode.encryptNodeIfNeeded(new DataStorageNode( - notesNode, storeNode.isPerUser(), storeNode.isAvailableForUser(), storeNode.isEncrypted())); + notesNode, storeNode.isPerUser(), storeNode.isReadableForUser(), storeNode.isEncrypted())); var string = mapper.writeValueAsString(storageNode); Files.writeString(encryptedNotesFile, string); } else if (notes != null) { diff --git a/app/src/main/java/io/xpipe/app/storage/StandardStorage.java b/app/src/main/java/io/xpipe/app/storage/StandardStorage.java index 4d7266f37..f25ae70fb 100644 --- a/app/src/main/java/io/xpipe/app/storage/StandardStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/StandardStorage.java @@ -161,16 +161,6 @@ public class StandardStorage extends DataStorage { } storeEntries.put(entry.get(), entry.get()); - } catch (JacksonException ex) { - // Data corruption and schema changes are expected - - // We only keep invalid entries in developer mode as there's no point in keeping them in - // production. - if (AppPrefs.get().isDevelopmentEnvironment()) { - directoriesToKeep.add(path); - } - - ErrorEvent.fromThrowable(ex).expected().omit().build().handle(); } catch (IOException ex) { // IO exceptions are not expected exception.set(new IOException("Unable to load data from " + path + ". Is it corrupted?", ex)); diff --git a/app/src/main/java/io/xpipe/app/util/AppJacksonModule.java b/app/src/main/java/io/xpipe/app/util/AppJacksonModule.java index f2f141b37..211f04cc3 100644 --- a/app/src/main/java/io/xpipe/app/util/AppJacksonModule.java +++ b/app/src/main/java/io/xpipe/app/util/AppJacksonModule.java @@ -238,7 +238,7 @@ public class AppJacksonModule extends SimpleModule { var e = DataStorage.get() .getStoreEntryIfPresent(id) .filter(dataStoreEntry -> dataStoreEntry.getValidity() != DataStoreEntry.Validity.LOAD_FAILED - || !dataStoreEntry.getStoreNode().isAvailableForUser()) + || !dataStoreEntry.getStoreNode().isReadableForUser()) .orElse(null); if (e == null) { return null; diff --git a/app/src/main/java/io/xpipe/app/util/SshLocalBridge.java b/app/src/main/java/io/xpipe/app/util/SshLocalBridge.java index 1d13bbfd3..829c32599 100644 --- a/app/src/main/java/io/xpipe/app/util/SshLocalBridge.java +++ b/app/src/main/java/io/xpipe/app/util/SshLocalBridge.java @@ -204,7 +204,7 @@ public class SshLocalBridge { var exec = CommandSupport.findProgram(sc, "sshd"); if (exec.isEmpty()) { throw ErrorEvent.expected(new IllegalStateException( - "No sshd executable found in PATH. The SSH terminal bridge requires a local ssh server")); + "No sshd executable found in PATH. The SSH terminal bridge requires a local ssh server to connect to an SSH terminal")); } return exec.get(); } diff --git a/core/src/main/java/io/xpipe/core/process/ShellControl.java b/core/src/main/java/io/xpipe/core/process/ShellControl.java index 09588b778..82fc328b9 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellControl.java +++ b/core/src/main/java/io/xpipe/core/process/ShellControl.java @@ -65,7 +65,7 @@ public interface ShellControl extends ProcessControl { ShellControl withSourceStore(DataStore store); - List getInitCommands(); + List getInitCommands(); ParentSystemAccess getParentSystemAccess(); @@ -186,7 +186,7 @@ public interface ShellControl extends ProcessControl { ShellControl elevated(ElevationFunction elevationFunction); - ShellControl withInitSnippet(ShellInitCommand snippet); + ShellControl withInitSnippet(ShellTerminalInitCommand snippet); default ShellControl subShell(@NonNull ShellDialect type) { var o = new ShellOpenFunction() { diff --git a/core/src/main/java/io/xpipe/core/process/ShellDialect.java b/core/src/main/java/io/xpipe/core/process/ShellDialect.java index 747c39d0b..63bae1b96 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellDialect.java +++ b/core/src/main/java/io/xpipe/core/process/ShellDialect.java @@ -138,7 +138,7 @@ public interface ShellDialect { String runScriptCommand(ShellControl parent, String file); - String sourceScriptCommand(ShellControl parent, String file); + String sourceScriptCommand(String file); String executeCommandWithShell(String cmd); diff --git a/core/src/main/java/io/xpipe/core/process/ShellInitCommand.java b/core/src/main/java/io/xpipe/core/process/ShellInitCommand.java deleted file mode 100644 index 0d3e58796..000000000 --- a/core/src/main/java/io/xpipe/core/process/ShellInitCommand.java +++ /dev/null @@ -1,79 +0,0 @@ -package io.xpipe.core.process; - -import lombok.NonNull; - -import java.util.Optional; - -public interface ShellInitCommand { - - default void runDumb(ShellControl shellControl) throws Exception { - throw new UnsupportedOperationException(); - } - - default Optional terminalContent(ShellControl shellControl) throws Exception { - throw new UnsupportedOperationException(); - } - - default boolean runInDumb() { - return false; - } - - boolean canPotentiallyRunInDialect(ShellDialect dialect); - - default boolean runInTerminal() { - return false; - } - - interface Terminal extends ShellInitCommand { - - Optional terminalContent(ShellControl shellControl); - - default boolean runInTerminal() { - return true; - } - } - - class Simple implements ShellInitCommand { - - @NonNull - private final String content; - - private final ShellDialect dialect; - - private final boolean dumb; - - private final boolean terminal; - - public Simple(@NonNull String content, ShellDialect dialect, boolean dumb, boolean terminal) { - this.content = content; - this.dialect = dialect; - this.dumb = dumb; - this.terminal = terminal; - } - - @Override - public void runDumb(ShellControl shellControl) throws Exception { - shellControl.executeSimpleCommand(content); - } - - @Override - public Optional terminalContent(ShellControl shellControl) { - return Optional.of(content); - } - - @Override - public boolean runInDumb() { - return dumb; - } - - @Override - public boolean canPotentiallyRunInDialect(ShellDialect dialect) { - return this.dialect == null || this.dialect.isCompatibleTo(dialect); - } - - @Override - public boolean runInTerminal() { - return terminal; - } - } -} diff --git a/core/src/main/java/io/xpipe/core/process/ShellTerminalInitCommand.java b/core/src/main/java/io/xpipe/core/process/ShellTerminalInitCommand.java new file mode 100644 index 000000000..c3541ff11 --- /dev/null +++ b/core/src/main/java/io/xpipe/core/process/ShellTerminalInitCommand.java @@ -0,0 +1,40 @@ +package io.xpipe.core.process; + +import lombok.NonNull; + +import java.util.Optional; + +public interface ShellTerminalInitCommand { + + boolean isStatic(); + + Optional content(ShellControl sc); + + boolean canPotentiallyRunInDialect(ShellDialect dialect); + + class Static implements ShellTerminalInitCommand { + + private final String content; + private final ShellDialect dialect; + + public Static(String content, ShellDialect dialect) { + this.content = content; + this.dialect = dialect; + } + + @Override + public boolean isStatic() { + return true; + } + + @Override + public Optional content(ShellControl sc) { + return Optional.of(content); + } + + @Override + public boolean canPotentiallyRunInDialect(ShellDialect dialect) { + return this.dialect == null || this.dialect.isCompatibleTo(dialect); + } + } +} diff --git a/core/src/main/java/io/xpipe/core/process/WrapperShellControl.java b/core/src/main/java/io/xpipe/core/process/WrapperShellControl.java index a8a843008..646ed3359 100644 --- a/core/src/main/java/io/xpipe/core/process/WrapperShellControl.java +++ b/core/src/main/java/io/xpipe/core/process/WrapperShellControl.java @@ -95,7 +95,7 @@ public class WrapperShellControl implements ShellControl { } @Override - public List getInitCommands() { + public List getInitCommands() { return parent.getInitCommands(); } @@ -334,7 +334,7 @@ public class WrapperShellControl implements ShellControl { } @Override - public ShellControl withInitSnippet(ShellInitCommand snippet) { + public ShellControl withInitSnippet(ShellTerminalInitCommand snippet) { return parent.withInitSnippet(snippet); } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStrategy.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStrategy.java index 8773e4400..f34be6a0b 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStrategy.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStrategy.java @@ -1,6 +1,8 @@ package io.xpipe.ext.base.identity; +import io.xpipe.app.core.AppProperties; import io.xpipe.app.issue.ErrorEvent; +import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.storage.ContextualFileReference; import io.xpipe.app.storage.DataStorage; import io.xpipe.app.util.SecretRetrievalStrategy; @@ -176,7 +178,7 @@ public interface SshIdentityStrategy { @Override public boolean isConnectionAttemptCostly() { - return false; + return password.expectsQuery() && AppPrefs.get().dontCachePasswords().get(); } public void checkComplete() throws ValidationException { diff --git a/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptStoreSetup.java b/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptStoreSetup.java index 762f1a27b..711dde694 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptStoreSetup.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptStoreSetup.java @@ -8,13 +8,12 @@ import io.xpipe.app.storage.DataStoreEntryRef; import io.xpipe.app.util.ShellTemp; import io.xpipe.core.process.ShellControl; import io.xpipe.core.process.ShellDialect; -import io.xpipe.core.process.ShellInitCommand; +import io.xpipe.core.process.ShellTerminalInitCommand; import io.xpipe.core.store.FileNames; import io.xpipe.core.store.FilePath; import lombok.Value; import java.util.*; -import java.util.stream.Collectors; public class ScriptStoreSetup { @@ -72,7 +71,7 @@ public class ScriptStoreSetup { var source = pc.getSourceStoreId(); if (source.isPresent()) { - var useCached = pc.getSourceStore().map(dataStore -> dataStore instanceof ShellStore s && s.isConnectionAttemptCostly()).orElse(false); + var useCached = true || pc.getSourceStore().map(dataStore -> dataStore instanceof ShellStore s && s.isConnectionAttemptCostly()).orElse(false); if (useCached) { var cached = hasGeneratedScriptsCached(source.get(), initFlattened) && hasGeneratedScriptsCached(source.get(), bringFlattened); if (cached) { @@ -81,11 +80,22 @@ public class ScriptStoreSetup { } } - initFlattened.forEach(simpleScriptStore -> { - pc.withInitSnippet(simpleScriptStore.getStore()); + initFlattened.forEach(s -> { + pc.withInitSnippet(new ShellTerminalInitCommand.Static(s.getStore().)) + pc.withInitSnippet(new ShellTerminalInitCommand() { + @Override + public Optional terminalContent(ShellControl shellControl) { + return Optional.ofNullable(s.getStore().assembleScriptChain(shellControl)); + } + + @Override + public boolean canPotentiallyRunInDialect(ShellDialect dialect) { + return s.getStore().getMinimumDialect().isCompatibleTo(dialect); + } + }); }); if (!bringFlattened.isEmpty() || source.isPresent()) { - pc.withInitSnippet(new ShellInitCommand() { + pc.withInitSnippet(new ShellTerminalInitCommand.Terminal() { String dir; @@ -116,11 +126,6 @@ public class ScriptStoreSetup { public boolean canPotentiallyRunInDialect(ShellDialect dialect) { return true; } - - @Override - public boolean runInTerminal() { - return true; - } }); } return pc; diff --git a/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStore.java b/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStore.java index 03dd49ef7..024a69418 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStore.java @@ -6,7 +6,6 @@ import io.xpipe.app.util.ScriptHelper; import io.xpipe.app.util.Validators; import io.xpipe.core.process.ShellControl; import io.xpipe.core.process.ShellDialect; -import io.xpipe.core.process.ShellInitCommand; import io.xpipe.core.util.ValidationException; import io.xpipe.ext.base.SelfReferentialStore; @@ -20,7 +19,6 @@ import lombok.extern.jackson.Jacksonized; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.stream.Collectors; @SuperBuilder(toBuilder = true) @@ -29,7 +27,7 @@ import java.util.stream.Collectors; @JsonTypeName("script") @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class SimpleScriptStore extends ScriptStore implements ShellInitCommand.Terminal, SelfReferentialStore { +public class SimpleScriptStore extends ScriptStore implements SelfReferentialStore { ShellDialect minimumDialect; String commands; @@ -62,7 +60,7 @@ public class SimpleScriptStore extends ScriptStore implements ShellInitCommand.T shellControl.getShellDialect().getNewLine().getNewLineString())); var targetType = shellControl.getOriginalShellDialect(); var script = ScriptHelper.createExecScript(targetType, shellControl, fixedCommands); - return targetType.sourceScriptCommand(shellControl, script.toString()); + return targetType.sourceScriptCommand(script.toString()); } return null; @@ -115,14 +113,4 @@ public class SimpleScriptStore extends ScriptStore implements ShellInitCommand.T .toList() : List.of(); } - - @Override - public Optional terminalContent(ShellControl shellControl) { - return Optional.ofNullable(assembleScriptChain(shellControl)); - } - - @Override - public boolean canPotentiallyRunInDialect(ShellDialect dialect) { - return this.minimumDialect.isCompatibleTo(dialect); - } }