mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,6 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
- [Changelog](#changelog)
|
||||
- [0.13.0](#0130)
|
||||
- [0.12.3](#0123)
|
||||
- [0.12.2](#0122)
|
||||
- [0.12.1](#0121)
|
||||
@@ -34,6 +35,21 @@
|
||||
|
||||
---
|
||||
|
||||
## 0.13.0
|
||||
|
||||
Released on 03/03/2024
|
||||
|
||||
- Added CLI subcommands
|
||||
- Changed `-t` to `theme`
|
||||
- Changed `-u` to `update`
|
||||
- Changed `-c` to `config`
|
||||
- Introduced support for [WebDAV](https://www.rfc-editor.org/rfc/rfc4918)
|
||||
- It is now possible also to connect directly to WebDAV server with the syntax `http(s)://username:password@google.com`
|
||||
- Bugfix:
|
||||
- [Issue 232](https://github.com/veeso/termscp/issues/232): AWS S3 wasn't working anymore due to rust-s3 outdate
|
||||
- Dependencies:
|
||||
- Added `remotefs-webdav 0.1.1`
|
||||
|
||||
## 0.12.3
|
||||
|
||||
Released on 06/10/2023
|
||||
|
||||
756
Cargo.lock
generated
756
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
17
Cargo.toml
17
Cargo.toml
@@ -16,7 +16,7 @@ license = "MIT"
|
||||
name = "termscp"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/veeso/termscp"
|
||||
version = "0.12.3"
|
||||
version = "0.13.0"
|
||||
|
||||
[package.metadata.rpm]
|
||||
package = "termscp"
|
||||
@@ -47,7 +47,7 @@ edit = "^0.1"
|
||||
filetime = "^0.2"
|
||||
hostname = "^0.3"
|
||||
keyring = { version = "^2.0", optional = true }
|
||||
lazy-regex = "^2.5"
|
||||
lazy-regex = "^3.1"
|
||||
lazy_static = "^1.4"
|
||||
log = "^0.4"
|
||||
magic-crypt = "^3.1"
|
||||
@@ -56,10 +56,11 @@ notify-rust = { version = "^4.5", default-features = false, features = ["d"] }
|
||||
open = "^5.0"
|
||||
rand = "^0.8.5"
|
||||
remotefs = "^0.2.0"
|
||||
remotefs-aws-s3 = { version = "^0.2.1", default-features = false, features = [
|
||||
remotefs-aws-s3 = { version = "^0.2.4", default-features = false, features = [
|
||||
"find",
|
||||
"rustls",
|
||||
] }
|
||||
remotefs-webdav = "^0.1.1"
|
||||
rpassword = "^7.0"
|
||||
self_update = { version = "^0.37", default-features = false, features = [
|
||||
"rustls",
|
||||
@@ -73,9 +74,9 @@ simplelog = "^0.12"
|
||||
ssh2-config = "^0.2"
|
||||
tempfile = "^3.4"
|
||||
thiserror = "^1"
|
||||
toml = "^0.7"
|
||||
tui-realm-stdlib = "=1.2"
|
||||
tuirealm = "=1.8.0"
|
||||
toml = "^0.8"
|
||||
tui-realm-stdlib = "^1.3.1"
|
||||
tuirealm = "^1.9.1"
|
||||
unicode-width = "^0.1"
|
||||
version-compare = "^0.1"
|
||||
whoami = "^1.4"
|
||||
@@ -83,10 +84,10 @@ wildmatch = "^2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "^1.3"
|
||||
serial_test = "^2.0"
|
||||
serial_test = "^3"
|
||||
|
||||
[build-dependencies]
|
||||
cfg_aliases = "0.1"
|
||||
cfg_aliases = "0.2"
|
||||
|
||||
[features]
|
||||
default = ["smb", "with-keyring"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# termscp
|
||||
|
||||
<p align="center">
|
||||
<img src="/assets/images/termscp.svg" width="256" height="256" />
|
||||
<img src="/assets/images/termscp.svg" alt="termscp logo" width="256" height="256" />
|
||||
</p>
|
||||
|
||||
<p align="center">~ A feature rich terminal file transfer ~</p>
|
||||
@@ -63,7 +63,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">Developed by <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Current version: 0.12.3 (06/10/2023)</p>
|
||||
<p align="center">Current version: 0.13.0 (03/03/2024)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
@@ -73,7 +73,7 @@
|
||||
/></a>
|
||||
<a href="https://github.com/veeso/termscp/stargazers"
|
||||
><img
|
||||
src="https://img.shields.io/github/stars/veeso/termscp.svg"
|
||||
src="https://img.shields.io/github/stars/veeso/termscp?style=flat"
|
||||
alt="Repo stars"
|
||||
/></a>
|
||||
<a href="https://crates.io/crates/termscp"
|
||||
@@ -133,6 +133,7 @@ Termscp is a feature rich terminal file transfer and explorer, with support for
|
||||
- **FTP** and **FTPS**
|
||||
- **S3**
|
||||
- **SMB**
|
||||
- **WebDAV**
|
||||
- 🖥 Explore and operate on the remote and on the local machine file system with a handy UI
|
||||
- Create, remove, rename, search, view and edit files
|
||||
- ⭐ Connect to your favourite hosts through built-in bookmarks and recent connections
|
||||
|
||||
2
dist/build/linux-aarch64.sh
vendored
2
dist/build/linux-aarch64.sh
vendored
@@ -40,7 +40,7 @@ mkdir -p ${PKGS_DIR}/deb/
|
||||
mkdir -p ${PKGS_DIR}/aarch64-unknown-linux-gnu/
|
||||
docker run --name "$ARM64_DEB_NAME" -d "$ARM64_DEB_NAME" || docker start "$ARM64_DEB_NAME"
|
||||
docker exec -it "$ARM64_DEB_NAME" bash -c ". \$HOME/.cargo/env && git fetch origin && git checkout origin/$BRANCH && cargo build --release && cargo deb"
|
||||
docker cp ${ARM64_DEB_NAME}:/usr/src/termscp/target/debian/termscp_${VERSION}_arm64.deb ${PKGS_DIR}/deb/
|
||||
docker cp ${ARM64_DEB_NAME}:/usr/src/termscp/target/debian/termscp_${VERSION}-1_arm64.deb ${PKGS_DIR}/deb/termscp_${VERSION}_arm64.deb
|
||||
docker cp ${ARM64_DEB_NAME}:/usr/src/termscp/target/release/termscp ${PKGS_DIR}/aarch64-unknown-linux-gnu/
|
||||
docker stop "$ARM64_DEB_NAME"
|
||||
# Make tar.gz
|
||||
|
||||
2
dist/build/linux-x86_64.sh
vendored
2
dist/build/linux-x86_64.sh
vendored
@@ -38,7 +38,7 @@ mkdir -p ${PKGS_DIR}/deb/
|
||||
mkdir -p ${PKGS_DIR}/x86_64-unknown-linux-gnu/
|
||||
docker run --name "$X86_64_DEB_NAME" -d "$X86_64_DEB_NAME" || docker start "$X86_64_DEB_NAME"
|
||||
docker exec -it "$X86_64_DEB_NAME" bash -c ". \$HOME/.cargo/env && git fetch origin && git checkout origin/$BRANCH && cargo build --release && cargo deb"
|
||||
docker cp ${X86_64_DEB_NAME}:/usr/src/termscp/target/debian/termscp_${VERSION}_amd64.deb ${PKGS_DIR}/deb/
|
||||
docker cp ${X86_64_DEB_NAME}:/usr/src/termscp/target/debian/termscp_${VERSION}-1_amd64.deb ${PKGS_DIR}/deb/termscp_${VERSION}_amd64.deb
|
||||
docker cp ${X86_64_DEB_NAME}:/usr/src/termscp/target/release/termscp ${PKGS_DIR}/x86_64-unknown-linux-gnu/
|
||||
docker stop "$X86_64_DEB_NAME"
|
||||
# Make tar.gz
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">Entwickelt von <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Aktuelle Version: 0.12.3 (06/10/2023)</p>
|
||||
<p align="center">Aktuelle Version: 0.13.0 (03/03/2024)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
@@ -138,6 +138,7 @@ Termscp ist ein funktionsreicher Terminal-Dateitransfer und Explorer mit Unterst
|
||||
- **FTP** und **FTPS**
|
||||
- **S3**
|
||||
- **SMB**
|
||||
- **WebDAV**
|
||||
- 🖥 Erkunden und bedienen Sie das Dateisystem der Fernbedienung und des lokalen Computers mit einer praktischen Benutzeroberfläche
|
||||
- Erstellen, Entfernen, Umbenennen, Suchen, Anzeigen und Bearbeiten von Dateien
|
||||
- ⭐ Verbinden Sie sich über integrierte Lesezeichen und aktuelle Verbindungen mit Ihren Lieblingshosts
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
- [AWS S3 address argument](#aws-s3-address-argument)
|
||||
- [SMB address argument](#smb-address-argument)
|
||||
- [How Password can be provided 🔐](#how-password-can-be-provided-)
|
||||
- [Subcommands](#subcommands)
|
||||
- [Import a theme](#import-a-theme)
|
||||
- [Install latest version](#install-latest-version)
|
||||
- [S3 connection parameters](#s3-connection-parameters)
|
||||
- [S3 credentials 🦊](#s3-credentials-)
|
||||
- [File explorer 📂](#file-explorer-)
|
||||
@@ -45,10 +48,7 @@ OR
|
||||
|
||||
- `-P, --password <password>` if address is provided, password will be this argument
|
||||
- `-b, --address-as-bookmark` resolve address argument as a bookmark name
|
||||
- `-c, --config` Open termscp starting from the configuration page
|
||||
- `-q, --quiet` Disable logging
|
||||
- `-t, --theme <path>` Import specified theme
|
||||
- `-u, --update` Update termscp to latest version
|
||||
- `-v, --version` Print version info
|
||||
- `-h, --help` Print help page
|
||||
|
||||
@@ -131,6 +131,17 @@ Password can be basically provided through 3 ways when address argument is provi
|
||||
- Via `sshpass`: you can provide password via `sshpass`, e.g. `sshpass -f ~/.ssh/topsecret.key termscp cvisintin@192.168.1.31`
|
||||
- You will be prompted for it: if you don't use any of the previous methods, you will be prompted for the password, as happens with the more classics tools such as `scp`, `ssh`, etc.
|
||||
|
||||
|
||||
### Subcommands
|
||||
|
||||
#### Import a theme
|
||||
|
||||
Run termscp as `termscp theme <theme-file>`
|
||||
|
||||
#### Install latest version
|
||||
|
||||
Run termscp as `termscp update`
|
||||
|
||||
---
|
||||
|
||||
## S3 connection parameters
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">Desarrollado por <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Versión actual: 0.12.3 (06/10/2023)</p>
|
||||
<p align="center">Versión actual: 0.13.0 (03/03/2024)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
@@ -138,6 +138,7 @@ Termscp es un explorador y transferencia de archivos de terminal rico en funcion
|
||||
- **FTP** y **FTPS**
|
||||
- **S3**
|
||||
- **SMB**
|
||||
- **WebDAV**
|
||||
- 🖥 Explore y opere en el sistema de archivos de la máquina local y remota con una interfaz de usuario práctica
|
||||
- Cree, elimine, cambie el nombre, busque, vea y edite archivos
|
||||
- ⭐ Conéctese a sus hosts favoritos y conexiones recientes
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
- [Uso ❓](#uso-)
|
||||
- [Argumento dirección 🌎](#argumento-dirección-)
|
||||
- [Argumento dirección por AWS S3](#argumento-dirección-por-aws-s3)
|
||||
- [Argumento de dirección de WebDAV](#argumento-de-dirección-de-webdav)
|
||||
- [Argumento dirección por SMB](#argumento-dirección-por-smb)
|
||||
- [Cómo se puede proporcionar la contraseña 🔐](#cómo-se-puede-proporcionar-la-contraseña-)
|
||||
- [S3 parámetros de conexión](#s3-parámetros-de-conexión)
|
||||
@@ -45,10 +46,7 @@ OR
|
||||
|
||||
- `-P, --password <password>` si se proporciona la dirección, la contraseña será este argumento
|
||||
- `-b, --address-as-bookmark` resuelve el argumento de la dirección como un nombre de marcador
|
||||
- `-c, --config` Abrir termscp comenzando desde la página de configuración
|
||||
- `-q, --quiet` Deshabilitar el registro
|
||||
- `-t, --theme <path>` Importar tema especificado
|
||||
- `-u, --update` Actualizar termscp a la última versión
|
||||
- `-v, --version` Imprimir información de la versión
|
||||
- `-h, --help` Imprimir página de ayuda
|
||||
|
||||
@@ -106,6 +104,20 @@ por ejemplo
|
||||
s3://buckethead@eu-central-1:default:/assets
|
||||
```
|
||||
|
||||
#### Argumento de dirección de WebDAV
|
||||
|
||||
En caso de que quieras conectarte a WebDAV utiliza la siguiente sintaxis
|
||||
|
||||
```txt
|
||||
http://<username>:<password>@<url></path>
|
||||
```
|
||||
|
||||
o en caso de que quieras usar https
|
||||
|
||||
```txt
|
||||
https://<username>:<password>@<url></path>
|
||||
```
|
||||
|
||||
#### Argumento dirección por SMB
|
||||
|
||||
SMB tiene una sintaxis diferente para el argumento de la dirección CLI, que es diferente si está en Windows u otros sistemas:
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">Développé par <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Version actuelle: 0.12.3 (06/10/2023)</p>
|
||||
<p align="center">Version actuelle: 0.13.0 (03/03/2024)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
@@ -138,6 +138,7 @@ Termscp est un file transfer et explorateur de fichiers de terminal riche en fon
|
||||
- **FTP** et **FTPS**
|
||||
- **S3**
|
||||
- **SMB**
|
||||
- **WebDAV**
|
||||
- 🖥 Explorer et opérer sur le système de fichiers distant et local avec une interface utilisateur pratique.
|
||||
- Créer, supprimer, renommer, rechercher, afficher et modifier des fichiers
|
||||
- ⭐ Connectez-vous à vos hôtes préférés via des signets et des connexions récentes.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
- [Usage ❓](#usage-)
|
||||
- [Argument d'adresse 🌎](#argument-dadresse-)
|
||||
- [Argument d'adresse AWS S3](#argument-dadresse-aws-s3)
|
||||
- [Argument d'adresse WebDAV](#argument-dadresse-webdav)
|
||||
- [Argument d'adresse SMB](#argument-dadresse-smb)
|
||||
- [Comment le mot de passe peut être fourni 🔐](#comment-le-mot-de-passe-peut-être-fourni-)
|
||||
- [S3 paramètres de connexion](#s3-paramètres-de-connexion)
|
||||
@@ -43,10 +44,7 @@ ou
|
||||
|
||||
- `-P, --password <password>` si l'adresse est fournie, le mot de passe sera cet argument
|
||||
- `-b, --address-as-bookmark` résoudre l'argument d'adresse en tant que nom de signet
|
||||
- `-c, --config` Ouvrir termscp à partir de la page de configuration
|
||||
- `-q, --quiet` Désactiver la journalisation
|
||||
- `-t, --theme <path>` Importer le thème spécifié
|
||||
- `-u, --update` Mettre à jour termscp vers la dernière version
|
||||
- `-v, --version` Imprimer les informations sur la version
|
||||
- `-h, --help` Imprimer la page d'aide
|
||||
|
||||
@@ -104,6 +102,20 @@ e.g.
|
||||
s3://buckethead@eu-central-1:default:/assets
|
||||
```
|
||||
|
||||
#### Argument d'adresse WebDAV
|
||||
|
||||
Dans le cas où vous souhaitez vous connecter à WebDAV, utilisez la syntaxe suivante
|
||||
|
||||
```txt
|
||||
http://<username>:<password>@<url></path>
|
||||
```
|
||||
|
||||
ou dans le cas où vous souhaitez utiliser https
|
||||
|
||||
```txt
|
||||
https://<username>:<password>@<url></path>
|
||||
```
|
||||
|
||||
#### Argument d'adresse SMB
|
||||
|
||||
SMB a une syntaxe différente pour l'argument d'adresse CLI, qui est différente que vous soyez sur Windows ou sur d'autres systèmes :
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">Sviluppato da <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Versione corrente: 0.12.3 (06/10/2023)</p>
|
||||
<p align="center">Versione corrente: 0.13.0 (03/03/2024)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
@@ -138,6 +138,7 @@ Termscp è un file transfer ed explorer ricco di funzionalità, con supporto a S
|
||||
- **FTP** and **FTPS**
|
||||
- **S3**
|
||||
- **SMB**
|
||||
- **WebDAV**
|
||||
- 🖥 Esplora e opera sia sul file system locale che su quello remoto con una UI di facile utilizzo.
|
||||
- Crea, rimuove, rinomina, cerca, visualizza e modifica file
|
||||
- ⭐ Connettiti ai tuoi host preferiti tramite la funzionalità integrata dei segnalibri e delle connessioni recenti.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
- [Argomenti da linea di comando ❓](#argomenti-da-linea-di-comando-)
|
||||
- [Argomento indirizzo 🌎](#argomento-indirizzo-)
|
||||
- [Argomento indirizzo per AWS S3](#argomento-indirizzo-per-aws-s3)
|
||||
- [Argomento indirizzo per WebDAV](#argomento-indirizzo-per-webdav)
|
||||
- [Indirizzo SMB](#indirizzo-smb)
|
||||
- [Come fornire la password 🔐](#come-fornire-la-password-)
|
||||
- [Parametri di connessione S3](#parametri-di-connessione-s3)
|
||||
@@ -43,10 +44,7 @@ O
|
||||
|
||||
- `-P, --password <password>` Se viene fornito l'argomento indirizzo, questa sarà la password utilizzata per autenticarsi
|
||||
- `-b, --address-as-bookmark` risolve l'argomento indirizzo come nome di un segnalibro
|
||||
- `-c, --config` Apri la configurazione di termscp
|
||||
- `-q, --quiet` Disabilita i log
|
||||
- `-t, --theme <path>` Importa il tema al percorso fornito
|
||||
- `-u, --update` Aggiorna termscp all'ultima versione
|
||||
- `-v, --version` Mostra a video le informazioni sulla versione attualmente installata
|
||||
- `-h, --help` Mostra la pagina di aiuto.
|
||||
|
||||
@@ -102,6 +100,20 @@ e.g.
|
||||
s3://buckethead@eu-central-1:default:/assets
|
||||
```
|
||||
|
||||
#### Argomento indirizzo per WebDAV
|
||||
|
||||
Nel caso in cui si desideri connettersi a WebDAV utilizzare la seguente sintassi
|
||||
|
||||
```txt
|
||||
http://<username>:<password>@<url></path>
|
||||
```
|
||||
|
||||
oppure nel caso in cui si desideri utilizzare https
|
||||
|
||||
```txt
|
||||
https://<username>:<password>@<url></path>
|
||||
```
|
||||
|
||||
#### Indirizzo SMB
|
||||
|
||||
SMB ha una sintassi differente rispetto agli altri protocolli e cambia in base al sistema operativo:
|
||||
|
||||
31
docs/man.md
31
docs/man.md
@@ -4,8 +4,12 @@
|
||||
- [Usage ❓](#usage-)
|
||||
- [Address argument 🌎](#address-argument-)
|
||||
- [AWS S3 address argument](#aws-s3-address-argument)
|
||||
- [WebDAV address argument](#webdav-address-argument)
|
||||
- [SMB address argument](#smb-address-argument)
|
||||
- [How Password can be provided 🔐](#how-password-can-be-provided-)
|
||||
- [Subcommands](#subcommands)
|
||||
- [Import a theme](#import-a-theme)
|
||||
- [Install latest version](#install-latest-version)
|
||||
- [S3 connection parameters](#s3-connection-parameters)
|
||||
- [S3 credentials 🦊](#s3-credentials-)
|
||||
- [File explorer 📂](#file-explorer-)
|
||||
@@ -43,10 +47,7 @@ OR
|
||||
|
||||
- `-P, --password <password>` if address is provided, password will be this argument
|
||||
- `-b, --address-as-bookmark` resolve address argument as a bookmark name
|
||||
- `-c, --config` Open termscp starting from the configuration page
|
||||
- `-q, --quiet` Disable logging
|
||||
- `-t, --theme <path>` Import specified theme
|
||||
- `-u, --update` Update termscp to latest version
|
||||
- `-v, --version` Print version info
|
||||
- `-h, --help` Print help page
|
||||
|
||||
@@ -104,6 +105,20 @@ e.g.
|
||||
s3://buckethead@eu-central-1:default:/assets
|
||||
```
|
||||
|
||||
#### WebDAV address argument
|
||||
|
||||
In case you want to connect to webDAV use the following syntax
|
||||
|
||||
```txt
|
||||
http://<username>:<password>@<url></path>
|
||||
```
|
||||
|
||||
or in case you want to use https
|
||||
|
||||
```txt
|
||||
https://<username>:<password>@<url></path>
|
||||
```
|
||||
|
||||
#### SMB address argument
|
||||
|
||||
SMB has a different syntax for CLI address argument, which is different whether you're on Windows or other systems:
|
||||
@@ -129,6 +144,16 @@ Password can be basically provided through 3 ways when address argument is provi
|
||||
- Via `sshpass`: you can provide password via `sshpass`, e.g. `sshpass -f ~/.ssh/topsecret.key termscp cvisintin@192.168.1.31`
|
||||
- You will be prompted for it: if you don't use any of the previous methods, you will be prompted for the password, as happens with the more classics tools such as `scp`, `ssh`, etc.
|
||||
|
||||
### Subcommands
|
||||
|
||||
#### Import a theme
|
||||
|
||||
Run termscp as `termscp theme <theme-file>`
|
||||
|
||||
#### Install latest version
|
||||
|
||||
Run termscp as `termscp update`
|
||||
|
||||
---
|
||||
|
||||
## S3 connection parameters
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">由 <a href="https://veeso.dev/" target="_blank">@veeso</a> 开发</p>
|
||||
<p align="center">当前版本: 0.12.3 (06/10/2023)</p>
|
||||
<p align="center">当前版本: 0.13.0 (03/03/2024)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
@@ -140,6 +140,7 @@ termscp 是一个功能丰富的终端文件浏览和传输工具,支持 SCP/S
|
||||
- **FTP** and **FTPS**
|
||||
- **S3**
|
||||
- **SMB**
|
||||
- **WebDAV**
|
||||
- 🖥 使用便捷的 UI 在远程和本地文件系统上浏览和操作
|
||||
- 创建、删除、重命名、搜索、查看和编辑文件
|
||||
- ⭐ 通过“内置书签”和“最近连接”快速连接到您的主机
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
- [用法](#用法)
|
||||
- [地址参数](#地址参数)
|
||||
- [AWS S3 地址参数](#aws-s3-地址参数)
|
||||
- [WebDAV 地址参数](#webdav-地址参数)
|
||||
- [SMB 地址参数](#smb-地址参数)
|
||||
- [如何输入密码](#如何输入密码)
|
||||
- [S3 连接参数](#s3-连接参数)
|
||||
@@ -43,9 +44,7 @@ termscp启动时可以使用以下选项:
|
||||
|
||||
- `-P, --password <password>` 登陆密码
|
||||
- `-b, --address-as-bookmark` 将地址参数解析为书签名称
|
||||
- `-c, --config` 打开termscp时打开配置页面
|
||||
- `-q, --quiet` 禁用日志
|
||||
- `-t, --theme <path>` 导入自定义主题
|
||||
- `-v, --version` 打印版本信息
|
||||
- `-h, --help` 打开帮助
|
||||
|
||||
@@ -103,6 +102,19 @@ s3://<bucket-name>@<region>[:profile][:/wrkdir]
|
||||
s3://buckethead@eu-central-1:default:/assets
|
||||
```
|
||||
|
||||
#### WebDAV 地址参数
|
||||
|
||||
如果您想要连接到 WebDAV,请使用以下语法
|
||||
|
||||
```txt
|
||||
http://<username>:<password>@<url></path>
|
||||
或者如果您想要使用 https
|
||||
```
|
||||
|
||||
```txt
|
||||
https://<username>:<password>@<url></path>
|
||||
```
|
||||
|
||||
#### SMB 地址参数
|
||||
|
||||
SMB 对 CLI 地址参数有不同的语法,无论您是在 Windows 还是其他系统上,这都是不同的:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<head>
|
||||
<title>
|
||||
termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3/SMB | termscp
|
||||
termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3/SMB/WebDAV | termscp
|
||||
</title>
|
||||
<meta property="og:description"
|
||||
content="termscp is a feature rich terminal file transfer and explorer, with support for SCP/SFTP/FTP/S3. It is Linux, MacOS, FreeBSD, NetBSD and Windows compatible" />
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<span translate="getStarted.windows.moderation">Consider that Chocolatey moderation can take up to a few weeks
|
||||
since last release, so if the latest version is not available yet,
|
||||
you can install it downloading the ZIP file from</span>
|
||||
<a href="https://github.com/veeso/termscp/releases/latest/download/termscp.0.12.3.nupkg"
|
||||
<a href="https://github.com/veeso/termscp/releases/latest/download/termscp.0.13.0.nupkg"
|
||||
target="_blank">Github</a>
|
||||
<span translate="getStarted.windows.then">and then, from the ZIP directory, install it via</span>
|
||||
</p>
|
||||
@@ -75,7 +75,7 @@
|
||||
On Debian based distros, you can install termscp using the Deb
|
||||
package via:
|
||||
</p>
|
||||
<pre><span class="function">wget</span> -O termscp.deb <span class="string">https://github.com/veeso/termscp/releases/latest/download/termscp_0.12.3_amd64.deb</span>
|
||||
<pre><span class="function">wget</span> -O termscp.deb <span class="string">https://github.com/veeso/termscp/releases/latest/download/termscp_0.13.0_amd64.deb</span>
|
||||
sudo <span class="function">dpkg</span> -i <span class="string">termscp.deb</span></pre>
|
||||
</div>
|
||||
<h3>
|
||||
@@ -87,7 +87,7 @@ sudo <span class="function">dpkg</span> -i <span class="string">termscp.deb</spa
|
||||
On RedHat based distros, you can install termscp using the RPM
|
||||
package via:
|
||||
</p>
|
||||
<pre><span class="function">wget</span> -O termscp.rpm <span class="string">https://github.com/veeso/termscp/releases/latest/download/termscp-0.12.3-1.x86_64.rpm</span>
|
||||
<pre><span class="function">wget</span> -O termscp.rpm <span class="string">https://github.com/veeso/termscp/releases/latest/download/termscp-0.13.0-1.x86_64.rpm</span>
|
||||
sudo <span class="function">rpm</span> -U <span class="string">termscp.rpm</span></pre>
|
||||
</div>
|
||||
<h3>
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<section id="intro" class="flex flex-col mx-auto items-center justify-center w-full px-4 dark:bg-brand dark:text-gray-100">
|
||||
<section id="intro"
|
||||
class="flex flex-col mx-auto items-center justify-center w-full px-4 dark:bg-brand dark:text-gray-100">
|
||||
<h1 class="text-3xl text-center font-thin">termscp</h1>
|
||||
<img class="w-[256px] h-auto m-auto" alt="logo" src="assets/images/termscp.webp" />
|
||||
<h2 class="text-xl font-thin text-center py-6" translate="intro.caption">
|
||||
A feature rich terminal UI file transfer and explorer with support for
|
||||
SCP/SFTP/FTP/S3/SMB
|
||||
SCP/SFTP/FTP/S3/SMB/WebDAV
|
||||
</h2>
|
||||
<button class="bg-brand hover:bg-gray-800 text-white font-thin text-xl py-2 px-4 rounded-xl max-w-fit">
|
||||
<a href="/get-started.html" class="no-underline" translate="intro.getStarted">Get started →</a>
|
||||
</button>
|
||||
<div class="p-4 my-4 text-sm text-green-800 rounded-lg bg-green-50">
|
||||
<p class="text-lg">
|
||||
<span translate="intro.versionAlert">termscp 0.12.3 is NOW out! Download it from</span>
|
||||
<span translate="intro.versionAlert">termscp 0.13.0 is NOW out! Download it from</span>
|
||||
<a href="/get-started.html" translate="intro.here">here!</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -66,7 +67,8 @@
|
||||
<div class="flex flex-row sm:flex-col justify-around gap-12">
|
||||
<div class="hook">
|
||||
<h3 class="text-center text-gray-500 dark:text-gray-100 font-light text-xl">
|
||||
<a href="/get-started.html" class="no-underline hover:underline" translate="intro.footer.getStarted">Get started</a>
|
||||
<a href="/get-started.html" class="no-underline hover:underline" translate="intro.footer.getStarted">Get
|
||||
started</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="hook">
|
||||
@@ -76,7 +78,8 @@
|
||||
</div>
|
||||
<div class="hook">
|
||||
<h3 class="text-center text-gray-500 dark:text-gray-100 font-light text-xl">
|
||||
<a href="/updates.html" class="no-underline hover:underline" translate="intro.footer.updates">Install updates</a>
|
||||
<a href="/updates.html" class="no-underline hover:underline" translate="intro.footer.updates">Install
|
||||
updates</a>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
<head>
|
||||
<title>
|
||||
termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3/SMB | termscp
|
||||
termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3/SMB/WebDAV | termscp
|
||||
</title>
|
||||
<meta property="og:description"
|
||||
content="a WinSCP alternative for Linux and MacOS with support for SCP/SFTP/FTP/S3/SMB. Command line file transfer with user interface compatible with all the operating systems." />
|
||||
content="a WinSCP alternative for Linux and MacOS with support for SCP/SFTP/FTP/S3/SMB/WebDAV. Command line file transfer with user interface compatible with all the operating systems." />
|
||||
<meta name="description"
|
||||
content="a WinSCP alternative for Linux and MacOS with support for SCP/SFTP/FTP/S3/SMB. Command line file transfer with user interface compatible with all the operating systems." />
|
||||
<meta property="og:title" content="termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3/SMB | termscp" />
|
||||
content="a WinSCP alternative for Linux and MacOS with support for SCP/SFTP/FTP/S3/SMB/WebDAV. Command line file transfer with user interface compatible with all the operating systems." />
|
||||
<meta property="og:title"
|
||||
content="termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3/SMB/WebDAV | termscp" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
@@ -40,7 +41,7 @@
|
||||
|
||||
<body>
|
||||
<div id="layout" class="dark:bg-brand dark:text-gray-100">
|
||||
|
||||
|
||||
<!-- Menu -->
|
||||
<header id="menu"></header>
|
||||
<main>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"intro": {
|
||||
"caption": "A feature rich terminal UI file transfer and explorer with support for SCP/SFTP/FTP/S3",
|
||||
"getStarted": "Get started →",
|
||||
"versionAlert": "termscp 0.12.3 is NOW out! Download it from",
|
||||
"versionAlert": "termscp 0.13.0 is NOW out! Download it from",
|
||||
"here": "here",
|
||||
"features": {
|
||||
"handy": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"intro": {
|
||||
"caption": "Un explorador y transferencia de archivos de terminal rico en funciones, con apoyo para SCP/SFTP/FTP/S3",
|
||||
"getStarted": "Para iniciar →",
|
||||
"versionAlert": "termscp 0.12.3 ya está disponible! Descárgalo desde",
|
||||
"versionAlert": "termscp 0.13.0 ya está disponible! Descárgalo desde",
|
||||
"here": "aquì",
|
||||
"features": {
|
||||
"handy": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"intro": {
|
||||
"caption": "Un file transfer et navigateur de terminal riche en fonctionnalités avec support pour SCP/SFTP/FTP/S3",
|
||||
"getStarted": "Pour commencer →",
|
||||
"versionAlert": "termscp 0.12.3 est maintenant sorti! Télécharge-le depuis",
|
||||
"versionAlert": "termscp 0.13.0 est maintenant sorti! Télécharge-le depuis",
|
||||
"here": "ici",
|
||||
"features": {
|
||||
"handy": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"intro": {
|
||||
"caption": "Un file transfer ed explorer ricco di funzionalità con supporto per SFTP/SCP/FTP/S3",
|
||||
"getStarted": "Installa termscp →",
|
||||
"versionAlert": "termscp 0.12.3 è ORA disponbile! Scaricalo da",
|
||||
"versionAlert": "termscp 0.13.0 è ORA disponbile! Scaricalo da",
|
||||
"here": "qui",
|
||||
"features": {
|
||||
"handy": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"intro": {
|
||||
"caption": "功能丰富的终端 UI 文件传输和浏览器,支持 SCP/SFTP/FTP/S3",
|
||||
"getStarted": "开始 →",
|
||||
"versionAlert": "termscp 0.12.3 现已发布! 从下载",
|
||||
"versionAlert": "termscp 0.13.0 现已发布! 从下载",
|
||||
"here": "这里",
|
||||
"features": {
|
||||
"handy": {
|
||||
|
||||
@@ -34,36 +34,25 @@ Address syntax can be:
|
||||
Please, report issues to <https://github.com/veeso/termscp>
|
||||
Please, consider supporting the author <https://ko-fi.com/veeso>")]
|
||||
pub struct Args {
|
||||
#[argh(
|
||||
switch,
|
||||
short = 'b',
|
||||
description = "resolve address argument as a bookmark name"
|
||||
)]
|
||||
#[argh(subcommand)]
|
||||
pub nested: Option<ArgsSubcommands>,
|
||||
/// resolve address argument as a bookmark name
|
||||
#[argh(switch, short = 'b')]
|
||||
pub address_as_bookmark: bool,
|
||||
#[argh(switch, short = 'c', description = "open termscp configuration")]
|
||||
pub config: bool,
|
||||
#[argh(switch, short = 'D', description = "enable TRACE log level")]
|
||||
/// enable TRACE log level
|
||||
#[argh(switch, short = 'D')]
|
||||
pub debug: bool,
|
||||
#[argh(option, short = 'P', description = "provide password from CLI")]
|
||||
/// provide password from CLI
|
||||
#[argh(option, short = 'P')]
|
||||
pub password: Option<String>,
|
||||
#[argh(switch, short = 'q', description = "disable logging")]
|
||||
/// disable logging
|
||||
#[argh(switch, short = 'q')]
|
||||
pub quiet: bool,
|
||||
#[argh(option, short = 't', description = "import specified theme")]
|
||||
pub theme: Option<String>,
|
||||
#[argh(
|
||||
switch,
|
||||
short = 'u',
|
||||
description = "update termscp to the latest version"
|
||||
)]
|
||||
pub update: bool,
|
||||
#[argh(
|
||||
option,
|
||||
short = 'T',
|
||||
default = "10",
|
||||
description = "set UI ticks; default 10ms"
|
||||
)]
|
||||
/// set UI ticks; default 10ms
|
||||
#[argh(option, short = 'T', default = "10")]
|
||||
pub ticks: u64,
|
||||
#[argh(switch, short = 'v', description = "print version")]
|
||||
/// print version
|
||||
#[argh(switch, short = 'v')]
|
||||
pub version: bool,
|
||||
// -- positional
|
||||
#[argh(
|
||||
@@ -73,6 +62,33 @@ pub struct Args {
|
||||
pub positional: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand)]
|
||||
pub enum ArgsSubcommands {
|
||||
Config(ConfigArgs),
|
||||
LoadTheme(LoadThemeArgs),
|
||||
Update(UpdateArgs),
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
/// open termscp configuration
|
||||
#[argh(subcommand, name = "config")]
|
||||
pub struct ConfigArgs {}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
/// import the specified theme
|
||||
#[argh(subcommand, name = "update")]
|
||||
pub struct UpdateArgs {}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
/// import the specified theme
|
||||
#[argh(subcommand, name = "theme")]
|
||||
pub struct LoadThemeArgs {
|
||||
#[argh(positional)]
|
||||
/// theme file
|
||||
pub theme: PathBuf,
|
||||
}
|
||||
|
||||
pub struct RunOpts {
|
||||
pub remote: Remote,
|
||||
pub ticks: Duration,
|
||||
@@ -80,6 +96,29 @@ pub struct RunOpts {
|
||||
pub task: Task,
|
||||
}
|
||||
|
||||
impl RunOpts {
|
||||
pub fn config() -> Self {
|
||||
Self {
|
||||
task: Task::Activity(NextActivity::SetupActivity),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update() -> Self {
|
||||
Self {
|
||||
task: Task::InstallUpdate,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import_theme(theme: PathBuf) -> Self {
|
||||
Self {
|
||||
task: Task::ImportTheme(theme),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RunOpts {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
||||
@@ -11,6 +11,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::filetransfer::params::{
|
||||
AwsS3Params, GenericProtocolParams, ProtocolParams, SmbParams as TransferSmbParams,
|
||||
WebDAVProtocolParams,
|
||||
};
|
||||
use crate::filetransfer::{FileTransferParams, FileTransferProtocol};
|
||||
|
||||
@@ -114,6 +115,17 @@ impl From<FileTransferParams> for Bookmark {
|
||||
local_path,
|
||||
s3: None,
|
||||
},
|
||||
ProtocolParams::WebDAV(parms) => Self {
|
||||
protocol,
|
||||
address: Some(parms.uri),
|
||||
port: None,
|
||||
username: Some(parms.username),
|
||||
password: Some(parms.password),
|
||||
remote_path,
|
||||
local_path,
|
||||
s3: None,
|
||||
smb: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,6 +173,14 @@ impl From<Bookmark> for FileTransferParams {
|
||||
|
||||
Self::new(bookmark.protocol, ProtocolParams::Smb(params))
|
||||
}
|
||||
FileTransferProtocol::WebDAV => Self::new(
|
||||
FileTransferProtocol::WebDAV,
|
||||
ProtocolParams::WebDAV(WebDAVProtocolParams {
|
||||
uri: bookmark.address.unwrap_or_default(),
|
||||
username: bookmark.username.unwrap_or_default(),
|
||||
password: bookmark.password.unwrap_or_default(),
|
||||
}),
|
||||
),
|
||||
}
|
||||
.remote_path(bookmark.remote_path) // Set entry remote_path
|
||||
.local_path(bookmark.local_path) // Set entry local path
|
||||
@@ -390,6 +410,35 @@ mod tests {
|
||||
assert_eq!(gparams.password.as_deref().unwrap(), "password");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ftparams_from_webdav() {
|
||||
let bookmark: Bookmark = Bookmark {
|
||||
address: Some(String::from("192.168.1.1")),
|
||||
port: None,
|
||||
protocol: FileTransferProtocol::WebDAV,
|
||||
username: Some(String::from("root")),
|
||||
password: Some(String::from("password")),
|
||||
remote_path: Some(PathBuf::from("/tmp")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: None,
|
||||
smb: None,
|
||||
};
|
||||
let params = FileTransferParams::from(bookmark);
|
||||
assert_eq!(params.protocol, FileTransferProtocol::WebDAV);
|
||||
assert_eq!(
|
||||
params.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/tmp")
|
||||
);
|
||||
assert_eq!(
|
||||
params.local_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/usr")
|
||||
);
|
||||
let gparams = params.params.webdav_params().unwrap();
|
||||
assert_eq!(gparams.uri.as_str(), "192.168.1.1");
|
||||
assert_eq!(gparams.username, "root");
|
||||
assert_eq!(gparams.password, "password");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ftparams_from_s3_bookmark() {
|
||||
let bookmark: Bookmark = Bookmark {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
//! `config` is the module which provides access to all the termscp configurations
|
||||
|
||||
// export
|
||||
pub use params::*;
|
||||
|
||||
pub mod bookmarks;
|
||||
pub mod params;
|
||||
|
||||
@@ -12,7 +12,9 @@ use remotefs_smb::SmbOptions;
|
||||
#[cfg(smb)]
|
||||
use remotefs_smb::{SmbCredentials, SmbFs};
|
||||
use remotefs_ssh::{ScpFs, SftpFs, SshConfigParseRule, SshOpts};
|
||||
use remotefs_webdav::WebDAVFs;
|
||||
|
||||
use super::params::WebDAVProtocolParams;
|
||||
#[cfg(not(smb))]
|
||||
use super::params::{AwsS3Params, GenericProtocolParams};
|
||||
#[cfg(smb)]
|
||||
@@ -51,6 +53,9 @@ impl Builder {
|
||||
(FileTransferProtocol::Smb, ProtocolParams::Smb(params)) => {
|
||||
Box::new(Self::smb_client(params))
|
||||
}
|
||||
(FileTransferProtocol::WebDAV, ProtocolParams::WebDAV(params)) => {
|
||||
Box::new(Self::webdav_client(params))
|
||||
}
|
||||
(protocol, params) => {
|
||||
error!("Invalid params for protocol '{:?}'", protocol);
|
||||
panic!("Invalid protocol '{protocol:?}' with parameters of type {params:?}")
|
||||
@@ -154,6 +159,10 @@ impl Builder {
|
||||
SmbFs::new(credentials)
|
||||
}
|
||||
|
||||
fn webdav_client(params: WebDAVProtocolParams) -> WebDAVFs {
|
||||
WebDAVFs::new(¶ms.username, ¶ms.password, ¶ms.uri)
|
||||
}
|
||||
|
||||
/// Build ssh options from generic protocol params and client configuration
|
||||
fn build_ssh_opts(params: GenericProtocolParams, config_client: &ConfigClient) -> SshOpts {
|
||||
let mut opts = SshOpts::new(params.address.clone())
|
||||
|
||||
@@ -18,6 +18,7 @@ pub enum FileTransferProtocol {
|
||||
Scp,
|
||||
Sftp,
|
||||
Smb,
|
||||
WebDAV,
|
||||
}
|
||||
|
||||
// Traits
|
||||
@@ -33,6 +34,7 @@ impl std::string::ToString for FileTransferProtocol {
|
||||
FileTransferProtocol::Scp => "SCP",
|
||||
FileTransferProtocol::Sftp => "SFTP",
|
||||
FileTransferProtocol::Smb => "SMB",
|
||||
FileTransferProtocol::WebDAV => "WEBDAV",
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -47,6 +49,7 @@ impl std::str::FromStr for FileTransferProtocol {
|
||||
"SCP" => Ok(FileTransferProtocol::Scp),
|
||||
"SFTP" => Ok(FileTransferProtocol::Sftp),
|
||||
"SMB" => Ok(FileTransferProtocol::Smb),
|
||||
"WEBDAV" | "HTTP" | "HTTPS" => Ok(FileTransferProtocol::WebDAV),
|
||||
_ => Err(s.to_string()),
|
||||
}
|
||||
}
|
||||
@@ -134,9 +137,17 @@ mod tests {
|
||||
FileTransferProtocol::Ftp(false).to_string(),
|
||||
String::from("FTP")
|
||||
);
|
||||
assert_eq!(
|
||||
FileTransferProtocol::WebDAV.to_string(),
|
||||
String::from("WEBDAV")
|
||||
);
|
||||
assert_eq!(FileTransferProtocol::Scp.to_string(), String::from("SCP"));
|
||||
assert_eq!(FileTransferProtocol::Sftp.to_string(), String::from("SFTP"));
|
||||
assert_eq!(FileTransferProtocol::AwsS3.to_string(), String::from("S3"));
|
||||
assert_eq!(FileTransferProtocol::Smb.to_string(), String::from("SMB"));
|
||||
assert_eq!(
|
||||
FileTransferProtocol::WebDAV.to_string(),
|
||||
String::from("WEBDAV")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ pub enum ProtocolParams {
|
||||
Generic(GenericProtocolParams),
|
||||
AwsS3(AwsS3Params),
|
||||
Smb(SmbParams),
|
||||
WebDAV(WebDAVProtocolParams),
|
||||
}
|
||||
|
||||
/// Protocol params used by most common protocols
|
||||
@@ -89,6 +90,7 @@ impl FileTransferParams {
|
||||
ProtocolParams::AwsS3(params) => params.password_missing(),
|
||||
ProtocolParams::Generic(params) => params.password_missing(),
|
||||
ProtocolParams::Smb(params) => params.password_missing(),
|
||||
ProtocolParams::WebDAV(params) => params.password_missing(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,10 +100,29 @@ impl FileTransferParams {
|
||||
ProtocolParams::AwsS3(params) => params.set_default_secret(secret),
|
||||
ProtocolParams::Generic(params) => params.set_default_secret(secret),
|
||||
ProtocolParams::Smb(params) => params.set_default_secret(secret),
|
||||
ProtocolParams::WebDAV(params) => params.set_default_secret(secret),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Protocol params used by WebDAV
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WebDAVProtocolParams {
|
||||
pub uri: String,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
impl WebDAVProtocolParams {
|
||||
fn set_default_secret(&mut self, secret: String) {
|
||||
self.password = secret;
|
||||
}
|
||||
|
||||
fn password_missing(&self) -> bool {
|
||||
self.password.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FileTransferParams {
|
||||
fn default() -> Self {
|
||||
Self::new(FileTransferProtocol::Sftp, ProtocolParams::default())
|
||||
@@ -149,6 +170,15 @@ impl ProtocolParams {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Retrieve WebDAV parameters if any
|
||||
pub fn webdav_params(&self) -> Option<&WebDAVProtocolParams> {
|
||||
match self {
|
||||
ProtocolParams::WebDAV(params) => Some(params),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Generic protocol params
|
||||
@@ -512,6 +542,40 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn set_default_secret_smb() {
|
||||
let mut params = FileTransferParams::new(
|
||||
FileTransferProtocol::Scp,
|
||||
ProtocolParams::Smb(SmbParams::new("localhost", "temp")),
|
||||
);
|
||||
params.set_default_secret(String::from("secret"));
|
||||
assert_eq!(
|
||||
params
|
||||
.params
|
||||
.smb_params()
|
||||
.unwrap()
|
||||
.password
|
||||
.as_deref()
|
||||
.unwrap(),
|
||||
"secret"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_default_secret_webdav() {
|
||||
let mut params = FileTransferParams::new(
|
||||
FileTransferProtocol::Scp,
|
||||
ProtocolParams::WebDAV(WebDAVProtocolParams {
|
||||
uri: "http://localhost".to_string(),
|
||||
username: "user".to_string(),
|
||||
password: "pass".to_string(),
|
||||
}),
|
||||
);
|
||||
params.set_default_secret(String::from("secret"));
|
||||
assert_eq!(params.params.webdav_params().unwrap().password, "secret");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_default_secret_generic() {
|
||||
let mut params =
|
||||
|
||||
@@ -1081,10 +1081,7 @@ mod tests {
|
||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
let host: Localhost = Localhost::new(PathBuf::from(tmpdir.path())).ok().unwrap();
|
||||
// Execute
|
||||
#[cfg(unix)]
|
||||
assert_eq!(host.exec("echo 5").ok().unwrap().as_str(), "5\n");
|
||||
#[cfg(windows)]
|
||||
assert_eq!(host.exec("echo 5").ok().unwrap().as_str(), "5\r\n");
|
||||
assert!(host.exec("echo 5").ok().unwrap().as_str().contains("5"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
93
src/main.rs
93
src/main.rs
@@ -32,7 +32,7 @@ mod utils;
|
||||
|
||||
// namespaces
|
||||
use activity_manager::{ActivityManager, NextActivity};
|
||||
use cli_opts::{Args, BookmarkParams, HostParams, Remote, RunOpts, Task};
|
||||
use cli_opts::{Args, ArgsSubcommands, BookmarkParams, HostParams, Remote, RunOpts, Task};
|
||||
use filetransfer::FileTransferParams;
|
||||
use system::logging::{self, LogLevel};
|
||||
|
||||
@@ -63,59 +63,57 @@ fn main() {
|
||||
/// In case of success returns `RunOpts`
|
||||
/// in case something is wrong returns the error message
|
||||
fn parse_args(args: Args) -> Result<RunOpts, String> {
|
||||
let mut run_opts: RunOpts = RunOpts::default();
|
||||
// Version
|
||||
if args.version {
|
||||
return Err(format!(
|
||||
"termscp - {TERMSCP_VERSION} - Developed by {TERMSCP_AUTHORS}",
|
||||
));
|
||||
}
|
||||
// Setup activity?
|
||||
if args.config {
|
||||
run_opts.task = Task::Activity(NextActivity::SetupActivity);
|
||||
}
|
||||
// Logging
|
||||
if args.debug {
|
||||
run_opts.log_level = LogLevel::Trace;
|
||||
} else if args.quiet {
|
||||
run_opts.log_level = LogLevel::Off;
|
||||
}
|
||||
// Match ticks
|
||||
run_opts.ticks = Duration::from_millis(args.ticks);
|
||||
// @! extra modes
|
||||
if let Some(theme) = args.theme.as_deref() {
|
||||
run_opts.task = Task::ImportTheme(PathBuf::from(theme));
|
||||
}
|
||||
if args.update {
|
||||
run_opts.task = Task::InstallUpdate;
|
||||
}
|
||||
// @! Ordinary mode
|
||||
// Remote argument
|
||||
match parse_address_arg(&args) {
|
||||
Err(err) => return Err(err),
|
||||
Ok(Remote::None) => {}
|
||||
Ok(remote) => {
|
||||
// Set params
|
||||
run_opts.remote = remote;
|
||||
// In this case the first activity will be FileTransfer
|
||||
run_opts.task = Task::Activity(NextActivity::FileTransfer);
|
||||
}
|
||||
}
|
||||
let run_opts = match args.nested {
|
||||
Some(ArgsSubcommands::Update(_)) => RunOpts::update(),
|
||||
Some(ArgsSubcommands::LoadTheme(args)) => RunOpts::import_theme(args.theme),
|
||||
Some(ArgsSubcommands::Config(_)) => RunOpts::config(),
|
||||
None => {
|
||||
let mut run_opts: RunOpts = RunOpts::default();
|
||||
// Version
|
||||
if args.version {
|
||||
return Err(format!(
|
||||
"termscp - {TERMSCP_VERSION} - Developed by {TERMSCP_AUTHORS}",
|
||||
));
|
||||
}
|
||||
// Logging
|
||||
if args.debug {
|
||||
run_opts.log_level = LogLevel::Trace;
|
||||
} else if args.quiet {
|
||||
run_opts.log_level = LogLevel::Off;
|
||||
}
|
||||
// Match ticks
|
||||
run_opts.ticks = Duration::from_millis(args.ticks);
|
||||
// Remote argument
|
||||
match parse_address_arg(&args) {
|
||||
Err(err) => return Err(err),
|
||||
Ok(Remote::None) => {}
|
||||
Ok(remote) => {
|
||||
// Set params
|
||||
run_opts.remote = remote;
|
||||
// In this case the first activity will be FileTransfer
|
||||
run_opts.task = Task::Activity(NextActivity::FileTransfer);
|
||||
}
|
||||
}
|
||||
|
||||
// Local directory
|
||||
if let Some(localdir) = args.positional.get(1) {
|
||||
// Change working directory if local dir is set
|
||||
let localdir: PathBuf = PathBuf::from(localdir);
|
||||
if let Err(err) = env::set_current_dir(localdir.as_path()) {
|
||||
return Err(format!("Bad working directory argument: {err}"));
|
||||
// Local directory
|
||||
if let Some(localdir) = args.positional.get(1) {
|
||||
// Change working directory if local dir is set
|
||||
let localdir: PathBuf = PathBuf::from(localdir);
|
||||
if let Err(err) = env::set_current_dir(localdir.as_path()) {
|
||||
return Err(format!("Bad working directory argument: {err}"));
|
||||
}
|
||||
}
|
||||
|
||||
run_opts
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(run_opts)
|
||||
}
|
||||
|
||||
/// Parse address argument from cli args
|
||||
fn parse_address_arg(args: &Args) -> Result<Remote, String> {
|
||||
if let Some(remote) = args.positional.get(0) {
|
||||
if let Some(remote) = args.positional.first() {
|
||||
if args.address_as_bookmark {
|
||||
Ok(Remote::Bookmark(BookmarkParams::new(
|
||||
remote,
|
||||
@@ -197,5 +195,6 @@ fn run_activity(activity: NextActivity, ticks: Duration, remote: Remote) -> i32
|
||||
Remote::None => {}
|
||||
}
|
||||
manager.run(activity);
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
@@ -419,7 +419,7 @@ mod tests {
|
||||
use tempfile::TempDir;
|
||||
|
||||
use super::*;
|
||||
use crate::config::UserConfig;
|
||||
use crate::config::params::UserConfig;
|
||||
use crate::utils::random::random_alphanumeric_with_len;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -48,7 +48,7 @@ impl SshKeyStorage {
|
||||
.query(host)
|
||||
.identity_file
|
||||
.as_ref()
|
||||
.and_then(|x| x.get(0).cloned());
|
||||
.and_then(|x| x.first().cloned());
|
||||
|
||||
key
|
||||
})
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
// Locals
|
||||
use super::{AuthActivity, FileTransferParams};
|
||||
use crate::filetransfer::params::{AwsS3Params, GenericProtocolParams, ProtocolParams, SmbParams};
|
||||
use crate::filetransfer::params::{
|
||||
AwsS3Params, GenericProtocolParams, ProtocolParams, SmbParams, WebDAVProtocolParams,
|
||||
};
|
||||
|
||||
impl AuthActivity {
|
||||
/// Delete bookmark
|
||||
@@ -164,6 +166,7 @@ impl AuthActivity {
|
||||
ProtocolParams::AwsS3(params) => self.load_bookmark_s3_into_gui(params),
|
||||
ProtocolParams::Generic(params) => self.load_bookmark_generic_into_gui(params),
|
||||
ProtocolParams::Smb(params) => self.load_bookmark_smb_into_gui(params),
|
||||
ProtocolParams::WebDAV(params) => self.load_bookmark_webdav_into_gui(params),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,4 +199,10 @@ impl AuthActivity {
|
||||
#[cfg(unix)]
|
||||
self.mount_smb_workgroup(params.workgroup.as_deref().unwrap_or(""));
|
||||
}
|
||||
|
||||
fn load_bookmark_webdav_into_gui(&mut self, params: WebDAVProtocolParams) {
|
||||
self.mount_webdav_uri(¶ms.uri);
|
||||
self.mount_username(¶ms.username);
|
||||
self.mount_password(¶ms.password);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use super::{FileTransferProtocol, FormMsg, Msg, UiMsg};
|
||||
use crate::ui::activities::auth::{
|
||||
RADIO_PROTOCOL_FTP, RADIO_PROTOCOL_FTPS, RADIO_PROTOCOL_S3, RADIO_PROTOCOL_SCP,
|
||||
RADIO_PROTOCOL_SFTP, RADIO_PROTOCOL_SMB,
|
||||
RADIO_PROTOCOL_SFTP, RADIO_PROTOCOL_SMB, RADIO_PROTOCOL_WEBDAV,
|
||||
};
|
||||
|
||||
// -- protocol
|
||||
@@ -31,9 +31,9 @@ impl ProtocolRadio {
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.choices(if cfg!(smb) {
|
||||
&["SFTP", "SCP", "FTP", "FTPS", "S3", "SMB"]
|
||||
&["SFTP", "SCP", "FTP", "FTPS", "S3", "WebDAV", "SMB"]
|
||||
} else {
|
||||
&["SFTP", "SCP", "FTP", "FTPS", "S3"]
|
||||
&["SFTP", "SCP", "FTP", "FTPS", "S3", "WebDAV"]
|
||||
})
|
||||
.foreground(color)
|
||||
.rewind(true)
|
||||
@@ -50,6 +50,7 @@ impl ProtocolRadio {
|
||||
RADIO_PROTOCOL_FTPS => FileTransferProtocol::Ftp(true),
|
||||
RADIO_PROTOCOL_S3 => FileTransferProtocol::AwsS3,
|
||||
RADIO_PROTOCOL_SMB => FileTransferProtocol::Smb,
|
||||
RADIO_PROTOCOL_WEBDAV => FileTransferProtocol::WebDAV,
|
||||
_ => FileTransferProtocol::Sftp,
|
||||
}
|
||||
}
|
||||
@@ -63,6 +64,7 @@ impl ProtocolRadio {
|
||||
FileTransferProtocol::Ftp(true) => RADIO_PROTOCOL_FTPS,
|
||||
FileTransferProtocol::AwsS3 => RADIO_PROTOCOL_S3,
|
||||
FileTransferProtocol::Smb => RADIO_PROTOCOL_SMB,
|
||||
FileTransferProtocol::WebDAV => RADIO_PROTOCOL_WEBDAV,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -788,3 +790,40 @@ impl Component<Msg, NoUserEvent> for InputSmbWorkgroup {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MockComponent)]
|
||||
pub struct InputWebDAVUri {
|
||||
component: Input,
|
||||
}
|
||||
|
||||
impl InputWebDAVUri {
|
||||
pub fn new(host: &str, color: Color) -> Self {
|
||||
Self {
|
||||
component: Input::default()
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.placeholder(
|
||||
"http://localhost:8080",
|
||||
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||
)
|
||||
.title("HTTP url", Alignment::Left)
|
||||
.input_type(InputType::Text)
|
||||
.value(host),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for InputWebDAVUri {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
handle_input_ev(
|
||||
self,
|
||||
ev,
|
||||
Msg::Ui(UiMsg::WebDAVUriBlurDown),
|
||||
Msg::Ui(UiMsg::WebDAVUriBlurUp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ pub use form::{
|
||||
InputAddress, InputLocalDirectory, InputPassword, InputPort, InputRemoteDirectory,
|
||||
InputS3AccessKey, InputS3Bucket, InputS3Endpoint, InputS3Profile, InputS3Region,
|
||||
InputS3SecretAccessKey, InputS3SecurityToken, InputS3SessionToken, InputSmbShare,
|
||||
InputUsername, ProtocolRadio, RadioS3NewPathStyle,
|
||||
InputUsername, InputWebDAVUri, ProtocolRadio, RadioS3NewPathStyle,
|
||||
};
|
||||
pub use popup::{
|
||||
ErrorPopup, InfoPopup, InstallUpdatePopup, Keybindings, QuitPopup, ReleaseNotes, WaitPopup,
|
||||
|
||||
@@ -15,6 +15,7 @@ impl AuthActivity {
|
||||
FileTransferProtocol::Ftp(_) => 21,
|
||||
FileTransferProtocol::AwsS3 => 22, // Doesn't matter, since not used
|
||||
FileTransferProtocol::Smb => 445,
|
||||
FileTransferProtocol::WebDAV => 80, // Doesn't matter, since not used
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +42,7 @@ impl AuthActivity {
|
||||
FileTransferProtocol::Ftp(_)
|
||||
| FileTransferProtocol::Scp
|
||||
| FileTransferProtocol::Sftp => self.collect_generic_host_params(self.protocol),
|
||||
FileTransferProtocol::WebDAV => self.collect_webdav_host_params(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +100,19 @@ impl AuthActivity {
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn collect_webdav_host_params(&self) -> Result<FileTransferParams, &'static str> {
|
||||
let params = self.get_webdav_params_input();
|
||||
if params.uri.is_empty() {
|
||||
return Err("Invalid URI");
|
||||
}
|
||||
Ok(FileTransferParams {
|
||||
protocol: FileTransferProtocol::WebDAV,
|
||||
params: ProtocolParams::WebDAV(params),
|
||||
local_path: self.get_input_local_directory(),
|
||||
remote_path: self.get_input_remote_directory(),
|
||||
})
|
||||
}
|
||||
|
||||
// -- update install
|
||||
|
||||
/// If enabled in configuration, check for updates from Github
|
||||
|
||||
@@ -29,7 +29,8 @@ const RADIO_PROTOCOL_SCP: usize = 1;
|
||||
const RADIO_PROTOCOL_FTP: usize = 2;
|
||||
const RADIO_PROTOCOL_FTPS: usize = 3;
|
||||
const RADIO_PROTOCOL_S3: usize = 4;
|
||||
const RADIO_PROTOCOL_SMB: usize = 5;
|
||||
const RADIO_PROTOCOL_WEBDAV: usize = 5;
|
||||
const RADIO_PROTOCOL_SMB: usize = 6;
|
||||
|
||||
// -- components
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
@@ -71,6 +72,7 @@ pub enum Id {
|
||||
Title,
|
||||
Username,
|
||||
WaitPopup,
|
||||
WebDAVUri,
|
||||
WindowSizeError,
|
||||
}
|
||||
|
||||
@@ -155,6 +157,8 @@ pub enum UiMsg {
|
||||
ShowSaveBookmarkPopup,
|
||||
UsernameBlurDown,
|
||||
UsernameBlurUp,
|
||||
WebDAVUriBlurDown,
|
||||
WebDAVUriBlurUp,
|
||||
WindowResized,
|
||||
}
|
||||
|
||||
@@ -164,6 +168,7 @@ enum InputMask {
|
||||
Generic,
|
||||
AwsS3,
|
||||
Smb,
|
||||
WebDAV,
|
||||
}
|
||||
|
||||
// Store keys
|
||||
@@ -240,6 +245,7 @@ impl AuthActivity {
|
||||
| FileTransferProtocol::Scp
|
||||
| FileTransferProtocol::Sftp => InputMask::Generic,
|
||||
FileTransferProtocol::Smb => InputMask::Smb,
|
||||
FileTransferProtocol::WebDAV => InputMask::WebDAV,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ impl AuthActivity {
|
||||
InputMask::Generic => &Id::Password,
|
||||
InputMask::Smb => &Id::Password,
|
||||
InputMask::AwsS3 => &Id::S3Bucket,
|
||||
InputMask::WebDAV => &Id::Password,
|
||||
})
|
||||
.is_ok());
|
||||
}
|
||||
@@ -82,6 +83,7 @@ impl AuthActivity {
|
||||
InputMask::Generic => &Id::Password,
|
||||
InputMask::Smb => &Id::Password,
|
||||
InputMask::AwsS3 => &Id::S3Bucket,
|
||||
InputMask::WebDAV => &Id::Password,
|
||||
})
|
||||
.is_ok());
|
||||
}
|
||||
@@ -177,6 +179,7 @@ impl AuthActivity {
|
||||
#[cfg(windows)]
|
||||
InputMask::Smb => &Id::RemoteDirectory,
|
||||
InputMask::AwsS3 => panic!("this shouldn't happen (password on s3)"),
|
||||
InputMask::WebDAV => &Id::RemoteDirectory,
|
||||
})
|
||||
.is_ok());
|
||||
}
|
||||
@@ -189,7 +192,8 @@ impl AuthActivity {
|
||||
.active(match self.input_mask() {
|
||||
InputMask::Generic => &Id::Username,
|
||||
InputMask::Smb => &Id::SmbShare,
|
||||
InputMask::AwsS3 => panic!("this shouldn't happen (port on s3)"),
|
||||
InputMask::AwsS3 | InputMask::WebDAV =>
|
||||
panic!("this shouldn't happen (port on s3)"),
|
||||
})
|
||||
.is_ok());
|
||||
}
|
||||
@@ -203,6 +207,7 @@ impl AuthActivity {
|
||||
InputMask::Generic => &Id::Address,
|
||||
InputMask::Smb => &Id::Address,
|
||||
InputMask::AwsS3 => &Id::S3Bucket,
|
||||
InputMask::WebDAV => &Id::WebDAVUri,
|
||||
})
|
||||
.is_ok());
|
||||
}
|
||||
@@ -225,6 +230,7 @@ impl AuthActivity {
|
||||
#[cfg(windows)]
|
||||
InputMask::Smb => &Id::Password,
|
||||
InputMask::AwsS3 => &Id::S3NewPathStyle,
|
||||
InputMask::WebDAV => &Id::Password,
|
||||
})
|
||||
.is_ok());
|
||||
}
|
||||
@@ -332,9 +338,16 @@ impl AuthActivity {
|
||||
InputMask::Generic => &Id::Port,
|
||||
InputMask::Smb => &Id::SmbShare,
|
||||
InputMask::AwsS3 => panic!("this shouldn't happen (username on s3)"),
|
||||
InputMask::WebDAV => &Id::WebDAVUri,
|
||||
})
|
||||
.is_ok());
|
||||
}
|
||||
UiMsg::WebDAVUriBlurDown => {
|
||||
assert!(self.app.active(&Id::Username).is_ok());
|
||||
}
|
||||
UiMsg::WebDAVUriBlurUp => {
|
||||
assert!(self.app.active(&Id::Protocol).is_ok());
|
||||
}
|
||||
UiMsg::WindowResized => {
|
||||
self.redraw = true;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ use tuirealm::tui::widgets::Clear;
|
||||
use tuirealm::{State, StateValue, Sub, SubClause, SubEventClause};
|
||||
|
||||
use super::{components, AuthActivity, Context, FileTransferProtocol, Id, InputMask};
|
||||
use crate::filetransfer::params::{AwsS3Params, GenericProtocolParams, ProtocolParams, SmbParams};
|
||||
use crate::filetransfer::params::{
|
||||
AwsS3Params, GenericProtocolParams, ProtocolParams, SmbParams, WebDAVProtocolParams,
|
||||
};
|
||||
use crate::filetransfer::FileTransferParams;
|
||||
use crate::utils::ui::{Popup, Size};
|
||||
|
||||
@@ -61,6 +63,7 @@ impl AuthActivity {
|
||||
self.mount_smb_share("");
|
||||
#[cfg(unix)]
|
||||
self.mount_smb_workgroup("");
|
||||
self.mount_webdav_uri("");
|
||||
// Version notice
|
||||
if let Some(version) = self
|
||||
.context()
|
||||
@@ -195,6 +198,18 @@ impl AuthActivity {
|
||||
)
|
||||
.direction(Direction::Vertical)
|
||||
.split(auth_chunks[4]),
|
||||
InputMask::WebDAV => Layout::default()
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Length(3), // uri
|
||||
Constraint::Length(3), // username
|
||||
Constraint::Length(3), // password
|
||||
Constraint::Length(3), // dir
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.direction(Direction::Vertical)
|
||||
.split(auth_chunks[4]),
|
||||
};
|
||||
// Create bookmark chunks
|
||||
let bookmark_chunks = Layout::default()
|
||||
@@ -230,6 +245,13 @@ impl AuthActivity {
|
||||
self.app.view(&view_ids[2], f, input_mask[2]);
|
||||
self.app.view(&view_ids[3], f, input_mask[3]);
|
||||
}
|
||||
InputMask::WebDAV => {
|
||||
let view_ids = self.get_webdav_view();
|
||||
self.app.view(&view_ids[0], f, input_mask[0]);
|
||||
self.app.view(&view_ids[1], f, input_mask[1]);
|
||||
self.app.view(&view_ids[2], f, input_mask[2]);
|
||||
self.app.view(&view_ids[3], f, input_mask[3]);
|
||||
}
|
||||
}
|
||||
// Bookmark chunks
|
||||
self.app.view(&Id::BookmarksList, f, bookmark_chunks[0]);
|
||||
@@ -794,6 +816,18 @@ impl AuthActivity {
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
pub(super) fn mount_webdav_uri(&mut self, uri: &str) {
|
||||
let addr_color = self.theme().auth_address;
|
||||
assert!(self
|
||||
.app
|
||||
.remount(
|
||||
Id::WebDAVUri,
|
||||
Box::new(components::InputWebDAVUri::new(uri, addr_color)),
|
||||
vec![]
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
// -- query
|
||||
|
||||
/// Collect input values from view
|
||||
@@ -860,6 +894,18 @@ impl AuthActivity {
|
||||
.password(password)
|
||||
}
|
||||
|
||||
pub(super) fn get_webdav_params_input(&self) -> WebDAVProtocolParams {
|
||||
let uri: String = self.get_webdav_uri();
|
||||
let username = self.get_input_username().unwrap_or_default();
|
||||
let password = self.get_input_password().unwrap_or_default();
|
||||
|
||||
WebDAVProtocolParams {
|
||||
uri,
|
||||
username,
|
||||
password,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_input_remote_directory(&self) -> Option<PathBuf> {
|
||||
match self.app.state(&Id::RemoteDirectory) {
|
||||
Ok(State::One(StateValue::String(x))) if !x.is_empty() => {
|
||||
@@ -878,6 +924,13 @@ impl AuthActivity {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_webdav_uri(&self) -> String {
|
||||
match self.app.state(&Id::WebDAVUri) {
|
||||
Ok(State::One(StateValue::String(x))) => x,
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_input_addr(&self) -> String {
|
||||
match self.app.state(&Id::Address) {
|
||||
Ok(State::One(StateValue::String(x))) => x,
|
||||
@@ -1011,6 +1064,7 @@ impl AuthActivity {
|
||||
InputMask::AwsS3 => 12,
|
||||
InputMask::Generic => 12,
|
||||
InputMask::Smb => 12,
|
||||
InputMask::WebDAV => 12,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1069,6 +1123,7 @@ impl AuthActivity {
|
||||
};
|
||||
format!("\\\\{username}{}\\{}", params.address, params.share)
|
||||
}
|
||||
ProtocolParams::WebDAV(params) => params.uri,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1180,6 +1235,23 @@ impl AuthActivity {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_webdav_view(&self) -> [Id; 4] {
|
||||
match self.app.focus() {
|
||||
Some(&Id::LocalDirectory) => [
|
||||
Id::Username,
|
||||
Id::Password,
|
||||
Id::RemoteDirectory,
|
||||
Id::LocalDirectory,
|
||||
],
|
||||
_ => [
|
||||
Id::WebDAVUri,
|
||||
Id::Username,
|
||||
Id::Password,
|
||||
Id::RemoteDirectory,
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn init_global_listener(&mut self) {
|
||||
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
assert!(self
|
||||
|
||||
@@ -7,7 +7,7 @@ use tuirealm::props::{
|
||||
Alignment, AttrValue, Attribute, Borders, Color, Style, Table, TextModifiers,
|
||||
};
|
||||
use tuirealm::tui::layout::Corner;
|
||||
use tuirealm::tui::text::{Span, Spans};
|
||||
use tuirealm::tui::text::{Line, Span};
|
||||
use tuirealm::tui::widgets::{List as TuiList, ListItem, ListState};
|
||||
use tuirealm::{MockComponent, Props, State, StateValue};
|
||||
|
||||
@@ -211,7 +211,7 @@ impl MockComponent for FileList {
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
ListItem::new(Spans::from(columns))
|
||||
ListItem::new(Line::from(columns))
|
||||
})
|
||||
.collect(), // Make List item from TextSpan
|
||||
_ => Vec::new(),
|
||||
@@ -245,7 +245,7 @@ impl MockComponent for FileList {
|
||||
if matches!(attr, Attribute::Content) {
|
||||
self.states.init_list_states(
|
||||
match self.props.get(Attribute::Content).map(|x| x.unwrap_table()) {
|
||||
Some(spans) => spans.len(),
|
||||
Some(line) => line.len(),
|
||||
_ => 0,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -112,6 +112,7 @@ impl FileTransferActivity {
|
||||
ProtocolParams::Generic(params) => params.address.clone(),
|
||||
ProtocolParams::AwsS3(params) => params.bucket_name.clone(),
|
||||
ProtocolParams::Smb(params) => params.address.clone(),
|
||||
ProtocolParams::WebDAV(params) => params.uri.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +142,13 @@ impl FileTransferActivity {
|
||||
);
|
||||
format!("Connecting to \\\\{}\\{}…", params.address, params.share)
|
||||
}
|
||||
ProtocolParams::WebDAV(params) => {
|
||||
info!(
|
||||
"Client is not connected to remote; connecting to {}",
|
||||
params.uri
|
||||
);
|
||||
format!("Connecting to {}…", params.uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::explorer::GroupDirs as GroupDirsEnum;
|
||||
use crate::filetransfer::FileTransferProtocol;
|
||||
use crate::ui::activities::setup::{
|
||||
RADIO_PROTOCOL_FTP, RADIO_PROTOCOL_FTPS, RADIO_PROTOCOL_S3, RADIO_PROTOCOL_SCP,
|
||||
RADIO_PROTOCOL_SFTP, RADIO_PROTOCOL_SMB,
|
||||
RADIO_PROTOCOL_SFTP, RADIO_PROTOCOL_SMB, RADIO_PROTOCOL_WEBDAV,
|
||||
};
|
||||
use crate::utils::parser::parse_bytesize;
|
||||
|
||||
@@ -67,7 +67,7 @@ impl DefaultProtocol {
|
||||
.color(Color::Cyan)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.choices(&["SFTP", "SCP", "FTP", "FTPS", "S3", "SMB"])
|
||||
.choices(&["SFTP", "SCP", "FTP", "FTPS", "S3", "SMB", "WebDAV"])
|
||||
.foreground(Color::Cyan)
|
||||
.rewind(true)
|
||||
.title("Default protocol", Alignment::Left)
|
||||
@@ -78,6 +78,7 @@ impl DefaultProtocol {
|
||||
FileTransferProtocol::Ftp(true) => RADIO_PROTOCOL_FTPS,
|
||||
FileTransferProtocol::AwsS3 => RADIO_PROTOCOL_S3,
|
||||
FileTransferProtocol::Smb => RADIO_PROTOCOL_SMB,
|
||||
FileTransferProtocol::WebDAV => RADIO_PROTOCOL_WEBDAV,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ const RADIO_PROTOCOL_FTP: usize = 2;
|
||||
const RADIO_PROTOCOL_FTPS: usize = 3;
|
||||
const RADIO_PROTOCOL_S3: usize = 4;
|
||||
const RADIO_PROTOCOL_SMB: usize = 5;
|
||||
const RADIO_PROTOCOL_WEBDAV: usize = 6;
|
||||
|
||||
// -- components
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
|
||||
@@ -7,9 +7,6 @@ pub mod setup;
|
||||
pub mod ssh_keys;
|
||||
pub mod theme;
|
||||
|
||||
pub use setup::*;
|
||||
pub use ssh_keys::*;
|
||||
pub use theme::*;
|
||||
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::tui::widgets::Clear;
|
||||
use tuirealm::{Frame, Sub, SubClause, SubEventClause};
|
||||
|
||||
@@ -10,7 +10,9 @@ use std::path::PathBuf;
|
||||
use tuirealm::tui::layout::{Constraint, Direction, Layout};
|
||||
use tuirealm::{State, StateValue};
|
||||
|
||||
use super::{components, Context, Id, IdCommon, IdConfig, SetupActivity, ViewLayout};
|
||||
use super::{
|
||||
components, Context, Id, IdCommon, IdConfig, SetupActivity, ViewLayout, RADIO_PROTOCOL_WEBDAV,
|
||||
};
|
||||
use crate::explorer::GroupDirs;
|
||||
use crate::filetransfer::FileTransferProtocol;
|
||||
use crate::ui::activities::setup::{
|
||||
@@ -277,6 +279,7 @@ impl SetupActivity {
|
||||
RADIO_PROTOCOL_FTPS => FileTransferProtocol::Ftp(true),
|
||||
RADIO_PROTOCOL_S3 => FileTransferProtocol::AwsS3,
|
||||
RADIO_PROTOCOL_SMB => FileTransferProtocol::Smb,
|
||||
RADIO_PROTOCOL_WEBDAV => FileTransferProtocol::WebDAV,
|
||||
_ => FileTransferProtocol::Sftp,
|
||||
};
|
||||
self.config_mut().set_default_protocol(protocol);
|
||||
|
||||
@@ -14,7 +14,9 @@ use tuirealm::utils::parser as tuirealm_parser;
|
||||
|
||||
#[cfg(smb)]
|
||||
use crate::filetransfer::params::SmbParams;
|
||||
use crate::filetransfer::params::{AwsS3Params, GenericProtocolParams, ProtocolParams};
|
||||
use crate::filetransfer::params::{
|
||||
AwsS3Params, GenericProtocolParams, ProtocolParams, WebDAVProtocolParams,
|
||||
};
|
||||
use crate::filetransfer::{FileTransferParams, FileTransferProtocol};
|
||||
#[cfg(not(test))] // NOTE: don't use configuration during tests
|
||||
use crate::system::config_client::ConfigClient;
|
||||
@@ -43,6 +45,16 @@ static REMOTE_GENERIC_OPT_REGEX: Lazy<Regex> = lazy_regex!(
|
||||
r"(?:([^@]+)@)?(?:([^:]+))(?::((?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])))?(?::([^:]+))?"
|
||||
);
|
||||
|
||||
/**
|
||||
* Regex matches:
|
||||
* - group 1: Username
|
||||
* - group 2: Password
|
||||
* - group 2: Uri
|
||||
* - group 4: Some(path) | None
|
||||
*/
|
||||
static REMOTE_WEBDAV_OPT_REGEX: Lazy<Regex> =
|
||||
lazy_regex!(r"(?:([^:]+):)(?:([^@]+)@)(?:([^/]+))(?:/(.+))?");
|
||||
|
||||
/**
|
||||
* Regex matches:
|
||||
* - group 1: Bucket
|
||||
@@ -145,14 +157,24 @@ pub fn parse_remote_opt(s: &str) -> Result<FileTransferParams, String> {
|
||||
#[cfg(test)] // NOTE: during test set protocol just to Sftp
|
||||
let default_protocol: FileTransferProtocol = FileTransferProtocol::Sftp;
|
||||
// Get protocol
|
||||
let (protocol, s): (FileTransferProtocol, String) =
|
||||
let (protocol, remote): (FileTransferProtocol, String) =
|
||||
parse_remote_opt_protocol(s, default_protocol)?;
|
||||
// Match against regex for protocol type
|
||||
match protocol {
|
||||
FileTransferProtocol::AwsS3 => parse_s3_remote_opt(s.as_str()),
|
||||
FileTransferProtocol::AwsS3 => parse_s3_remote_opt(remote.as_str()),
|
||||
#[cfg(smb)]
|
||||
FileTransferProtocol::Smb => parse_smb_remote_opts(s.as_str()),
|
||||
protocol => parse_generic_remote_opt(s.as_str(), protocol),
|
||||
FileTransferProtocol::Smb => parse_smb_remote_opts(remote.as_str()),
|
||||
FileTransferProtocol::WebDAV => {
|
||||
// get the differnece between s and remote
|
||||
let prefix = if s.starts_with("https") {
|
||||
"https"
|
||||
} else {
|
||||
"http"
|
||||
};
|
||||
|
||||
parse_webdav_remote_opt(remote.as_str(), prefix)
|
||||
}
|
||||
protocol => parse_generic_remote_opt(remote.as_str(), protocol),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +254,29 @@ fn parse_generic_remote_opt(
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_webdav_remote_opt(s: &str, prefix: &str) -> Result<FileTransferParams, String> {
|
||||
match REMOTE_WEBDAV_OPT_REGEX.captures(s) {
|
||||
Some(groups) => {
|
||||
let username = groups.get(1).map(|x| x.as_str().to_string()).unwrap();
|
||||
let password = groups.get(2).map(|x| x.as_str().to_string()).unwrap();
|
||||
let uri = groups.get(3).map(|x| x.as_str().to_string()).unwrap();
|
||||
let remote_path: Option<PathBuf> =
|
||||
groups.get(4).map(|group| PathBuf::from(group.as_str()));
|
||||
|
||||
let params = ProtocolParams::WebDAV(WebDAVProtocolParams {
|
||||
uri: format!("{}://{}", prefix, uri),
|
||||
username,
|
||||
password,
|
||||
});
|
||||
Ok(
|
||||
FileTransferParams::new(FileTransferProtocol::WebDAV, params)
|
||||
.remote_path(remote_path),
|
||||
)
|
||||
}
|
||||
None => Err(String::from("Bad remote host syntax!")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse remote options for s3 protocol
|
||||
fn parse_s3_remote_opt(s: &str) -> Result<FileTransferParams, String> {
|
||||
match REMOTE_S3_OPT_REGEX.captures(s) {
|
||||
@@ -553,6 +598,25 @@ mod tests {
|
||||
assert!(parse_remote_opt(&String::from("scp://172.26.104.1:650000")).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_should_parse_webdav_opt() {
|
||||
let result =
|
||||
parse_remote_opt("https://omar:password@myserver:4445/myshare/dir/subdir").unwrap();
|
||||
|
||||
let params = result.params.webdav_params().unwrap();
|
||||
assert_eq!(params.uri.as_str(), "https://myserver:4445");
|
||||
assert_eq!(params.username.as_str(), "omar");
|
||||
assert_eq!(params.password.as_str(), "password");
|
||||
|
||||
let result =
|
||||
parse_remote_opt("http://omar:password@myserver:4445/myshare/dir/subdir").unwrap();
|
||||
|
||||
let params = result.params.webdav_params().unwrap();
|
||||
assert_eq!(params.uri.as_str(), "http://myserver:4445");
|
||||
assert_eq!(params.username.as_str(), "omar");
|
||||
assert_eq!(params.password.as_str(), "password");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_aws_s3_opt() {
|
||||
// Simple
|
||||
|
||||
@@ -60,8 +60,8 @@ where
|
||||
}
|
||||
(None, _) => comps.push(Component::ParentDir),
|
||||
(Some(a), Some(b)) if comps.is_empty() && a == b => (),
|
||||
(Some(a), Some(b)) if b == Component::CurDir => comps.push(a),
|
||||
(Some(_), Some(b)) if b == Component::ParentDir => return None,
|
||||
(Some(a), Some(Component::CurDir)) => comps.push(a),
|
||||
(Some(_), Some(Component::ParentDir)) => return None,
|
||||
(Some(a), Some(_)) => {
|
||||
comps.push(Component::ParentDir);
|
||||
for _ in itb {
|
||||
|
||||
@@ -67,7 +67,7 @@ mod tests {
|
||||
let child: Rect = Popup(Size::Percentage(75), Size::Percentage(30)).draw_in(area);
|
||||
assert_eq!(child.x, 43);
|
||||
assert_eq!(child.y, 63);
|
||||
assert_eq!(child.width, 271);
|
||||
assert_eq!(child.height, 54);
|
||||
assert_eq!(child.width, 272);
|
||||
assert_eq!(child.height, 55);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user