Merge pull request #236 from veeso/develop

0.13.0
This commit is contained in:
Christian Visintin
2024-03-02 20:27:16 +01:00
committed by GitHub
52 changed files with 1186 additions and 394 deletions

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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"]

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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.

View File

@@ -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 :

View File

@@ -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.

View File

@@ -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:

View File

@@ -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

View File

@@ -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 在远程和本地文件系统上浏览和操作
- 创建、删除、重命名、搜索、查看和编辑文件
- ⭐ 通过“内置书签”和“最近连接”快速连接到您的主机

View File

@@ -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 还是其他系统上,这都是不同的:

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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>&nbsp;
<span translate="intro.versionAlert">termscp 0.13.0 is NOW out! Download it from</span>&nbsp;
<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>

View File

@@ -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>

View File

@@ -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": {

View File

@@ -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": {

View File

@@ -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": {

View File

@@ -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": {

View File

@@ -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": {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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(&params.username, &params.password, &params.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())

View File

@@ -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")
);
}
}

View File

@@ -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 =

View File

@@ -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]

View File

@@ -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
}

View File

@@ -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]

View File

@@ -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
})

View File

@@ -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(&params.uri);
self.mount_username(&params.username);
self.mount_password(&params.password);
}
}

View File

@@ -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),
)
}
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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,
}
}
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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,
},
);

View File

@@ -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)
}
}
}

View File

@@ -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,
}),
}
}

View File

@@ -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)]

View File

@@ -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};

View File

@@ -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);

View File

@@ -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

View File

@@ -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 {

View File

@@ -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);
}
}