Compare commits
120 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f48c765e3 | ||
|
|
ca005cbecd | ||
|
|
ee28d34f29 | ||
|
|
295191b9eb | ||
|
|
16b2aa8596 | ||
|
|
522ee0355e | ||
|
|
e5ca956897 | ||
|
|
a071f3d420 | ||
|
|
66888c418c | ||
|
|
b5f712fa04 | ||
|
|
1509401599 | ||
|
|
6506f4ae59 | ||
|
|
5a5fa52b57 | ||
|
|
b7d4c68ebe | ||
|
|
00a6c53178 | ||
|
|
67bc1a2a08 | ||
|
|
0bb3c9a26e | ||
|
|
4394941c71 | ||
|
|
175ea23bfc | ||
|
|
cef100e5d9 | ||
|
|
afba6c32c5 | ||
|
|
31f5bd095b | ||
|
|
e1add0ed28 | ||
|
|
79bc6a684b | ||
|
|
bae4db8e27 | ||
|
|
4cbba3f3cb | ||
|
|
e9fbb7f317 | ||
|
|
044f2436f8 | ||
|
|
b7369162d2 | ||
|
|
a13663e5e9 | ||
|
|
79dd9e2303 | ||
|
|
b4fa50a666 | ||
|
|
5a67fc7b0e | ||
|
|
9dab34e7ee | ||
|
|
c5eeae74b7 | ||
|
|
9009002b6e | ||
|
|
9bf23b3f6b | ||
|
|
3ec94c17bc | ||
|
|
ed8ba628e5 | ||
|
|
7c638473d6 | ||
|
|
d37140ead6 | ||
|
|
fdb0d97a61 | ||
|
|
0c1e9c0d60 | ||
|
|
e1a2255a58 | ||
|
|
f6b2c814c0 | ||
|
|
efb30231ee | ||
|
|
8cb6c19210 | ||
|
|
51c2618b5a | ||
|
|
279df59b5d | ||
|
|
d8373d0eba | ||
|
|
74fe97f2e2 | ||
|
|
e62f15f375 | ||
|
|
21dc73b556 | ||
|
|
a10325f6fb | ||
|
|
1ecef6fc16 | ||
|
|
67df3dc76a | ||
|
|
7ecfaea278 | ||
|
|
97151d18bf | ||
|
|
7ad7a1580d | ||
|
|
46f2954bc4 | ||
|
|
0c88f559e2 | ||
|
|
8a0979578a | ||
|
|
7ee28a00ae | ||
|
|
82330f870e | ||
|
|
54aecdc478 | ||
|
|
d5038a22ee | ||
|
|
c0bc493d21 | ||
|
|
1c75c7d386 | ||
|
|
251b125cbb | ||
|
|
efd2235ff3 | ||
|
|
1db2ff7ec5 | ||
|
|
4d5f3a6b63 | ||
|
|
62003308a7 | ||
|
|
cb037cfca9 | ||
|
|
15fffdf0c9 | ||
|
|
c1b245b4c9 | ||
|
|
d14225c0b9 | ||
|
|
d60be7dc80 | ||
|
|
146a1f3ed6 | ||
|
|
12fe10100b | ||
|
|
298d590306 | ||
|
|
8e6aae08b6 | ||
|
|
cb33ba114e | ||
|
|
6369ead491 | ||
|
|
ae350b4bfa | ||
|
|
cd5bb28fb7 | ||
|
|
f27a4e2a02 | ||
|
|
0f432d0375 | ||
|
|
8b67c59247 | ||
|
|
fdd7901f7e | ||
|
|
2eec7c4b2b | ||
|
|
64e3848c97 | ||
|
|
833cd7d3ba | ||
|
|
2f1b644a40 | ||
|
|
6a5c248d35 | ||
|
|
96df152220 | ||
|
|
a22c5025d7 | ||
|
|
cce0c92c0b | ||
|
|
90d7083b24 | ||
|
|
b3ab76e573 | ||
|
|
66913d5fc1 | ||
|
|
f5174ccac0 | ||
|
|
82489e7459 | ||
|
|
ad562a7b09 | ||
|
|
08c42e5acf | ||
|
|
aac9cf4fa0 | ||
|
|
a26b21da89 | ||
|
|
bb95c9a4d0 | ||
|
|
80dea3f71a | ||
|
|
0057a657d2 | ||
|
|
816270d545 | ||
|
|
2caa0432df | ||
|
|
e0d8b80cdf | ||
|
|
f094979ddb | ||
|
|
0c8144aaa8 | ||
|
|
3249e82873 | ||
|
|
66adad3135 | ||
|
|
f2e5bf4441 | ||
|
|
601b0595d4 | ||
|
|
c47fcb2225 |
1
.github/FUNDING.yml
vendored
@@ -1 +0,0 @@
|
||||
ko_fi: veeso
|
||||
1
.github/actions-rs/grcov.yml
vendored
@@ -8,6 +8,7 @@ ignore:
|
||||
- "../*"
|
||||
- src/main.rs
|
||||
- src/activity_manager.rs
|
||||
- src/cli_opts.rs
|
||||
- src/support.rs
|
||||
- src/system/notifications.rs
|
||||
- "src/ui/activities/*"
|
||||
|
||||
42
.github/workflows/build-artifacts.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: "Build artifacts"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-binaries:
|
||||
name: Build - ${{ matrix.platform.release_for }}
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- release_for: MacOS-x86_64
|
||||
os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
script: macos.sh
|
||||
|
||||
- release_for: MacOS-M1
|
||||
os: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
script: macos.sh
|
||||
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
target: ${{ matrix.platform.target }}
|
||||
- name: Build release
|
||||
run: cargo build --release --target ${{ matrix.platform.target }}
|
||||
- name: Prepare artifact files
|
||||
run: |
|
||||
mkdir -p .artifact
|
||||
mv target/${{ matrix.platform.target }}/release/termscp .artifact/termscp
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
name: ${{ matrix.platform.release_for }}
|
||||
path: .artifact/*
|
||||
12
.github/workflows/coverage.yml
vendored
@@ -1,6 +1,14 @@
|
||||
name: Coverage
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
push:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -12,7 +20,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install dependencies
|
||||
run: sudo apt update && sudo apt install -y libdbus-1-dev libssh2-1-dev libssl-dev
|
||||
run: sudo apt update && sudo apt install -y libdbus-1-dev libsmbclient-dev libsmbclient
|
||||
- name: Setup nightly toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
||||
23
.github/workflows/install.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Install.sh
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install dependencies
|
||||
run: sudo apt update && sudo apt install -y curl wget libsmbclient
|
||||
- name: Install termscp from script
|
||||
run: |
|
||||
./install.sh -v=0.12.1 -f
|
||||
which termscp || exit 1
|
||||
12
.github/workflows/linux.yml
vendored
@@ -1,6 +1,14 @@
|
||||
name: Linux
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
push:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -12,7 +20,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install dependencies
|
||||
run: sudo apt update && sudo apt install -y libdbus-1-dev libssh2-1-dev libssl-dev
|
||||
run: sudo apt update && sudo apt install -y libdbus-1-dev libsmbclient-dev
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
11
.github/workflows/macos.yml
vendored
@@ -1,6 +1,14 @@
|
||||
name: MacOS
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
push:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -8,7 +16,6 @@ env:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
|
||||
23
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Close inactive issues
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
|
||||
jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v4.1.1
|
||||
with:
|
||||
days-before-issue-stale: 30
|
||||
days-before-issue-close: 7
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
|
||||
close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale."
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
exempt-issue-labels: "backlog"
|
||||
43
.github/workflows/website.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Simple workflow for deploying static content to GitHub Pages
|
||||
name: Deploy static content to Pages
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "./site/**/*"
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow one concurrent deployment
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Single deploy job since we're just deploying
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v2
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
with:
|
||||
path: "./site/"
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
10
.github/workflows/windows.yml
vendored
@@ -1,6 +1,14 @@
|
||||
name: Windows
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
push:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
162
CHANGELOG.md
@@ -1,6 +1,15 @@
|
||||
# Changelog
|
||||
|
||||
- [Changelog](#changelog)
|
||||
- [0.12.1](#0121)
|
||||
- [0.12.0](#0120)
|
||||
- [0.11.3](#0113)
|
||||
- [0.11.2](#0112)
|
||||
- [0.11.1](#0111)
|
||||
- [0.11.0](#0110)
|
||||
- [0.10.0](#0100)
|
||||
- [0.9.0](#090)
|
||||
- [0.8.2](#082)
|
||||
- [0.8.1](#081)
|
||||
- [0.8.0](#080)
|
||||
- [0.7.0](#070)
|
||||
@@ -23,6 +32,159 @@
|
||||
|
||||
---
|
||||
|
||||
## 0.12.1
|
||||
|
||||
Released on 06/07/2023
|
||||
|
||||
- [Issue 169](https://github.com/veeso/termscp/issues/169): Local working directory can now be specified in connection form and be saved into bookmarks.
|
||||
- [Issue 188](https://github.com/veeso/termscp/issues/188): The breadcrumbs path is not fallbacked after a failed enter into the directory
|
||||
- SMB support is now a feature (you can build rust without default features to disable smb).
|
||||
- If SSH connection timeout is 0, the connection won't timeout.
|
||||
|
||||
## 0.12.0
|
||||
|
||||
Released on 16/05/2023
|
||||
|
||||
- **Change file permissions**: you can now change file permissions easily with the permissions popup pressing `Z` in the explorer.
|
||||
- [Issue 172](https://github.com/veeso/termscp/issues/172)
|
||||
- **SMB protocol**: Support for SMB protocol has been added thanks to the [remotefs-smb](https://github.com/veeso/remotefs-rs-smb) library and the [pavao](https://github.com/veeso/pavao) project. You may notice that the interface is quiet different between Windows and Linux/MacOs/BSD due to the fact that SMB is natively supported on Windows systems.
|
||||
- [Issue 182](https://github.com/veeso/termscp/issues/182)
|
||||
- [Issue 153](https://github.com/veeso/termscp/issues/153): show a loading message when loading directory's content
|
||||
- [Issue 176](https://github.com/veeso/termscp/issues/176): debug log is now written to CACHE_DIR
|
||||
- [Issue 173](https://github.com/veeso/termscp/issues/173): allow unknown fields in ssh2 configuration file
|
||||
- [Issue 175](https://github.com/veeso/termscp/issues/175): don't prompt for password if a ssh key is set for that host
|
||||
- Fixed an issue that didn't use the `User` specified in ssh2-config
|
||||
|
||||
## 0.11.3
|
||||
|
||||
Released on 19/04/2023
|
||||
|
||||
- [Issue 166](https://github.com/veeso/termscp/issues/166): fixed SCP relative paths on Windows
|
||||
|
||||
## 0.11.2
|
||||
|
||||
Released on 18/04/2023
|
||||
|
||||
- [Issue 154](https://github.com/veeso/termscp/issues/154): fixed SCP relative paths on Windows
|
||||
|
||||
## 0.11.1
|
||||
|
||||
Released on 07/03/2022
|
||||
|
||||
- [Issue 150](https://github.com/veeso/termscp/issues/150)
|
||||
- fixed config directory not being created
|
||||
- before setting default ssh config path; check wheter it actually exists
|
||||
- Security:
|
||||
- removed `remove_dir_all` crate with `tempfile 3.4`
|
||||
- Dependencies:
|
||||
- Bump `ssh2-config` to `0.1.6`
|
||||
|
||||
## 0.11.0
|
||||
|
||||
Released on 20/02/2023
|
||||
|
||||
> 🦥 The lazy update
|
||||
|
||||
- **Transfers optimized**:
|
||||
- If local/remote file have the same "last modification time" (`mtime`), the file is not transferred
|
||||
- When the file is exchanged, all times attributes are set (if supported by the protocol)
|
||||
- **Default ssh config path**:
|
||||
- SSH configuration path is now `~/.ssh/config` by default
|
||||
- Added ARM64 Linux builds
|
||||
- **Bugfix**:
|
||||
- Fixed [Issue 126](https://github.com/veeso/termscp/issues/126)
|
||||
- Fixed [Issue 141](https://github.com/veeso/termscp/issues/141)
|
||||
- Dependencies:
|
||||
- Bump `remotefs-ssh` to `0.1.3`
|
||||
- Bump `self_update` to `0.35`
|
||||
- Bump `ssh2-config` to `0.1.4`
|
||||
- Bump `toml` to `0.7`
|
||||
|
||||
## 0.10.0
|
||||
|
||||
Released on 15/10/2022
|
||||
|
||||
> ⭐ 500 stars update ⭐
|
||||
> Thank you for supporting termscp and make it reaching 500 stars on Github
|
||||
|
||||
- **Changed keybindings for BACKTAB**: backtab will now change the explorer tab
|
||||
- To active the LOG PANEL, use `P`
|
||||
- **Yes/No dialogs** are now answerable by pressing `Y` or `N` on your keyboard ([Issue 121](https://github.com/veeso/termscp/issues/121))
|
||||
- **Use ssh2 config IdentityFile** as fallback for key based authentication
|
||||
- **Bugfix**
|
||||
- Fixed [Issue 122](https://github.com/veeso/termscp/issues/122)
|
||||
- Fixed version comparison when going above 0.9
|
||||
- Dependencies:
|
||||
- Bump `argh` to `0.1.9`
|
||||
- Bump `chrono` to `0.4.22`
|
||||
- Bump `keyring` to `1.2.0`
|
||||
- Bump `notify-rust` to `4.5.10`
|
||||
- Bump `open` to `3.0.3`
|
||||
- Bump `rpassword` to `7.0.0`
|
||||
- Changed `regex` to `lazy-regex 2.3.0`
|
||||
- Bump `remotefs-ftp` to `0.1.2`
|
||||
- Bump `remotefs-ssh` to `0.1.2`
|
||||
- Bump `self_update` to `0.32`
|
||||
- Bump `ssh2-config` to `0.1.3`
|
||||
- Bump `tuirealm` to `1.8.0`
|
||||
- Bump `tui-realm-stdlib` to `1.1.7`
|
||||
- Bump `unicode-width` to `0.1.10`
|
||||
- Added `version-compare 0.1.0`
|
||||
- Bump `whoami` to `1.2.3`
|
||||
- Bump `wildmatch` to `2.1.1`
|
||||
- Removed libssl dependency
|
||||
|
||||
## 0.9.0
|
||||
|
||||
Released on 18/06/2022
|
||||
|
||||
> 🏖️ Tenerife Update 🍹
|
||||
|
||||
- **Bookmark name as hostname for CLI arguments**
|
||||
- It is now possible to provide the name of the bookmark you want to connect to, instead of the address in command line arguments
|
||||
|
||||
To do so it is enough to run termscp as follows:
|
||||
|
||||
```sh
|
||||
termscp -b <bookmark-name>
|
||||
```
|
||||
|
||||
If the password is stored in the bookmark, it will be used, otherwise you will be prompted to type the password in.
|
||||
- **Remote directory path in authentication form and in bookmarks parameters**:
|
||||
- It is now possible to configure the directory path you want to enter when you connect to the remote host from the authentication form
|
||||
- This parameter can be stored into bookmarks as you already do with the other parameters
|
||||
- You can find this field scrolling down in the authentication form
|
||||
- **File system watcher**:
|
||||
- It is now possible to synchronize changes from a local path to the remote host
|
||||
- Press `<T>` to start synchronizing changes from the selected directory/file to the remote directory
|
||||
- The changes will be automatically applied to the remote host with a maximum delay of 5 seconds
|
||||
- These changes are (if possible) applied: file removed, file changed, file renamed
|
||||
- Press `<CTRL+T>` to show all the currently synchronized files
|
||||
- **Enhancements**:
|
||||
- Improved s3 auth form scrolling
|
||||
- **Bugfix**:
|
||||
- Fixed SSH key list showing `{hostname} at {username}` instead of `{username} at {hostname}`
|
||||
- Dependencies:
|
||||
- Updated `edit` to `0.1.4`
|
||||
- Updated `log` to `0.4.17`
|
||||
- Updated `magic-crypt` to `3.1.10`
|
||||
- Updated `open` to `2.1.3`
|
||||
- Updated `regex` to `1.5.6`
|
||||
- Updated `rpassword` to `6.0.1`
|
||||
- Updated `self_update` to `0.30.0`
|
||||
- Updated `simplelog` to `0.12.0`
|
||||
- Updated `toml` to `0.5.9`
|
||||
- Updated `tui-realm` to `1.6.0`
|
||||
|
||||
## 0.8.2
|
||||
|
||||
Released on 26/04/2022
|
||||
|
||||
- **Enhancements**
|
||||
- Write exitcode to log when termscp terminates
|
||||
- Bugfix:
|
||||
- [Issue 104](https://github.com/veeso/termscp/issues/104): Fixed termscp panics when displaying long non-ascii filenames
|
||||
|
||||
## 0.8.1
|
||||
|
||||
Released on 22/03/2022
|
||||
|
||||
2643
Cargo.lock
generated
115
Cargo.toml
@@ -1,16 +1,22 @@
|
||||
[package]
|
||||
authors = ["Christian Visintin"]
|
||||
authors = ["Christian Visintin <christian.visintin@veeso.dev>"]
|
||||
categories = ["command-line-utilities"]
|
||||
description = "termscp is a feature rich terminal file transfer and explorer with support for SCP/SFTP/FTP/S3"
|
||||
edition = "2021"
|
||||
homepage = "https://veeso.github.io/termscp/"
|
||||
homepage = "https://termscp.veeso.dev"
|
||||
include = ["src/**/*", "LICENSE", "README.md", "CHANGELOG.md"]
|
||||
keywords = ["scp-client", "sftp-client", "ftp-client", "winscp", "command-line-utility"]
|
||||
keywords = [
|
||||
"scp-client",
|
||||
"sftp-client",
|
||||
"ftp-client",
|
||||
"winscp",
|
||||
"command-line-utility",
|
||||
]
|
||||
license = "MIT"
|
||||
name = "termscp"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/veeso/termscp"
|
||||
version = "0.8.1"
|
||||
version = "0.12.1"
|
||||
|
||||
[package.metadata.rpm]
|
||||
package = "termscp"
|
||||
@@ -22,8 +28,8 @@ buildflags = ["--release"]
|
||||
termscp = { path = "/usr/bin/termscp" }
|
||||
|
||||
[package.metadata.deb]
|
||||
maintainer = "Christian Visintin <christian.visintin1997@gmail.com>"
|
||||
copyright = "2022, Christian Visintin <christian.visintin1997@gmail.com>"
|
||||
maintainer = "Christian Visintin <christian.visintin@veeso.dev>"
|
||||
copyright = "2022, Christian Visintin <christian.visintin@veeso.dev>"
|
||||
extended-description-file = "docs/misc/README.deb.txt"
|
||||
|
||||
[[bin]]
|
||||
@@ -31,50 +37,75 @@ name = "termscp"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
argh = "0.1.7"
|
||||
bitflags = "1.3.2"
|
||||
bytesize = "1.1.0"
|
||||
chrono = "0.4.19"
|
||||
content_inspector = "0.2.4"
|
||||
dirs = "4.0.0"
|
||||
edit = "0.1.3"
|
||||
hostname = "0.3.1"
|
||||
keyring = { version = "1.1.2", optional = true }
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.14"
|
||||
magic-crypt = "3.1.9"
|
||||
notify-rust = { version = "4.5.6", default-features = false, features = [ "d" ] }
|
||||
open = "2.0.3"
|
||||
rand = "0.8.5"
|
||||
regex = "1.5.5"
|
||||
argh = "^0.1"
|
||||
bitflags = "^2.1"
|
||||
bytesize = "^1.1"
|
||||
chrono = "^0.4"
|
||||
content_inspector = "^0.2"
|
||||
dirs = "^5.0"
|
||||
edit = "^0.1"
|
||||
filetime = "^0.2"
|
||||
hostname = "^0.3"
|
||||
keyring = { version = "^2.0", optional = true }
|
||||
lazy-regex = "^2.5"
|
||||
lazy_static = "^1.4"
|
||||
log = "^0.4"
|
||||
magic-crypt = "^3.1"
|
||||
notify = "=4.0.17"
|
||||
notify-rust = { version = "^4.5", default-features = false, features = ["d"] }
|
||||
open = "^5.0"
|
||||
rand = "^0.8.5"
|
||||
remotefs = "^0.2.0"
|
||||
remotefs-aws-s3 = "^0.2.0"
|
||||
remotefs-ftp = { version = "^0.1.0", features = [ "secure" ] }
|
||||
remotefs-ssh = "^0.1.0"
|
||||
rpassword = "5.0.1"
|
||||
self_update = { version = "0.28.0", features = [ "archive-tar", "archive-zip", "compression-flate2", "compression-zip-deflate" ] }
|
||||
serde = { version = "^1.0.0", features = [ "derive" ] }
|
||||
simplelog = "0.11.1"
|
||||
tempfile = "3.2.0"
|
||||
thiserror = "^1.0.0"
|
||||
toml = "0.5.8"
|
||||
tui-realm-stdlib = "1.1.6"
|
||||
tuirealm = "1.5.0"
|
||||
unicode-width = "0.1.8"
|
||||
whoami = "1.2.1"
|
||||
wildmatch = "2.1.0"
|
||||
remotefs-aws-s3 = { version = "^0.2.1", default-features = false, features = [
|
||||
"find",
|
||||
"rustls",
|
||||
] }
|
||||
rpassword = "^7.0"
|
||||
self_update = { version = "^0.37", default-features = false, features = [
|
||||
"rustls",
|
||||
"archive-tar",
|
||||
"archive-zip",
|
||||
"compression-flate2",
|
||||
"compression-zip-deflate",
|
||||
] }
|
||||
serde = { version = "^1", features = ["derive"] }
|
||||
simplelog = "^0.12"
|
||||
ssh2-config = "^0.2"
|
||||
tempfile = "^3.4"
|
||||
thiserror = "^1"
|
||||
toml = "^0.7"
|
||||
tui-realm-stdlib = "^1.2"
|
||||
tuirealm = "^1.8.0"
|
||||
unicode-width = "^0.1"
|
||||
version-compare = "^0.1"
|
||||
whoami = "^1.4"
|
||||
wildmatch = "^2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.1.0"
|
||||
serial_test = "^0.5.1"
|
||||
pretty_assertions = "^1.3"
|
||||
serial_test = "^2.0"
|
||||
|
||||
[build-dependencies]
|
||||
cfg_aliases = "0.1"
|
||||
|
||||
[features]
|
||||
default = [ "with-keyring" ]
|
||||
github-actions = [ ]
|
||||
with-keyring = [ "keyring" ]
|
||||
default = ["smb", "with-keyring"]
|
||||
github-actions = []
|
||||
with-keyring = ["keyring"]
|
||||
smb = ["remotefs-smb"]
|
||||
|
||||
[target."cfg(not(target_os = \"macos\"))".dependencies]
|
||||
remotefs-smb = { version = "^0.2", optional = true }
|
||||
|
||||
[target."cfg(target_family = \"windows\")"]
|
||||
[target."cfg(target_family = \"windows\")".dependencies]
|
||||
remotefs-ftp = { version = "^0.1.2", features = ["native-tls"] }
|
||||
remotefs-ssh = "^0.2"
|
||||
|
||||
[target."cfg(target_family = \"unix\")"]
|
||||
[target."cfg(target_family = \"unix\")".dependencies]
|
||||
remotefs-ftp = { version = "^0.1.2", features = ["vendored", "native-tls"] }
|
||||
remotefs-ssh = { version = "^0.2", features = ["ssh2-vendored"] }
|
||||
users = "0.11.0"
|
||||
|
||||
[profile.dev]
|
||||
|
||||
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Christian Visintin
|
||||
Copyright (c) 2020-2022 Christian Visintin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
59
README.md
@@ -6,18 +6,18 @@
|
||||
|
||||
<p align="center">~ A feature rich terminal file transfer ~</p>
|
||||
<p align="center">
|
||||
<a href="https://veeso.github.io/termscp/" target="_blank">Website</a>
|
||||
<a href="https://termscp.veeso.dev" target="_blank">Website</a>
|
||||
·
|
||||
<a href="https://veeso.github.io/termscp/#get-started" target="_blank">Installation</a>
|
||||
<a href="https://termscp.veeso.dev/#get-started" target="_blank">Installation</a>
|
||||
·
|
||||
<a href="https://veeso.github.io/termscp/#user-manual" target="_blank">User manual</a>
|
||||
<a href="https://termscp.veeso.dev/#user-manual" target="_blank">User manual</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/veeso/termscp"
|
||||
><img
|
||||
height="20"
|
||||
src="/assets/images/flags/us.png"
|
||||
src="/assets/images/flags/gb.png"
|
||||
alt="English"
|
||||
/></a>
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">Developed by <a href="https://veeso.github.io/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Current version: 0.8.1 (22/03/2022)</p>
|
||||
<p align="center">Developed by <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Current version: 0.12.1 (06/07/2023)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
@@ -119,7 +119,7 @@
|
||||
|
||||
## About termscp 🖥
|
||||
|
||||
Termscp is a feature rich terminal file transfer and explorer, with support for SCP/SFTP/FTP/S3. So basically is a terminal utility with an TUI to connect to a remote server to retrieve and upload files and to interact with the local file system. It is **Linux**, **MacOS**, **FreeBSD** and **Windows** compatible.
|
||||
Termscp is a feature rich terminal file transfer and explorer, with support for SCP/SFTP/FTP/S3. So basically is a terminal utility with an TUI to connect to a remote server to retrieve and upload files and to interact with the local file system. It is **Linux**, **MacOS**, **FreeBSD**, **NetBSD** and **Windows** compatible.
|
||||
|
||||

|
||||
|
||||
@@ -132,12 +132,13 @@ Termscp is a feature rich terminal file transfer and explorer, with support for
|
||||
- **SCP**
|
||||
- **FTP** and **FTPS**
|
||||
- **S3**
|
||||
- **SMB**
|
||||
- 🖥 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 and edit files with your favourite applications
|
||||
- 💁 SFTP/SCP authentication with SSH keys and username/password
|
||||
- 🐧 Compatible with Windows, Linux, FreeBSD and MacOS
|
||||
- 🐧 Compatible with Windows, Linux, FreeBSD, NetBSD and MacOS
|
||||
- 🎨 Make it yours!
|
||||
- Themes
|
||||
- Custom file explorer format
|
||||
@@ -145,6 +146,7 @@ Termscp is a feature rich terminal file transfer and explorer, with support for
|
||||
- Customizable file sorting
|
||||
- and many other parameters...
|
||||
- 📫 Get notified via Desktop Notifications when a large file has been transferred
|
||||
- 🔭 Keep file changes synchronized with the remote host
|
||||
- 🔐 Save your password in your operating system key vault
|
||||
- 🦀 Rust-powered
|
||||
- 👀 Developed keeping an eye on performance
|
||||
@@ -160,29 +162,37 @@ If you want to contribute to this project, don't forget to check out our [contri
|
||||
If you are a Linux, a FreeBSD or a MacOS user this simple shell script will install termscp on your system with a single command:
|
||||
|
||||
```sh
|
||||
curl --proto '=https' --tlsv1.2 -sSLf "https://git.io/JBhDb" | sh
|
||||
curl -sSLf http://get-termscp.veeso.dev | sh
|
||||
```
|
||||
|
||||
> ❗ MacOs installation requires [Homebrew](https://brew.sh/), otherwise the Rust compiler will be installed
|
||||
|
||||
while if you're a Windows user, you can install termscp with [Chocolatey](https://chocolatey.org/):
|
||||
|
||||
```sh
|
||||
```ps
|
||||
choco install termscp
|
||||
```
|
||||
|
||||
For more information or other platforms, please visit [veeso.github.io](https://veeso.github.io/termscp/#get-started) to view all installation methods.
|
||||
NetBSD users can install termscp from the official repositories.
|
||||
|
||||
```sh
|
||||
pkgin install termscp
|
||||
```
|
||||
|
||||
For more information or other platforms, please visit [termscp.veeso.dev](https://termscp.veeso.dev/#get-started) to view all installation methods.
|
||||
|
||||
⚠️ If you're looking on how to update termscp just run termscp from CLI with: `(sudo) termscp --update` ⚠️
|
||||
|
||||
### Requirements ❗
|
||||
|
||||
- **Linux** users:
|
||||
- libssh
|
||||
- libdbus-1
|
||||
- pkg-config
|
||||
- **FreeBSD** users:
|
||||
- libssh
|
||||
- libsmbclient
|
||||
- **FreeBSD** or, **NetBSD** users:
|
||||
- dbus
|
||||
- pkgconf
|
||||
- libsmbclient
|
||||
|
||||
### Optional Requirements ✔️
|
||||
|
||||
@@ -210,33 +220,19 @@ You can make a donation with one of these platforms:
|
||||
|
||||
[](https://ko-fi.com/veeso)
|
||||
[](https://www.paypal.me/chrisintin)
|
||||
[](https://btc.com/bc1qvlmykjn7htz0vuprmjrlkwtv9m9pan6kylsr8w)
|
||||
|
||||
---
|
||||
|
||||
## User manual 📚
|
||||
|
||||
The user manual can be found on the [termscp's website](https://veeso.github.io/termscp/#user-manual) or on [Github](docs/man.md).
|
||||
The user manual can be found on the [termscp's website](https://termscp.veeso.dev/#user-manual) or on [Github](docs/man.md).
|
||||
|
||||
---
|
||||
|
||||
## Upcoming Features 🧪
|
||||
|
||||
For **2022** there will be two major updates during the year.
|
||||
|
||||
Planned for *Summer update 2022*:
|
||||
|
||||
- **File system watcher 🔭**: The feature consists in the possibility to track some files in order to automatically sync them with remote host. For the implementation [notify](https://github.com/notify-rs/notify) will be used.
|
||||
|
||||
Planned for *future updates ⏲️*:
|
||||
|
||||
- **Translations 🌐**: The feature consists in the possibility for the user to install the language pack for the language he prefers in order to replace the default English interface. The following language will be provided along to English:
|
||||
- 🇨🇳 Chinese
|
||||
- 🇫🇷 French
|
||||
- 🇩🇪 German
|
||||
- 🇮🇹 Italian
|
||||
- 🇳🇱 Dutch
|
||||
- 🇪🇸 Spanish
|
||||
- **Configuration profile for bookmarks 📚**: Basically this feature adds the possibility to have a specific setup for a certain host, instead of having only one global configuration. (Maybe will be postponed to spring 2022).
|
||||
For **2023** there will be two major updates during the year.
|
||||
|
||||
Along to new features, termscp developments is now focused on UX and performance improvements, so if you have any suggestion, feel free to open an issue.
|
||||
|
||||
@@ -268,6 +264,7 @@ termscp is powered by these awesome projects:
|
||||
- [edit](https://github.com/milkey-mouse/edit)
|
||||
- [keyring-rs](https://github.com/hwchen/keyring-rs)
|
||||
- [open-rs](https://github.com/Byron/open-rs)
|
||||
- [pavao](https://github.com/veeso/pavao)
|
||||
- [remotefs](https://github.com/veeso/remotefs-rs)
|
||||
- [rpassword](https://github.com/conradkleinespel/rpassword)
|
||||
- [self_update](https://github.com/jaemk/self_update)
|
||||
|
||||
BIN
assets/images/flags/gb.png
Normal file
|
After Width: | Height: | Size: 1002 B |
15
build.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use cfg_aliases::cfg_aliases;
|
||||
|
||||
fn main() {
|
||||
// Setup cfg aliases
|
||||
cfg_aliases! {
|
||||
// Platforms
|
||||
macos: { target_os = "macos" },
|
||||
linux: { target_os = "linux" },
|
||||
unix: { target_family = "unix" },
|
||||
windows: { target_family = "windows" },
|
||||
// exclusive features
|
||||
smb: { all(feature = "smb", not( macos )) },
|
||||
smb_unix: { all(unix, feature = "smb", not(macos)) }
|
||||
}
|
||||
}
|
||||
26
dist/build/aarch64_centos7/Dockerfile
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
FROM centos:centos7 as builder
|
||||
|
||||
WORKDIR /usr/src/
|
||||
# Install dependencies
|
||||
RUN yum -y install \
|
||||
git \
|
||||
gcc \
|
||||
pkgconfig \
|
||||
gcc \
|
||||
make \
|
||||
dbus-devel \
|
||||
libsmbclient-devel \
|
||||
bash \
|
||||
rpm-build
|
||||
# Install rust
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > /tmp/rust.sh && \
|
||||
chmod +x /tmp/rust.sh && \
|
||||
/tmp/rust.sh -y
|
||||
# Clone repository
|
||||
RUN git clone https://github.com/veeso/termscp.git
|
||||
# Set workdir to termscp
|
||||
WORKDIR /usr/src/termscp/
|
||||
# Install cargo rpm
|
||||
RUN source $HOME/.cargo/env && cargo install cargo-rpm
|
||||
|
||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM debian:jessie
|
||||
FROM debian:buster
|
||||
|
||||
WORKDIR /usr/src/
|
||||
# Install dependencies
|
||||
@@ -6,9 +6,11 @@ RUN apt update && apt install -y \
|
||||
git \
|
||||
gcc \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
libssh2-1-dev \
|
||||
libdbus-1-dev \
|
||||
build-essential \
|
||||
libsmbclient-dev \
|
||||
libsmbclient \
|
||||
bash \
|
||||
curl
|
||||
|
||||
# Install rust
|
||||
@@ -21,9 +23,5 @@ RUN git clone https://github.com/veeso/termscp.git
|
||||
WORKDIR /usr/src/termscp/
|
||||
# Install cargo deb
|
||||
RUN . $HOME/.cargo/env && cargo install cargo-deb
|
||||
# Build for x86_64
|
||||
RUN . $HOME/.cargo/env && cargo build --release
|
||||
# Build pkgs
|
||||
RUN . $HOME/.cargo/env && cargo deb
|
||||
|
||||
CMD ["sh"]
|
||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
|
||||
39
dist/build/docker.sh
vendored
@@ -1,39 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: docker.sh <version>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$1
|
||||
|
||||
set -e # Don't fail
|
||||
|
||||
# Create pkgs directory
|
||||
cd ..
|
||||
PKGS_DIR=$(pwd)/pkgs
|
||||
cd -
|
||||
mkdir -p ${PKGS_DIR}/
|
||||
# Build x86_64_deb
|
||||
cd x86_64_debian9/
|
||||
docker build --build-arg branch=${VERSION} --tag termscp-${VERSION}-x86_64_debian9 .
|
||||
cd -
|
||||
mkdir -p ${PKGS_DIR}/deb/
|
||||
mkdir -p ${PKGS_DIR}/x86_64-unknown-linux-gnu/
|
||||
CONTAINER_NAME=$(docker create termscp-${VERSION}-x86_64_debian9 /bin/bash)
|
||||
docker cp ${CONTAINER_NAME}:/usr/src/termscp/target/debian/termscp_${VERSION}_amd64.deb ${PKGS_DIR}/deb/
|
||||
docker cp ${CONTAINER_NAME}:/usr/src/termscp/target/release/termscp ${PKGS_DIR}/x86_64-unknown-linux-gnu/
|
||||
# Make tar.gz
|
||||
cd ${PKGS_DIR}/x86_64-unknown-linux-gnu/
|
||||
tar cvzf termscp-v${VERSION}-x86_64-unknown-linux-gnu.tar.gz termscp
|
||||
rm termscp
|
||||
cd -
|
||||
# Build x86_64_centos7
|
||||
cd x86_64_centos7/
|
||||
docker build --build-arg branch=${VERSION} --tag termscp-${VERSION}-x86_64_centos7 .
|
||||
cd -
|
||||
mkdir -p ${PKGS_DIR}/rpm/
|
||||
CONTAINER_NAME=$(docker create termscp-${VERSION}-x86_64_centos7 /bin/bash)
|
||||
docker cp ${CONTAINER_NAME}:/usr/src/termscp/target/release/rpmbuild/RPMS/x86_64/termscp-${VERSION}-1.el7.x86_64.rpm ${PKGS_DIR}/rpm/termscp-${VERSION}-1.x86_64.rpm
|
||||
|
||||
exit $?
|
||||
2
dist/build/freebsd.sh
vendored
@@ -39,7 +39,7 @@ echo -e "desc: <<EOD\n\
|
||||
A feature rich terminal UI file transfer and explorer with support for SCP/SFTP/FTP/S3\n\
|
||||
EOD\n\
|
||||
arch: \"amd64\"\n\
|
||||
www: \"https://veeso.github.io/termscp/\"\n\
|
||||
www: \"https://termscp.veeso.dev/termscp/\"\n\
|
||||
maintainer: \"christian.visintin1997@gmail.com\"\n\
|
||||
prefix: \"/usr/local/bin\"\n\
|
||||
deps: {\n\
|
||||
|
||||
62
dist/build/linux-aarch64.sh
vendored
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: $0 <version> [branch] [--no-cache]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$1
|
||||
|
||||
if [ -z "$2" ]; then
|
||||
BRANCH=$VERSION
|
||||
else
|
||||
BRANCH=$2
|
||||
fi
|
||||
|
||||
CACHE=""
|
||||
|
||||
if [ "$3" == "--no-cache" ]; then
|
||||
CACHE="--no-cache"
|
||||
fi
|
||||
|
||||
# names
|
||||
ARM64_DEB_NAME="termscp-arm64_deb"
|
||||
ARM64_RPM_NAME="termscp-arm64_rpm"
|
||||
|
||||
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
|
||||
set -e # Don't fail
|
||||
|
||||
# Create pkgs directory
|
||||
cd ..
|
||||
PKGS_DIR=$(pwd)/pkgs
|
||||
cd -
|
||||
mkdir -p ${PKGS_DIR}/
|
||||
# Build aarch64_deb
|
||||
cd aarch64_debian10/
|
||||
docker buildx build --platform linux/arm64 $CACHE --build-arg branch=${BRANCH} --tag $ARM64_DEB_NAME .
|
||||
cd -
|
||||
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/release/termscp ${PKGS_DIR}/aarch64-unknown-linux-gnu/
|
||||
docker stop "$ARM64_DEB_NAME"
|
||||
# Make tar.gz
|
||||
cd ${PKGS_DIR}/aarch64-unknown-linux-gnu/
|
||||
tar cvzf termscp-v${VERSION}-aarch64-unknown-linux-gnu.tar.gz termscp
|
||||
echo "Sha256 (homebrew aarch64): $(sha256sum termscp-v${VERSION}-aarch64-unknown-linux-gnu.tar.gz)"
|
||||
rm termscp
|
||||
cd -
|
||||
# Build aarch64_centos7
|
||||
cd aarch64_centos7/
|
||||
docker buildx build --platform linux/arm64 $CACHE --build-arg branch=${BRANCH} --tag $ARM64_RPM_NAME .
|
||||
cd -
|
||||
mkdir -p ${PKGS_DIR}/rpm/
|
||||
docker run --name "$ARM64_RPM_NAME" -d "$ARM64_RPM_NAME" || docker start "$ARM64_RPM_NAME"
|
||||
docker exec -it "$ARM64_RPM_NAME" bash -c ". \$HOME/.cargo/env && git fetch origin && git checkout origin/$BRANCH; cargo rpm init; cargo rpm build"
|
||||
docker cp ${ARM64_RPM_NAME}:/usr/src/termscp/target/release/rpmbuild/RPMS/aarch64/termscp-${VERSION}-1.el7.aarch64.rpm ${PKGS_DIR}/rpm/termscp-${VERSION}-1.aarch64.rpm
|
||||
docker stop "$ARM64_RPM_NAME"
|
||||
|
||||
exit $?
|
||||
60
dist/build/linux-x86_64.sh
vendored
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: $0 <version> [branch] [--no-cache]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$1
|
||||
|
||||
if [ -z "$2" ]; then
|
||||
BRANCH=$VERSION
|
||||
else
|
||||
BRANCH=$2
|
||||
fi
|
||||
|
||||
CACHE=""
|
||||
|
||||
if [ "$3" == "--no-cache" ]; then
|
||||
CACHE="--no-cache"
|
||||
fi
|
||||
|
||||
# names
|
||||
X86_64_DEB_NAME="termscp-x86_64_deb"
|
||||
X86_64_RPM_NAME="termscp-x86_64_rpm"
|
||||
|
||||
set -e # Don't fail
|
||||
|
||||
# Create pkgs directory
|
||||
cd ..
|
||||
PKGS_DIR=$(pwd)/pkgs
|
||||
cd -
|
||||
mkdir -p ${PKGS_DIR}/
|
||||
# Build x86_64_deb
|
||||
cd x86_64_debian10/
|
||||
docker build $CACHE --build-arg branch=${BRANCH} --tag "$X86_64_DEB_NAME" .
|
||||
cd -
|
||||
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/release/termscp ${PKGS_DIR}/x86_64-unknown-linux-gnu/
|
||||
docker stop "$X86_64_DEB_NAME"
|
||||
# Make tar.gz
|
||||
cd ${PKGS_DIR}/x86_64-unknown-linux-gnu/
|
||||
tar cvzf termscp-v${VERSION}-x86_64-unknown-linux-gnu.tar.gz termscp
|
||||
echo "Sha256 x86_64 (homebrew): $(sha256sum termscp-v${VERSION}-x86_64-unknown-linux-gnu.tar.gz)"
|
||||
rm termscp
|
||||
cd -
|
||||
# Build x86_64_centos7
|
||||
cd x86_64_centos7/
|
||||
docker build $CACHE --build-arg branch=${BRANCH} --tag "$X86_64_RPM_NAME" .
|
||||
cd -
|
||||
mkdir -p ${PKGS_DIR}/rpm/
|
||||
docker run --name "$X86_64_RPM_NAME" -d "$X86_64_RPM_NAME" || docker start "$X86_64_RPM_NAME"
|
||||
docker exec -it "$X86_64_RPM_NAME" bash -c ". \$HOME/.cargo/env && git fetch origin && git checkout origin/$BRANCH; cargo rpm init; cargo rpm build"
|
||||
docker cp ${X86_64_RPM_NAME}:/usr/src/termscp/target/release/rpmbuild/RPMS/x86_64/termscp-${VERSION}-1.el7.x86_64.rpm ${PKGS_DIR}/rpm/termscp-${VERSION}-1.x86_64.rpm
|
||||
docker stop "$X86_64_RPM_NAME"
|
||||
|
||||
exit $?
|
||||
115
dist/build/macos.sh
vendored
@@ -8,49 +8,47 @@ make_pkg() {
|
||||
TARGET_DIR=target/release/
|
||||
fi
|
||||
ROOT_DIR=$(pwd)
|
||||
cd $TARGET_DIR
|
||||
cd "$TARGET_DIR"
|
||||
PKG="termscp-v${VERSION}-${ARCH}-apple-darwin.tar.gz"
|
||||
tar czf $PKG termscp
|
||||
HASH=$(sha256sum $PKG)
|
||||
mkdir -p ${ROOT_DIR}/dist/pkgs/macos/
|
||||
mv $PKG ${ROOT_DIR}/dist/pkgs/macos/$PKG
|
||||
tar czf "$PKG" termscp
|
||||
HASH=$(sha256sum "$PKG")
|
||||
mkdir -p "${ROOT_DIR}/dist/pkgs/macos/"
|
||||
mv "$PKG" "${ROOT_DIR}/dist/pkgs/macos/$PKG"
|
||||
cd -
|
||||
echo "$HASH"
|
||||
}
|
||||
|
||||
build_openssl_arm64() {
|
||||
# setup dirs
|
||||
BUILD_DIR=$(pwd)
|
||||
OPENSSL_BUILD_DIR=/tmp/openssl-build/
|
||||
# setup openssl dir
|
||||
mkdir -p $OPENSSL_DIR
|
||||
cd $OPENSSL_DIR
|
||||
# check if openssl has already been compiled
|
||||
if [ -e ./include/ ]; then
|
||||
return 0
|
||||
detect_platform() {
|
||||
local platform
|
||||
platform="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
|
||||
case "${platform}" in
|
||||
linux) platform="linux" ;;
|
||||
darwin) platform="macos" ;;
|
||||
freebsd) platform="freebsd" ;;
|
||||
esac
|
||||
|
||||
printf '%s' "${platform}"
|
||||
}
|
||||
|
||||
detect_arch() {
|
||||
local arch
|
||||
arch="$(uname -m | tr '[:upper:]' '[:lower:]')"
|
||||
|
||||
case "${arch}" in
|
||||
amd64) arch="x86_64" ;;
|
||||
armv*) arch="arm" ;;
|
||||
arm64) arch="aarch64" ;;
|
||||
esac
|
||||
|
||||
# `uname -m` in some cases mis-reports 32-bit OS as 64-bit, so double check
|
||||
if [ "${arch}" = "x86_64" ] && [ "$(getconf LONG_BIT)" -eq 32 ]; then
|
||||
arch="i686"
|
||||
elif [ "${arch}" = "aarch64" ] && [ "$(getconf LONG_BIT)" -eq 32 ]; then
|
||||
arch="arm"
|
||||
fi
|
||||
# download package
|
||||
TEMP_TGZ=/tmp/openssl.tar.gz
|
||||
wget https://www.openssl.org/source/openssl-1.1.1m.tar.gz -O $TEMP_TGZ
|
||||
# setup build dir
|
||||
mkdir -p $OPENSSL_BUILD_DIR
|
||||
cd $OPENSSL_BUILD_DIR
|
||||
# extract sources
|
||||
tar xzvf $TEMP_TGZ
|
||||
rm $TEMP_TGZ
|
||||
# build
|
||||
cd openssl-1.1.1m/
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.15
|
||||
./Configure enable-rc5 zlib darwin64-arm64-cc no-asm
|
||||
make
|
||||
make install DESTDIR=$(pwd)/out/
|
||||
# copy compiled assets to openssl dir
|
||||
cp -r out/usr/local/* $OPENSSL_DIR/
|
||||
# go back to build dir
|
||||
cd $BUILD_DIR
|
||||
# delete temp dir
|
||||
rm -rf $OPENSSL_BUILD_DIR
|
||||
return 0
|
||||
|
||||
printf '%s' "${arch}"
|
||||
}
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
@@ -58,8 +56,17 @@ if [ -z "$1" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PLATFORM="$(detect_platform)"
|
||||
ARCH="$(detect_arch)"
|
||||
|
||||
if [ "$PLATFORM" != "macos" ]; then
|
||||
echo "macos build is only available on MacOs systems"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$1
|
||||
export BUILD_ROOT=$(pwd)/../../
|
||||
export BUILD_ROOT
|
||||
BUILD_ROOT="$(pwd)/../../"
|
||||
|
||||
set -e # Don't fail
|
||||
|
||||
@@ -72,24 +79,32 @@ if [ ! -f Cargo.toml ]; then
|
||||
fi
|
||||
|
||||
# Build release (x86_64)
|
||||
cargo build --release
|
||||
X86_TARGET=""
|
||||
X86_TARGET_DIR=""
|
||||
if [ "$ARCH" = "aarch64" ]; then
|
||||
X86_TARGET="--target x86_64-apple-darwin"
|
||||
X86_TARGET_DIR="target/x86_64-apple-darwin/release/"
|
||||
fi
|
||||
cargo build --release $X86_TARGET
|
||||
# Make pkg
|
||||
X86_64_HASH=$(make_pkg "x86_64" $VERSION)
|
||||
X86_64_HASH=$(make_pkg "x86_64" "$VERSION" $X86_TARGET_DIR)
|
||||
RET_X86_64=$?
|
||||
|
||||
# set openssl dir
|
||||
export OPENSSL_DIR=$BUILD_ROOT/dist/build/macos/openssl/
|
||||
export OPENSSL_STATIC=1
|
||||
export OPENSSL_LIB_DIR=${OPENSSL_DIR}/lib/
|
||||
export OPENSSL_INCLUDE_DIR=${OPENSSL_DIR}/include/
|
||||
# build openssl
|
||||
build_openssl_arm64
|
||||
cd $BUILD_ROOT
|
||||
ARM64_TARGET=""
|
||||
ARM64_TARGET_DIR=""
|
||||
if [ "$ARCH" = "aarch64" ]; then
|
||||
ARM64_TARGET="--target aarch64-apple-darwin"
|
||||
ARM64_TARGET_DIR="target/aarch64-apple-darwin/release/"
|
||||
fi
|
||||
cd "$BUILD_ROOT"
|
||||
# Build ARM64 pkg
|
||||
cargo build --release --target aarch64-apple-darwin
|
||||
cargo build --release $ARM64_TARGET
|
||||
# Make pkg
|
||||
ARM64_HASH=$(make_pkg "arm64" $VERSION "target/aarch64-apple-darwin/release/")
|
||||
ARM64_HASH=$(make_pkg "arm64" "$VERSION" $ARM64_TARGET_DIR)
|
||||
RET_ARM64=$?
|
||||
|
||||
echo "x86_64 hash: $X86_64_HASH"
|
||||
echo "arm64 hash: $ARM64_HASH"
|
||||
|
||||
[ "$RET_ARM64" -eq 0 ] && [ "$RET_X86_64" -eq 0 ]
|
||||
exit $?
|
||||
|
||||
4
dist/build/windows.ps1
vendored
@@ -14,7 +14,7 @@ cargo build --release
|
||||
# Make zip
|
||||
$zipName = "termscp-v$version-x86_64-pc-windows-msvc.zip"
|
||||
Set-Location .\target\release\
|
||||
Compress-Archive termscp.exe $zipName
|
||||
Compress-Archive -Force termscp.exe $zipName
|
||||
# Get checksum
|
||||
checksum.exe -t sha256 $zipName
|
||||
Get-FileHash $zipName
|
||||
Move-Item $zipName .\..\..\dist\pkgs\windows\$zipName
|
||||
|
||||
24
dist/build/x86_64/Dockerfile
vendored
@@ -1,24 +0,0 @@
|
||||
FROM rust:1.55.0 AS builder
|
||||
|
||||
WORKDIR /usr/src/
|
||||
# Add toolchains
|
||||
RUN rustup target add x86_64-unknown-linux-gnu
|
||||
# Install dependencies
|
||||
RUN apt update && apt install -y \
|
||||
git \
|
||||
gcc \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
libssh2-1-dev \
|
||||
libdbus-1-dev \
|
||||
curl
|
||||
# Clone repository
|
||||
RUN git clone https://github.com/veeso/termscp.git
|
||||
# Set workdir to termscp
|
||||
WORKDIR /usr/src/termscp/
|
||||
# Install cargo RPM/Deb
|
||||
RUN cargo install cargo-strip
|
||||
# Build for x86_64
|
||||
RUN cargo build --release --target x86_64-unknown-linux-gnu && cargo strip
|
||||
|
||||
CMD ["sh"]
|
||||
21
dist/build/x86_64_centos7/Dockerfile
vendored
@@ -1,29 +1,26 @@
|
||||
FROM centos:centos7 as builder
|
||||
|
||||
ARG branch
|
||||
ENV branch=$branch
|
||||
WORKDIR /usr/src/
|
||||
# Install dependencies
|
||||
RUN yum -y install \
|
||||
git \
|
||||
gcc \
|
||||
openssl \
|
||||
pkgconfig \
|
||||
gcc \
|
||||
make \
|
||||
dbus-devel \
|
||||
openssl-devel \
|
||||
bash
|
||||
libsmbclient-devel \
|
||||
bash \
|
||||
rpm-build
|
||||
# Install rust
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > /tmp/rust.sh && \
|
||||
chmod +x /tmp/rust.sh && \
|
||||
/tmp/rust.sh -y
|
||||
# Clone repository
|
||||
RUN git clone --branch $branch https://github.com/veeso/termscp.git
|
||||
RUN git clone https://github.com/veeso/termscp.git
|
||||
# Set workdir to termscp
|
||||
WORKDIR /usr/src/termscp/
|
||||
# Install cargo arxch
|
||||
# Install cargo rpm
|
||||
RUN source $HOME/.cargo/env && cargo install cargo-rpm
|
||||
# Build for x86_64
|
||||
RUN source $HOME/.cargo/env && cargo build --release
|
||||
# Build pkgs
|
||||
RUN source $HOME/.cargo/env && yum -y install rpm-build && cargo rpm init && cargo rpm build
|
||||
CMD ["sh"]
|
||||
|
||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
FROM debian:stretch
|
||||
FROM debian:buster
|
||||
|
||||
ARG branch
|
||||
ENV branch=$branch
|
||||
WORKDIR /usr/src/
|
||||
# Install dependencies
|
||||
RUN apt update && apt install -y \
|
||||
git \
|
||||
gcc \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
libssh2-1-dev \
|
||||
libdbus-1-dev \
|
||||
build-essential \
|
||||
libsmbclient-dev \
|
||||
libsmbclient \
|
||||
bash \
|
||||
curl
|
||||
|
||||
@@ -19,14 +18,10 @@ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > /tmp/rust.sh &&
|
||||
chmod +x /tmp/rust.sh && \
|
||||
/tmp/rust.sh -y
|
||||
# Clone repository
|
||||
RUN git clone --branch $branch https://github.com/veeso/termscp.git
|
||||
RUN git clone https://github.com/veeso/termscp.git
|
||||
# Set workdir to termscp
|
||||
WORKDIR /usr/src/termscp/
|
||||
# Install cargo deb
|
||||
RUN . $HOME/.cargo/env && cargo install cargo-deb
|
||||
# Build for x86_64
|
||||
RUN . $HOME/.cargo/env && cargo build --release
|
||||
# Build pkgs
|
||||
RUN . $HOME/.cargo/env && cargo deb
|
||||
|
||||
CMD ["bash"]
|
||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
|
||||
@@ -6,18 +6,18 @@
|
||||
|
||||
<p align="center">~ Eine funktionsreiche Terminal-Dateiübertragung ~</p>
|
||||
<p align="center">
|
||||
<a href="https://veeso.github.io/termscp/" target="_blank">Webseite</a>
|
||||
<a href="https://termscp.veeso.dev" target="_blank">Webseite</a>
|
||||
·
|
||||
<a href="https://veeso.github.io/termscp/#get-started" target="_blank">Installation</a>
|
||||
<a href="https://termscp.veeso.dev/#get-started" target="_blank">Installation</a>
|
||||
·
|
||||
<a href="https://veeso.github.io/termscp/#user-manual" target="_blank">Benutzerhandbuch</a>
|
||||
<a href="https://termscp.veeso.dev/#user-manual" target="_blank">Benutzerhandbuch</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/veeso/termscp"
|
||||
><img
|
||||
height="20"
|
||||
src="/assets/images/flags/us.png"
|
||||
src="/assets/images/flags/gb.png"
|
||||
alt="English"
|
||||
/></a>
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">Entwickelt von <a href="https://veeso.github.io/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Aktuelle Version: 0.8.1 (22/03/2022)</p>
|
||||
<p align="center">Entwickelt von <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Aktuelle Version: 0.12.1 (06/07/2023)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
@@ -137,6 +137,7 @@ Termscp ist ein funktionsreicher Terminal-Dateitransfer und Explorer mit Unterst
|
||||
- **SCP**
|
||||
- **FTP** und **FTPS**
|
||||
- **S3**
|
||||
- **SMB**
|
||||
- 🖥 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
|
||||
@@ -150,6 +151,7 @@ Termscp ist ein funktionsreicher Terminal-Dateitransfer und Explorer mit Unterst
|
||||
- Anpassbare Dateisortierung
|
||||
- und viele andere Parameter...
|
||||
- 📫 Lassen Sie sich benachrichtigen, wenn eine große Datei übertragen wurde
|
||||
- 🔭 Halten Sie Dateiänderungen mit dem Remote-Host synchron
|
||||
- 🔐 Speichern Sie Ihr Passwort in Ihrem Betriebssystem-Schlüsseltresor
|
||||
- 🦀 Rust-powered
|
||||
- 👀 Entwickelt, um die Leistung im Auge zu behalten
|
||||
@@ -165,7 +167,7 @@ Wenn Sie zu diesem Projekt beitragen möchten, vergessen Sie nicht, unseren [Bei
|
||||
Wenn Sie ein Linux-, FreeBSD- oder MacOS-Benutzer sind, installiert dieses einfache Shell-Skript termscp mit einem einzigen Befehl auf Ihrem System:
|
||||
|
||||
```sh
|
||||
curl --proto '=https' --tlsv1.2 -sSLf "https://git.io/JBhDb" | sh
|
||||
curl -sSLf http://get-termscp.veeso.dev | sh
|
||||
```
|
||||
|
||||
Wenn Sie ein Windows-Benutzer sind, können Sie termscp mit [Chocolatey](https://chocolatey.org/) installieren:
|
||||
@@ -174,7 +176,7 @@ Wenn Sie ein Windows-Benutzer sind, können Sie termscp mit [Chocolatey](https:/
|
||||
choco install termscp
|
||||
```
|
||||
|
||||
Für weitere Informationen oder andere Plattformen besuchen Sie bitte [veeso.github.io](https://veeso.github.io/termscp/#get-started), um alle Installationsmethoden anzuzeigen.
|
||||
Für weitere Informationen oder andere Plattformen besuchen Sie bitte [termscp.veeso.dev](https://termscp.veeso.dev/termscp/#get-started), um alle Installationsmethoden anzuzeigen.
|
||||
|
||||
⚠️ Wenn Sie wissen möchten, wie Sie termscp aktualisieren können, führen Sie einfach termscp über die CLI aus mit: `(sudo) termscp --update` ⚠️
|
||||
|
||||
@@ -184,10 +186,12 @@ Für weitere Informationen oder andere Plattformen besuchen Sie bitte [veeso.git
|
||||
- libssh
|
||||
- libdbus-1
|
||||
- pkg-config
|
||||
- libsmbclient
|
||||
- **FreeBSD** Benutzer:
|
||||
- libssh
|
||||
- dbus
|
||||
- pkgconf
|
||||
- libsmbclient
|
||||
|
||||
### Optionale Softwareanforderungen ✔️
|
||||
|
||||
@@ -220,7 +224,7 @@ Sie können mit einer dieser Plattformen spenden:
|
||||
|
||||
## User manual 📚
|
||||
|
||||
Das Benutzerhandbuch finden Sie auf der [termscp-Website](https://veeso.github.io/termscp/#user-manual) oder auf [Github](man.md).
|
||||
Das Benutzerhandbuch finden Sie auf der [termscp-Website](https://termscp.veeso.dev/termscp/#user-manual) oder auf [Github](man.md).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
- [Usage ❓](#usage-)
|
||||
- [Address argument 🌎](#address-argument-)
|
||||
- [AWS S3 address argument](#aws-s3-address-argument)
|
||||
- [SMB address argument](#smb-address-argument)
|
||||
- [How Password can be provided 🔐](#how-password-can-be-provided-)
|
||||
- [S3 connection parameters](#s3-connection-parameters)
|
||||
- [S3 credentials 🦊](#s3-credentials-)
|
||||
@@ -28,6 +29,7 @@
|
||||
- [Text Editor ✏](#text-editor-)
|
||||
- [Logging 🩺](#logging-)
|
||||
- [Notifications 📫](#notifications-)
|
||||
- [File watcher 🔭](#file-watcher-)
|
||||
|
||||
> ❗ I need a help to translate this manual into German. If you want to contribute to the translations, please open a PR 🙏
|
||||
|
||||
@@ -37,7 +39,12 @@ termscp can be started with the following options:
|
||||
|
||||
`termscp [options]... [protocol://user@address:port:wrkdir] [local-wrkdir]`
|
||||
|
||||
OR
|
||||
|
||||
`termscp [options]... -b [bookmark-name] [local-wrkdir]`
|
||||
|
||||
- `-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
|
||||
@@ -45,11 +52,11 @@ termscp can be started with the following options:
|
||||
- `-v, --version` Print version info
|
||||
- `-h, --help` Print help page
|
||||
|
||||
termscp can be started in two different mode, if no extra arguments is provided, termscp will show the authentication form, where the user will be able to provide the parameters required to connect to the remote peer.
|
||||
termscp can be started in three different modes, if no extra arguments is provided, termscp will show the authentication form, where the user will be able to provide the parameters required to connect to the remote peer.
|
||||
|
||||
Alternatively, the user can provide an address as argument to skip the authentication form and starting directly the connection to the remote server.
|
||||
|
||||
If address argument is provided you can also provide the start working directory for local host
|
||||
If address argument or bookmark name is provided you can also provide the start working directory for local host
|
||||
|
||||
### Address argument 🌎
|
||||
|
||||
@@ -99,6 +106,22 @@ e.g.
|
||||
s3://buckethead@eu-central-1:default:/assets
|
||||
```
|
||||
|
||||
#### SMB address argument
|
||||
|
||||
SMB has a different syntax for CLI address argument, which is different whether you're on Windows or other systems:
|
||||
|
||||
**Windows** syntax:
|
||||
|
||||
```txt
|
||||
\\[username@]<server-name>\<share>[\path\...]
|
||||
```
|
||||
|
||||
**Other systems** syntax:
|
||||
|
||||
```txt
|
||||
smb://[username@]<server-name>[:port]/<share>[/path/.../]
|
||||
```
|
||||
|
||||
#### How Password can be provided 🔐
|
||||
|
||||
You have probably noticed, that, when providing the address as argument, there's no way to provide the password.
|
||||
@@ -198,16 +221,20 @@ In order to change panel you need to type `<LEFT>` to move the remote explorer p
|
||||
| `<M>` | Select a file | Mark |
|
||||
| `<N>` | Create new file with provided name | New |
|
||||
| `<O|F4>` | Edit file; see Text editor | Open |
|
||||
| `<P>` | Open log panel | Panel |
|
||||
| `<Q|F10>` | Quit termscp | Quit |
|
||||
| `<R|F6>` | Rename file | Rename |
|
||||
| `<S|F2>` | Save file as... | Save |
|
||||
| `<T>` | Synchronize changes to selected path to remote | Track |
|
||||
| `<U>` | Go to parent directory | Upper |
|
||||
| `<V|F3>` | Open file with default program for filetype | View |
|
||||
| `<W>` | Open file with provided program | With |
|
||||
| `<X>` | Execute a command | eXecute |
|
||||
| `<Y>` | Toggle synchronized browsing | sYnc |
|
||||
| `<Z>` | Change file mode | |
|
||||
| `<CTRL+A>` | Select all files | |
|
||||
| `<CTRL+C>` | Abort file transfer process | |
|
||||
| `<CTRL+T>` | Show all synchronized paths | Track |
|
||||
|
||||
### Work on multiple files 🥷
|
||||
|
||||
@@ -331,7 +358,7 @@ These parameters can be changed:
|
||||
- **Local File formatter syntax**: syntax to display file info for each file in the local explorer. See [File explorer format](#file-explorer-format)
|
||||
- **Enable notifications?**: If set to `Yes`, notifications will be displayed.
|
||||
- **Notifications: minimum transfer size**: if transfer size is greater or equal than the specified value, notifications for transfer will be displayed. The accepted values are in format `{UNSIGNED} B/KB/MB/GB/TB/PB`
|
||||
- **SSH configuration path**: Set SSH configuration file to use when connecting to a SCP/SFTP server. If unset (empty) no file will be used. You can specify a path starting with `~` to indicate the home path (e.g. `~/.ssh/config`)
|
||||
- **SSH configuration path**: Set SSH configuration file to use when connecting to a SCP/SFTP server. If unset (empty) no file will be used. You can specify a path starting with `~` to indicate the home path (e.g. `~/.ssh/config`). The parameters supported by termscp are specified [HERE](https://github.com/veeso/ssh2-config#exposed-attributes).
|
||||
|
||||
### SSH Key Storage 🔐
|
||||
|
||||
@@ -473,9 +500,9 @@ Just a reminder: **you can edit only textual file**; binary files are not suppor
|
||||
|
||||
termscp writes a log file for each session, which is written at
|
||||
|
||||
- `$HOME/.config/termscp/termscp.log` on Linux/BSD
|
||||
- `$HOME/Library/Application Support/termscp/termscp.log` on MacOs
|
||||
- `FOLDERID_RoamingAppData\termscp\termscp.log` on Windows
|
||||
- `$HOME/.cache/termscp/termscp.log` on Linux/BSD
|
||||
- `$HOME/Library/Caches/termscp/termscp.log` on MacOs
|
||||
- `FOLDERID_LocalAppData\termscp\termscp.log` on Windows
|
||||
|
||||
the log won't be rotated, but will just be truncated after each launch of termscp, so if you want to report an issue and you want to attach your log file, keep in mind to save the log file in a safe place before using termscp again.
|
||||
The logging by default reports in *INFO* level, so it is not very verbose.
|
||||
@@ -507,3 +534,26 @@ Termscp will send Desktop notifications for these kind of events:
|
||||
|
||||
❗ If you prefer to keep notifications turned off, you can just enter setup and set `Enable notifications?` to `No` 😉.
|
||||
❗ If you want to change the minimum transfer size to display notifications, you can change the value in the configuration with key `Notifications: minimum transfer size` and set it to whatever suits better for you 🙂.
|
||||
|
||||
## File watcher 🔭
|
||||
|
||||
The file watcher allows you to setup a list of paths to synchronize with the remote hosts.
|
||||
This means that whenever a change on the local file system will be detected on the synchronized path, the change will be automatically reported to the configured remote host path, within 5 seconds.
|
||||
|
||||
You can set as many paths to synchronize as you prefer:
|
||||
|
||||
1. Put the cursor on the local explorer on the directory/file you want to keep synchronized
|
||||
2. Go to the directory you want the changes to be reported to on the remote host
|
||||
3. Press `<T>`
|
||||
4. Answer `<YES>` to the radio popup
|
||||
|
||||
To unwatch, just press `<T>` on the local synchronized path (or to any of its subfolders)
|
||||
OR you can just press `<CTRL+T>` and press `<ENTER>` to the synchronized path you want to unwatch.
|
||||
|
||||
These changes will be reported to the remote host:
|
||||
|
||||
- New files, file changes
|
||||
- File moved/renamed
|
||||
- File removed/unlinked
|
||||
|
||||
> ❗ The watcher works only in one direction (local > remote). It is NOT possible to synchronize automatically the changes from remote to local.
|
||||
|
||||
@@ -6,18 +6,18 @@
|
||||
|
||||
<p align="center">~ Una transferencia de archivos de terminal rica en funciones ~</p>
|
||||
<p align="center">
|
||||
<a href="https://veeso.github.io/termscp/" target="_blank">Sitio Web</a>
|
||||
<a href="https://termscp.veeso.dev" target="_blank">Sitio Web</a>
|
||||
·
|
||||
<a href="https://veeso.github.io/termscp/#get-started" target="_blank">Instalación</a>
|
||||
<a href="https://termscp.veeso.dev/#get-started" target="_blank">Instalación</a>
|
||||
·
|
||||
<a href="https://veeso.github.io/termscp/#user-manual" target="_blank">Manual de usuario</a>
|
||||
<a href="https://termscp.veeso.dev/#user-manual" target="_blank">Manual de usuario</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/veeso/termscp"
|
||||
><img
|
||||
height="20"
|
||||
src="/assets/images/flags/us.png"
|
||||
src="/assets/images/flags/gb.png"
|
||||
alt="English"
|
||||
/></a>
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">Desarrollado por <a href="https://veeso.github.io/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Versión actual: 0.8.1 (22/03/2022)</p>
|
||||
<p align="center">Desarrollado por <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Versión actual: 0.12.1 (06/07/2023)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
@@ -137,6 +137,7 @@ Termscp es un explorador y transferencia de archivos de terminal rico en funcion
|
||||
- **SCP**
|
||||
- **FTP** y **FTPS**
|
||||
- **S3**
|
||||
- **SMB**
|
||||
- 🖥 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
|
||||
@@ -150,6 +151,7 @@ Termscp es un explorador y transferencia de archivos de terminal rico en funcion
|
||||
- Clasificación de archivos personalizable
|
||||
- y muchos otros parámetros ...
|
||||
- 📫 Reciba una notificación cuando se haya transferido un archivo grande
|
||||
- 🔭 Mantenga los cambios de archivos sincronizados con el host remoto
|
||||
- 🔐 Guarde su contraseña en el almacén de claves de su sistema operativo
|
||||
- 🦀 Rust-powered
|
||||
- 👀 Desarrollado sin perder de vista el rendimiento
|
||||
@@ -165,7 +167,7 @@ Si desea contribuir a este proyecto, no olvide consultar nuestra [guía de contr
|
||||
Si tu eres un usuario de Linux, FreeBSD o MacOS, este sencillo script de shell instalará termscp en tu sistema con un solo comando:
|
||||
|
||||
```sh
|
||||
curl --proto '=https' --tlsv1.2 -sSLf "https://git.io/JBhDb" | sh
|
||||
curl -sSLf http://get-termscp.veeso.dev | sh
|
||||
```
|
||||
|
||||
mientras que si eres un usuario de Windows, puedes instalar termscp con [Chocolatey](https://chocolatey.org/):
|
||||
@@ -174,20 +176,20 @@ mientras que si eres un usuario de Windows, puedes instalar termscp con [Chocola
|
||||
choco install termscp
|
||||
```
|
||||
|
||||
Para obtener más información u otras plataformas, visite [veeso.github.io](https://veeso.github.io/termscp/#get-started) para ver todos los métodos de instalación.
|
||||
Para obtener más información u otras plataformas, visite [termscp.veeso.dev](https://termscp.veeso.dev/termscp/#get-started) para ver todos los métodos de instalación.
|
||||
|
||||
⚠️ Si estás buscando cómo actualizar termscp, simplemente ejecute termscp desde CLI con:: `(sudo) termscp --update` ⚠️
|
||||
|
||||
### Requisitos ❗
|
||||
|
||||
- Usuarios **Linux**:
|
||||
- libssh
|
||||
- **Linux** users:
|
||||
- libdbus-1
|
||||
- pkg-config
|
||||
- Usuarios **FreeBSD**:
|
||||
- libssh
|
||||
- libsmbclient
|
||||
- **FreeBSD** or, **NetBSD** users:
|
||||
- dbus
|
||||
- pkgconf
|
||||
- libsmbclient
|
||||
|
||||
### Requisitos opcionales ✔️
|
||||
|
||||
@@ -220,7 +222,7 @@ Puedes hacer una donación con una de estas plataformas:
|
||||
|
||||
## Manual de usuario y documentación 📚
|
||||
|
||||
El manual del usuario se puede encontrar en el [sitio web de termscp](https://veeso.github.io/termscp/#user-manual) o en [Github](man.md).
|
||||
El manual del usuario se puede encontrar en el [sitio web de termscp](https://termscp.veeso.dev/termscp/#user-manual) o en [Github](man.md).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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 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)
|
||||
- [Credenciales de S3 🦊](#credenciales-de-s3-)
|
||||
@@ -28,6 +29,7 @@
|
||||
- [Text Editor ✏](#text-editor-)
|
||||
- [Logging 🩺](#logging-)
|
||||
- [Notificaciones 📫](#notificaciones-)
|
||||
- [Observador de archivos 🔭](#observador-de-archivos-)
|
||||
|
||||
> ❗ Este documento ha sido traducido con Google Translator (y luego lo he revisado a grandes rasgos, pero no puedo hablar el idioma muy bien). Si habla l'idioma, abra un [issue](https://github.com/veeso/termscp/issues/new/choose) utilizando la label COPY o abra un PR 🙏
|
||||
|
||||
@@ -37,7 +39,12 @@ termscp se puede iniciar con las siguientes opciones:
|
||||
|
||||
`termscp [options]... [protocol://user@address:port:wrkdir] [local-wrkdir]`
|
||||
|
||||
OR
|
||||
|
||||
`termscp [options]... -b [bookmark-name] [local-wrkdir]`
|
||||
|
||||
- `-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
|
||||
@@ -99,6 +106,22 @@ por ejemplo
|
||||
s3://buckethead@eu-central-1:default:/assets
|
||||
```
|
||||
|
||||
#### 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:
|
||||
|
||||
**Windows** sintaxis:
|
||||
|
||||
```txt
|
||||
\\[username@]<server-name>\<share>[\path\...]
|
||||
```
|
||||
|
||||
**Other systems** sintaxis:
|
||||
|
||||
```txt
|
||||
smb://[username@]<server-name>[:port]/<share>[/path/.../]
|
||||
```
|
||||
|
||||
#### Cómo se puede proporcionar la contraseña 🔐
|
||||
|
||||
Probablemente haya notado que, al proporcionar la dirección como argumento, no hay forma de proporcionar la contraseña.
|
||||
@@ -198,16 +221,20 @@ Para cambiar de panel, debe escribir `<LEFT>` para mover el panel del explorador
|
||||
| `<M>` | Seleccione un archivo | Mark |
|
||||
| `<N>` | Crear un nuevo archivo con el nombre proporcionado | New |
|
||||
| `<O|F4>` | Editar archivo | Open |
|
||||
| `<P>` | Open log panel | Panel |
|
||||
| `<Q|F10>` | Salir de termscp | Quit |
|
||||
| `<R|F6>` | Renombrar archivo | Rename |
|
||||
| `<S|F2>` | Guardar archivo como... | Save |
|
||||
| `<T>` | Sincronizar los cambios en la ruta seleccionada con el control remoto | Track |
|
||||
| `<U>` | Ir al directorio principal | Upper |
|
||||
| `<V|F3>` | Abrir archivo con el programa predeterminado | View |
|
||||
| `<W>` | Abrir archivo con el programa proporcionado | With |
|
||||
| `<X>` | Ejecutar un comando | eXecute |
|
||||
| `<Y>` | Alternar navegación sincronizada | sYnc |
|
||||
| `<Z>` | Cambiar ppermisos de archivo | |
|
||||
| `<CTRL+A>` | Seleccionar todos los archivos | |
|
||||
| `<CTRL+C>` | Abortar el proceso de transferencia de archivos | |
|
||||
| `<CTRL+T>` | Mostrar todas las rutas sincronizadas | Track |
|
||||
|
||||
### Trabaja en varios archivos 🥷
|
||||
|
||||
@@ -330,7 +357,7 @@ Estos parámetros se pueden cambiar:
|
||||
- **Local File formatter syntax**: sintaxis para mostrar información de archivo para cada archivo en el explorador local. Consulte [Formato del explorador de archivos](#formato-del-explorador-de-archivos).
|
||||
- **Enable notifications?**: Si se establece en "Sí", se mostrarán las notificaciones.
|
||||
- **Notifications: minimum transfer size**: si el tamaño de la transferencia es mayor o igual que el valor especificado, se mostrarán notificaciones de transferencia. Los valores aceptados están en formato `{UNSIGNED} B/KB/MB/GB/TB/PB`
|
||||
- **SSH configuration path**: Configure el archivo de configuración SSH para usar al conectarse a un servidor SCP / SFTP. Si no se configura (está vacío), no se utilizará ningún archivo. Puede especificar una ruta que comience con `~` para indicar la ruta de inicio (por ejemplo, `~/.ssh/config`)
|
||||
- **SSH configuration path**: Configure el archivo de configuración SSH para usar al conectarse a un servidor SCP / SFTP. Si no se configura (está vacío), no se utilizará ningún archivo. Puede especificar una ruta que comience con `~` para indicar la ruta de inicio (por ejemplo, `~/.ssh/config`). Se especifican los parámetros soportados [AQUI](https://github.com/veeso/ssh2-config#exposed-attributes).
|
||||
|
||||
### SSH Key Storage 🔐
|
||||
|
||||
@@ -472,9 +499,9 @@ En caso de que el archivo esté ubicado en un host remoto, el archivo se descarg
|
||||
|
||||
termscp escribe un archivo de registro para cada sesión, que se escribe en
|
||||
|
||||
- `$HOME/.config/termscp/termscp.log` en Linux/BSD
|
||||
- `$HOME/Library/Application Support/termscp/termscp.log` en MacOs
|
||||
- `FOLDERID_RoamingAppData\termscp\termscp.log` en Windows
|
||||
- `$HOME/.cache/termscp/termscp.log` en Linux/BSD
|
||||
- `$HOME/Library/Caches/termscp/termscp.log` en MacOs
|
||||
- `FOLDERID_LocalAppData\termscp\termscp.log` en Windows
|
||||
|
||||
el registro no se rotará, sino que se truncará después de cada lanzamiento de termscp, por lo que si desea informar un problema y desea adjuntar su archivo de registro, recuerde guardar el archivo de registro en un lugar seguro antes de usar termscp de nuevo.
|
||||
El registro por defecto informa en el nivel *INFO*, por lo que no es muy detallado.
|
||||
@@ -506,3 +533,26 @@ Termscp enviará notificaciones de escritorio para este tipo de eventos:
|
||||
|
||||
❗ Si prefiere mantener las notificaciones desactivadas, puede simplemente ingresar a la configuración y configurar `Enable notifications?` En `No` 😉.
|
||||
❗ Si desea cambiar el tamaño mínimo de transferencia para mostrar notificaciones, puede cambiar el valor en la configuración con la tecla `Notifications: minimum transfer size` y configurarlo como mejor le convenga 🙂.
|
||||
|
||||
## Observador de archivos 🔭
|
||||
|
||||
El observador de archivos le permite configurar una lista de rutas para sincronizar con los hosts remotos.
|
||||
Esto significa que siempre que se detecte un cambio en el sistema de archivos local en la ruta sincronizada, el cambio se informará automáticamente a la ruta del host remoto configurado, dentro de los 5 segundos.
|
||||
|
||||
Puede establecer tantas rutas para sincronizar como prefiera:
|
||||
|
||||
1. Coloque el cursor en el explorador local en el directorio/archivo que desea mantener sincronizado
|
||||
2. Vaya al directorio en el que desea que se informen los cambios en el host remoto
|
||||
3. Presione `<T>`
|
||||
4. Responda `<YES>` a la ventana emergente de radio
|
||||
|
||||
Para dejar de mirar, simplemente presione `<T>` en la ruta sincronizada local (o en cualquiera de sus subcarpetas)
|
||||
O simplemente puede presionar `<CTRL + T>` y presionar `<ENTER>` en la ruta sincronizada que desea dejar de ver.
|
||||
|
||||
Estos cambios se informarán al host remoto:
|
||||
|
||||
- Nuevos archivos, cambios de archivos
|
||||
- Archivo movido / renombrado
|
||||
- Archivo eliminado/desvinculado
|
||||
|
||||
> ❗ El vigilante trabaja solo en una dirección (local > remota). NO es posible sincronizar automáticamente los cambios de remoto a local.
|
||||
|
||||
@@ -6,18 +6,18 @@
|
||||
|
||||
<p align="center">~ Un file transfer de terminal riche en fonctionnalités ~</p>
|
||||
<p align="center">
|
||||
<a href="https://veeso.github.io/termscp/" target="_blank">Site internet</a>
|
||||
<a href="https://termscp.veeso.dev" target="_blank">Site internet</a>
|
||||
·
|
||||
<a href="https://veeso.github.io/termscp/#get-started" target="_blank">Installation</a>
|
||||
<a href="https://termscp.veeso.dev/#get-started" target="_blank">Installation</a>
|
||||
·
|
||||
<a href="https://veeso.github.io/termscp/#user-manual" target="_blank">Manuel de l'Utilisateur</a>
|
||||
<a href="https://termscp.veeso.dev/#user-manual" target="_blank">Manuel de l'Utilisateur</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/veeso/termscp"
|
||||
><img
|
||||
height="20"
|
||||
src="/assets/images/flags/us.png"
|
||||
src="/assets/images/flags/gb.png"
|
||||
alt="English"
|
||||
/></a>
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">Développé par <a href="https://veeso.github.io/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Version actuelle: 0.8.1 (22/03/2022)</p>
|
||||
<p align="center">Développé par <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Version actuelle: 0.12.1 (06/07/2023)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
@@ -137,6 +137,7 @@ Termscp est un file transfer et explorateur de fichiers de terminal riche en fon
|
||||
- **SCP**
|
||||
- **FTP** et **FTPS**
|
||||
- **S3**
|
||||
- **SMB**
|
||||
- 🖥 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.
|
||||
@@ -150,6 +151,7 @@ Termscp est un file transfer et explorateur de fichiers de terminal riche en fon
|
||||
- tri de fichiers personallisable
|
||||
- et bien d'autres paramètres...
|
||||
- 📫 Recevez une notification quande un gros fichier est télécharger.
|
||||
- 🔭 Gardez les modifications de fichiers synchronisées avec l'hôte distant
|
||||
- 🔐 Enregistre tes mots de passe dans le key vault du systeme.
|
||||
- 🦀 Rust-powered
|
||||
- 👀 Développé en gardant un œil sur les performances
|
||||
@@ -165,7 +167,7 @@ Si tu veux contribuer à ce projet, n'oublié pas de consulter notre [guide de c
|
||||
Si tu es un utilisateur Linux, FreeBSD ou MacOS ce simple shell script installera termscp sur te système en un seule commande:
|
||||
|
||||
```sh
|
||||
curl --proto '=https' --tlsv1.2 -sSLf "https://git.io/JBhDb" | sh
|
||||
curl -sSLf http://get-termscp.veeso.dev | sh
|
||||
```
|
||||
|
||||
tandis que si tu es un utilisateur Windows, tu peux installer termscp avec [Chocolatey](https://chocolatey.org/):
|
||||
@@ -174,20 +176,20 @@ tandis que si tu es un utilisateur Windows, tu peux installer termscp avec [Choc
|
||||
choco install termscp
|
||||
```
|
||||
|
||||
Pour plus d'informations sur les autres méthodes d'installation, veuillez visiter [veeso.github.io](https://veeso.github.io/termscp/#get-started).
|
||||
Pour plus d'informations sur les autres méthodes d'installation, veuillez visiter [termscp.veeso.dev](https://termscp.veeso.dev/termscp/#get-started).
|
||||
|
||||
⚠️ Si tu cherche comme de mettre à jour termscp, tu dois exécuter cette commande dans le terminal: `(sudo) termscp --update` ⚠️
|
||||
|
||||
### Requis ❗
|
||||
|
||||
- utilisateurs **Linux**:
|
||||
- libssh
|
||||
- **Linux** users:
|
||||
- libdbus-1
|
||||
- pkg-config
|
||||
- utilisateurs **FreeBSD**:
|
||||
- libssh
|
||||
- libsmbclient
|
||||
- **FreeBSD** or, **NetBSD** users:
|
||||
- dbus
|
||||
- pkgconf
|
||||
- libsmbclient
|
||||
|
||||
### Requis facultatives ✔️
|
||||
|
||||
@@ -220,7 +222,7 @@ Tu peux faire un don avec l'une de ces plateformes:
|
||||
|
||||
## Manuel d'utilisateur et Documentation 📚
|
||||
|
||||
Le manuel d'utilisateur peut être trouvé sur le [site de termscp](https://veeso.github.io/termscp/#user-manual) ou sur [Github](man.md).
|
||||
Le manuel d'utilisateur peut être trouvé sur le [site de termscp](https://termscp.veeso.dev/termscp/#user-manual) ou sur [Github](man.md).
|
||||
|
||||
La documentation peut être trouvé sur Rust Docs <https://docs.rs/termscp>
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
- [Usage ❓](#usage-)
|
||||
- [Argument d'adresse 🌎](#argument-dadresse-)
|
||||
- [Argument d'adresse AWS S3](#argument-dadresse-aws-s3)
|
||||
- [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)
|
||||
- [Identifiants S3 🦊](#identifiants-s3-)
|
||||
@@ -28,6 +29,7 @@
|
||||
- [Éditeur de texte ✏](#éditeur-de-texte-)
|
||||
- [Fichier Journal 🩺](#fichier-journal-)
|
||||
- [Notifications 📫](#notifications-)
|
||||
- [Observateur de fichiers 🔭](#observateur-de-fichiers-)
|
||||
|
||||
## Usage ❓
|
||||
|
||||
@@ -35,7 +37,12 @@ termscp peut être démarré avec les options suivantes :
|
||||
|
||||
`termscp [options]... [protocol://user@address:port:wrkdir] [local-wrkdir]`
|
||||
|
||||
ou
|
||||
|
||||
`termscp [options]... -b [bookmark-name] [local-wrkdir]`
|
||||
|
||||
- `-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é
|
||||
@@ -97,6 +104,23 @@ e.g.
|
||||
s3://buckethead@eu-central-1:default:/assets
|
||||
```
|
||||
|
||||
#### 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 :
|
||||
|
||||
syntaxe **Windows**:
|
||||
|
||||
```txt
|
||||
\\[username@]<server-name>\<share>[\path\...]
|
||||
```
|
||||
|
||||
syntaxe **Other systems**:
|
||||
|
||||
```txt
|
||||
smb://[username@]<server-name>[:port]/<share>[/path/.../]
|
||||
```
|
||||
|
||||
|
||||
#### Comment le mot de passe peut être fourni 🔐
|
||||
|
||||
Vous avez probablement remarqué que, lorsque vous fournissez l'adresse comme argument, il n'y a aucun moyen de fournir le mot de passe.
|
||||
@@ -196,16 +220,20 @@ Pour changer de panneau, vous devez taper `<LEFT>` pour déplacer le panneau de
|
||||
| `<M>` | Sélectionner un fichier | Mark |
|
||||
| `<N>` | Créer un nouveau fichier avec le nom fourni | New |
|
||||
| `<O|F4>` | Modifier le fichier | Open |
|
||||
| `<P>` | Ouvre le panel de journals | Panel |
|
||||
| `<Q|F10>` | Quitter termscp | Quit |
|
||||
| `<R|F6>` | Renommer le fichier | Rename |
|
||||
| `<S|F2>` | Enregistrer le fichier sous... | Save |
|
||||
| `<T>` | Synchroniser les modifications apportées au chemin sélectionné | Track |
|
||||
| `<U>` | Aller dans le répertoire parent | Upper |
|
||||
| `<V|F3>` | Ouvrir le fichier avec le programme défaut pour le type de fichier | View |
|
||||
| `<W>` | Ouvrir le fichier avec le programme spécifié | With |
|
||||
| `<X>` | Exécuter une commande | eXecute |
|
||||
| `<Y>` | Basculer la navigation synchronisée | sYnc |
|
||||
| `<Z>` | Changer permissions de fichier | |
|
||||
| `<CTRL+A>` | Sélectionner tous les fichiers | |
|
||||
| `<CTRL+C>` | Abandonner le processus de transfert de fichiers | |
|
||||
| `<CTRL+T>` | Afficher tous les chemins synchronisés | Track |
|
||||
|
||||
### Travailler sur plusieurs fichiers 🥷
|
||||
|
||||
@@ -328,7 +356,7 @@ Ces paramètres peuvent être modifiés :
|
||||
- **Local File formatter syntax**: syntaxe pour afficher les informations de fichier pour chaque fichier dans l'explorateur local. Voir [File explorer format](#format-de-lexplorateur-de-fichiers)
|
||||
- **Enable notifications?**: S'il est défini sur `Yes`, les notifications seront affichées.
|
||||
- **Notifications: minimum transfer size**: si la taille du transfert est supérieure ou égale à la valeur spécifiée, les notifications de transfert seront affichées. Les valeurs acceptées sont au format `{UNSIGNED} B/KB/MB/GB/TB/PB`
|
||||
- **SSH configuration path** : définissez le fichier de configuration SSH à utiliser lors de la connexion à un serveur SCP/SFTP. S'il n'est pas défini (vide), aucun fichier ne sera utilisé. Vous pouvez spécifier un chemin commençant par `~` pour indiquer le chemin d'accueil (par exemple `~/.ssh/config`)
|
||||
- **SSH configuration path** : définissez le fichier de configuration SSH à utiliser lors de la connexion à un serveur SCP/SFTP. S'il n'est pas défini (vide), aucun fichier ne sera utilisé. Vous pouvez spécifier un chemin commençant par `~` pour indiquer le chemin d'accueil (par exemple `~/.ssh/config`). Les paramétrages disponibles pour la configuration sont listées [ICI](https://github.com/veeso/ssh2-config#exposed-attributes).
|
||||
|
||||
### SSH Key Storage 🔐
|
||||
|
||||
@@ -470,9 +498,9 @@ Si le fichier se trouve sur l'hôte distant, le fichier sera d'abord télécharg
|
||||
|
||||
termscp écrit un fichier journal pour chaque session, qui est écrit à
|
||||
|
||||
- `$HOME/.config/termscp/termscp.log` sous Linux/BSD
|
||||
- `$HOME/Library/Application Support/termscp/termscp.log` sous MacOs
|
||||
- `FOLDERID_RoamingAppData\termscp\termscp.log` sous Windows
|
||||
- `$HOME/.cache/termscp/termscp.log` sous Linux/BSD
|
||||
- `$HOME/Library/Caches/termscp/termscp.log` sous MacOs
|
||||
- `FOLDERID_LocalAppData\termscp\termscp.log` sous Windows
|
||||
|
||||
le journal ne sera pas tourné, mais sera simplement tronqué après chaque lancement de termscp, donc si vous souhaitez signaler un problème et que vous souhaitez joindre votre fichier journal, n'oubliez pas de sauvegarder le fichier journal dans un endroit sûr avant de l'utiliser termescp à nouveau.
|
||||
|
||||
@@ -505,3 +533,26 @@ Termscp enverra des notifications de bureau pour ce type d'événements :
|
||||
|
||||
❗ Si vous préférez désactiver les notifications, vous pouvez simplement accéder à la configuration et définir `Enable notifications?` sur `No` 😉.
|
||||
❗ Si vous souhaitez modifier la taille de transfert minimale pour afficher les notifications, vous pouvez modifier la valeur dans la configuration avec la touche `Notifications: minimum transfer size` et la définir sur ce qui vous convient le mieux 🙂.
|
||||
|
||||
## Observateur de fichiers 🔭
|
||||
|
||||
L'observateur de fichiers vous permet de configurer une liste de chemins à synchroniser avec les hôtes distants.
|
||||
Cela signifie que chaque fois qu'un changement sur le système de fichiers local sera détecté sur le chemin synchronisé, le changement sera automatiquement signalé au chemin de l'hôte distant configuré, dans les 5 secondes.
|
||||
|
||||
Vous pouvez définir autant de chemins à synchroniser que vous préférez :
|
||||
|
||||
1. Placez le curseur de l'explorateur local sur le répertoire/fichier que vous souhaitez conserver synchronisé
|
||||
2. Accédez au répertoire dans lequel vous souhaitez que les modifications soient signalées sur l'hôte distant
|
||||
3. Appuyez sur `<T>`
|
||||
4. Répondez `<YES>` à la fenêtre contextuelle de la radio
|
||||
|
||||
Pour annuler la surveillance, appuyez simplement sur `<T>` sur le chemin synchronisé local (ou sur l'un de ses sous-dossiers)
|
||||
OU vous pouvez simplement appuyer sur `<CTRL + T>` et appuyer sur `<ENTER>` jusqu'au chemin synchronisé que vous souhaitez désactiver.
|
||||
|
||||
Ces modifications seront signalées à l'hôte distant :
|
||||
|
||||
- Nouveaux fichiers, modifications de fichiers
|
||||
- Fichier déplacé / renommé
|
||||
- Fichier supprimé / dissocié
|
||||
|
||||
> ❗ Le watcher ne fonctionne que dans un sens (local > distant). Il n'est PAS possible de synchroniser automatiquement les changements de distant à local.
|
||||
|
||||
@@ -6,18 +6,18 @@
|
||||
|
||||
<p align="center">~ Un file transfer ricco di funzionalità ~</p>
|
||||
<p align="center">
|
||||
<a href="https://veeso.github.io/termscp/" target="_blank">Sito</a>
|
||||
<a href="https://termscp.veeso.dev" target="_blank">Sito</a>
|
||||
·
|
||||
<a href="https://veeso.github.io/termscp/#get-started" target="_blank">Installazione</a>
|
||||
<a href="https://termscp.veeso.dev/#get-started" target="_blank">Installazione</a>
|
||||
·
|
||||
<a href="https://veeso.github.io/termscp/#user-manual" target="_blank">Manuale utente</a>
|
||||
<a href="https://termscp.veeso.dev/#user-manual" target="_blank">Manuale utente</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/veeso/termscp"
|
||||
><img
|
||||
height="20"
|
||||
src="/assets/images/flags/us.png"
|
||||
src="/assets/images/flags/gb.png"
|
||||
alt="English"
|
||||
/></a>
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">Sviluppato da <a href="https://veeso.github.io/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Versione corrente: 0.8.1 (22/03/2022)</p>
|
||||
<p align="center">Sviluppato da <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Versione corrente: 0.12.1 (06/07/2023)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
@@ -137,6 +137,7 @@ Termscp è un file transfer ed explorer ricco di funzionalità, con supporto a S
|
||||
- **SCP**
|
||||
- **FTP** and **FTPS**
|
||||
- **S3**
|
||||
- **SMB**
|
||||
- 🖥 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.
|
||||
@@ -150,6 +151,7 @@ Termscp è un file transfer ed explorer ricco di funzionalità, con supporto a S
|
||||
- Imposta l'ordinamento di file e cartelle
|
||||
- e tanto altro...
|
||||
- 📫 Ricevi notifiche desktop quando un file di cospicue dimensioni è stato trasferito
|
||||
- 🔭 Mantieni sincronizzate le modifiche con l'host remoto
|
||||
- 🔐 Salva le password degli host remoti nel keyring predefinito dal tuo sistema operativo
|
||||
- 🦀 Rust-powered
|
||||
- 👀 Progettato tenendo conto delle performance
|
||||
@@ -165,7 +167,7 @@ Se vuoi contribuire al progetto, non dimenticarti di leggere la [contribute guid
|
||||
Se sei un utente che utilizza Linux, FreeBSD o MacOS, questo shell script installerà termscp sul tuo sistema con un comando secco:
|
||||
|
||||
```sh
|
||||
curl --proto '=https' --tlsv1.2 -sSLf "https://git.io/JBhDb" | sh
|
||||
curl -sSLf http://get-termscp.veeso.dev | sh
|
||||
```
|
||||
|
||||
mentre se sei un utente Windows, puoi installare termscp con [Chocolatey](https://chocolatey.org/):
|
||||
@@ -174,20 +176,20 @@ mentre se sei un utente Windows, puoi installare termscp con [Chocolatey](https:
|
||||
choco install termscp
|
||||
```
|
||||
|
||||
Per ulteriori informazioni sui metodi di installazione su altre piattaforme, visita [veeso.github.io](https://veeso.github.io/termscp/#get-started).
|
||||
Per ulteriori informazioni sui metodi di installazione su altre piattaforme, visita [termscp.veeso.dev](https://termscp.veeso.dev/termscp/#get-started).
|
||||
|
||||
⚠️ Se stavi cercando come aggiornare la tua versione di termscp, puoi semplicemente lanciare termscp con questi argomenti: `(sudo) termscp --update` ⚠️
|
||||
|
||||
### Requisiti ❗
|
||||
|
||||
- Utenti **Linux**:
|
||||
- libssh
|
||||
- **Linux** users:
|
||||
- libdbus-1
|
||||
- pkg-config
|
||||
- Utenti **FreeBSD**:
|
||||
- libssh
|
||||
- libsmbclient
|
||||
- **FreeBSD** or, **NetBSD** users:
|
||||
- dbus
|
||||
- pkgconf
|
||||
- libsmbclient
|
||||
|
||||
### Requisiti opzionali ✔️
|
||||
|
||||
@@ -220,7 +222,7 @@ Puoi fare una donazione tramite una di queste piattaforme:
|
||||
|
||||
## Manuale utente 📚
|
||||
|
||||
Il manuale utente lo puoi trovare sul [sito di termscp](https://veeso.github.io/termscp/#user-manual) o su [Github](man.md).
|
||||
Il manuale utente lo puoi trovare sul [sito di termscp](https://termscp.veeso.dev/termscp/#user-manual) o su [Github](man.md).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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)
|
||||
- [Indirizzo SMB](#indirizzo-smb)
|
||||
- [Come fornire la password 🔐](#come-fornire-la-password-)
|
||||
- [Parametri di connessione S3](#parametri-di-connessione-s3)
|
||||
- [Credenziali S3 🦊](#credenziali-s3-)
|
||||
@@ -28,6 +29,7 @@
|
||||
- [Editor di testo ✏](#editor-di-testo-)
|
||||
- [Logging 🩺](#logging-)
|
||||
- [Notifiche 📫](#notifiche-)
|
||||
- [File watcher 🔭](#file-watcher-)
|
||||
|
||||
## Argomenti da linea di comando ❓
|
||||
|
||||
@@ -35,7 +37,12 @@ termscp può essere lanciato con questi argomenti:
|
||||
|
||||
`termscp [options]... [protocol://user@address:port:wrkdir] [local-wrkdir]`
|
||||
|
||||
O
|
||||
|
||||
`termscp [options]... -b [bookmark-name] [local-wrkdir]`
|
||||
|
||||
- `-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
|
||||
@@ -95,6 +102,23 @@ e.g.
|
||||
s3://buckethead@eu-central-1:default:/assets
|
||||
```
|
||||
|
||||
#### Indirizzo SMB
|
||||
|
||||
SMB ha una sintassi differente rispetto agli altri protocolli e cambia in base al sistema operativo:
|
||||
|
||||
**Windows**:
|
||||
|
||||
```txt
|
||||
\\[username@]<server-name>\<share>[\path\...]
|
||||
```
|
||||
|
||||
**Altri sistemi**:
|
||||
|
||||
```txt
|
||||
smb://[username@]<server-name>[:port]/<share>[/path/.../]
|
||||
```
|
||||
|
||||
|
||||
#### Come fornire la password 🔐
|
||||
|
||||
Quando si usa l'argomento indirizzo non è possibile fornire la password direttamente nell'argomento, esistono però altri metodi per farlo:
|
||||
@@ -192,16 +216,20 @@ Per cambiare pannello ti puoi muovere con le frecce, `<LEFT>` per andare sul pan
|
||||
| `<M>` | Seleziona file | Mark |
|
||||
| `<N>` | Crea nuovo file con il nome fornito | New |
|
||||
| `<O|F4>` | Modifica file; Vedi text editor | Open |
|
||||
| `<P>` | Apri pannello log | Panel |
|
||||
| `<Q|F10>` | Termina termscp | Quit |
|
||||
| `<R|F6>` | Rinomina file | Rename |
|
||||
| `<S|F2>` | Salva file con nome | Save |
|
||||
| `<T>` | Sincronizza il percorso locale con l'host remoto | Track |
|
||||
| `<U>` | Vai alla directory padre | Upper |
|
||||
| `<V|F3>` | Apri il file con il programma definito dal sistema | View |
|
||||
| `<W>` | Apri il file con il programma specificato | With |
|
||||
| `<X>` | Esegui comando shell | eXecute |
|
||||
| `<Y>` | Abilita/disabilita Sync-Browsing | sYnc |
|
||||
| `<Z>` | Modifica permessi file | |
|
||||
| `<CTRL+A>` | Seleziona tutti i file | |
|
||||
| `<CTRL+C>` | Annulla trasferimento file | |
|
||||
| `<CTRL+T>` | Visualizza tutti i percorsi sincronizzati | Track |
|
||||
|
||||
### Lavora su più file 🥷
|
||||
|
||||
@@ -326,7 +354,7 @@ Questi parametri possono essere impostati:
|
||||
- **Local File formatter syntax**: La formattazione da usare per formattare i file sull'explorer locale. Vedi [File explorer format](#file-explorer-format)
|
||||
- **Enable notifications?**: Se impostato a `yes`, le notifiche desktop saranno abilitate.
|
||||
- **Notifications: minimum transfer size**: se la dimensione di un trasferimento supera o è uguale al valore impostato, al termine del trasferimento riceverai una notifica desktop (se queste sono abilitate). Il formato del valore dev'essere `{UNSIGNED} B/KB/MB/GB/TB/PB`
|
||||
- **SSH configuration path**: Imposta il percorso del file di configurazione per SSH, per quando ci si connette ad un server SFTP/SCP. Se lasciato vuoto, nessun file verrà usato. Il percorso può anche iniziare con `~` per indicare il percorso della home dell'utente attuale (e.s. `~/.ssh/config`).
|
||||
- **SSH configuration path**: Imposta il percorso del file di configurazione per SSH, per quando ci si connette ad un server SFTP/SCP. Se lasciato vuoto, nessun file verrà usato. Il percorso può anche iniziare con `~` per indicare il percorso della home dell'utente attuale (e.s. `~/.ssh/config`). I parametri supportati dalla configurazioni sono descritti [QUI](https://github.com/veeso/ssh2-config#exposed-attributes).
|
||||
|
||||
### SSH Key Storage 🔐
|
||||
|
||||
@@ -469,9 +497,9 @@ Nel caso il file si trovi su host remoto, il file verrà prima scaricato tempora
|
||||
|
||||
termscp scrive un file di log per ogni sessione, nel percorso seguente:
|
||||
|
||||
- `$HOME/.config/termscp/termscp.log` su Linux/BSD
|
||||
- `$HOME/Library/Application Support/termscp/termscp.log` su MacOs
|
||||
- `FOLDERID_RoamingAppData\termscp\termscp.log` su Windows
|
||||
- `$HOME/.cache/termscp/termscp.log` su Linux/BSD
|
||||
- `$HOME/Library/Caches/termscp/termscp.log` su MacOs
|
||||
- `FOLDERID_LocalAppData\termscp\termscp.log` su Windows
|
||||
|
||||
Il log non viene ruotato, ma viene troncato ad ogni lancio di termscp, quindi se devi riportare un issue, non avviare termscp fino a che non avrai salvato il file di log.
|
||||
I log sono di default riportati a livello *INFO*, quindi non sono particolarmente parlanti.
|
||||
@@ -503,3 +531,26 @@ termscp invierà notifiche destkop per i seguenti eventi:
|
||||
|
||||
❗ Se vuoi disabilitare le notifiche, è sufficiente andare in configurazione ed impostare `Enable notifications?` a `No` 😉.
|
||||
❗ Se vuoi modificare la soglia minima per le notifiche dei trasferimenti, puoi impostare il valore di `Notifications: minimum transfer size` in configurazione 🙂.
|
||||
|
||||
## File watcher 🔭
|
||||
|
||||
Il file watcher ti permette di impostare una lista di percorsi da sincronizzare con l'host remoto.
|
||||
Ciò implica che ogni volta che una modifica verrà rilevata al percorso sincronizzato, la modifica verrà automaticamente sincronizzata con l'host remoto, entro 5 secondi.
|
||||
|
||||
Puoi impostare quanti percorsi preferisci da sincronizzare:
|
||||
|
||||
1. Porta il cursore dell'explorer sulla cartella/file che vuoi sincronizzare
|
||||
2. Vai alla directory sull'explorer remoto dove vuoi riportare le modifiche
|
||||
3. Premi `<T>`
|
||||
4. Rispondi `<YES>` alla domanda se vuoi sincronizzare il percorso
|
||||
|
||||
Per terminare la sincronizzazione, premi `<T>`, al percorso locale sincronizzato (od in qualsiasi sua sottocartella)
|
||||
OPPURE, puoi semplicemente premere `<CTRL+T>` e premi `<ENTER>` sul percorso che vuoi desincronizzare.
|
||||
|
||||
Queste modifiche verranno applicate sull'host remoto:
|
||||
|
||||
- Nuovi file, modifiche
|
||||
- File spostati o rinominati
|
||||
- File rimossi
|
||||
|
||||
> ❗ Il watcher funziona solo in maniera unidirezionale locale > remoto. NON è possibile tracciare le modifiche da remoto a locale.
|
||||
|
||||
64
docs/man.md
@@ -4,6 +4,7 @@
|
||||
- [Usage ❓](#usage-)
|
||||
- [Address argument 🌎](#address-argument-)
|
||||
- [AWS S3 address argument](#aws-s3-address-argument)
|
||||
- [SMB address argument](#smb-address-argument)
|
||||
- [How Password can be provided 🔐](#how-password-can-be-provided-)
|
||||
- [S3 connection parameters](#s3-connection-parameters)
|
||||
- [S3 credentials 🦊](#s3-credentials-)
|
||||
@@ -28,6 +29,7 @@
|
||||
- [Text Editor ✏](#text-editor-)
|
||||
- [Logging 🩺](#logging-)
|
||||
- [Notifications 📫](#notifications-)
|
||||
- [File watcher 🔭](#file-watcher-)
|
||||
|
||||
## Usage ❓
|
||||
|
||||
@@ -35,7 +37,12 @@ termscp can be started with the following options:
|
||||
|
||||
`termscp [options]... [protocol://user@address:port:wrkdir] [local-wrkdir]`
|
||||
|
||||
OR
|
||||
|
||||
`termscp [options]... -b [bookmark-name] [local-wrkdir]`
|
||||
|
||||
- `-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
|
||||
@@ -43,11 +50,11 @@ termscp can be started with the following options:
|
||||
- `-v, --version` Print version info
|
||||
- `-h, --help` Print help page
|
||||
|
||||
termscp can be started in two different mode, if no extra arguments is provided, termscp will show the authentication form, where the user will be able to provide the parameters required to connect to the remote peer.
|
||||
termscp can be started in three different modes, if no extra arguments is provided, termscp will show the authentication form, where the user will be able to provide the parameters required to connect to the remote peer.
|
||||
|
||||
Alternatively, the user can provide an address as argument to skip the authentication form and starting directly the connection to the remote server.
|
||||
|
||||
If address argument is provided you can also provide the start working directory for local host
|
||||
If address argument or bookmark name is provided you can also provide the start working directory for local host
|
||||
|
||||
### Address argument 🌎
|
||||
|
||||
@@ -97,6 +104,22 @@ e.g.
|
||||
s3://buckethead@eu-central-1:default:/assets
|
||||
```
|
||||
|
||||
#### SMB address argument
|
||||
|
||||
SMB has a different syntax for CLI address argument, which is different whether you're on Windows or other systems:
|
||||
|
||||
**Windows** syntax:
|
||||
|
||||
```txt
|
||||
\\[username@]<server-name>\<share>[\path\...]
|
||||
```
|
||||
|
||||
**Other systems** syntax:
|
||||
|
||||
```txt
|
||||
smb://[username@]<server-name>[:port]/<share>[/path/.../]
|
||||
```
|
||||
|
||||
#### How Password can be provided 🔐
|
||||
|
||||
You have probably noticed, that, when providing the address as argument, there's no way to provide the password.
|
||||
@@ -196,16 +219,20 @@ In order to change panel you need to type `<LEFT>` to move the remote explorer p
|
||||
| `<M>` | Select a file | Mark |
|
||||
| `<N>` | Create new file with provided name | New |
|
||||
| `<O|F4>` | Edit file; see Text editor | Open |
|
||||
| `<P>` | Open log panel | Panel |
|
||||
| `<Q|F10>` | Quit termscp | Quit |
|
||||
| `<R|F6>` | Rename file | Rename |
|
||||
| `<S|F2>` | Save file as... | Save |
|
||||
| `<T>` | Synchronize changes to selected path to remote | Track |
|
||||
| `<U>` | Go to parent directory | Up |
|
||||
| `<V|F3>` | Open file with default program for filetype | View |
|
||||
| `<W>` | Open file with provided program | With |
|
||||
| `<X>` | Execute a command | eXecute |
|
||||
| `<Y>` | Toggle synchronized browsing | sYnc |
|
||||
| `<Z>` | Change file mode | |
|
||||
| `<CTRL+A>` | Select all files | |
|
||||
| `<CTRL+C>` | Abort file transfer process | |
|
||||
| `<CTRL+T>` | Show all synchronized paths | Track |
|
||||
|
||||
### Work on multiple files 🥷
|
||||
|
||||
@@ -329,7 +356,7 @@ These parameters can be changed:
|
||||
- **Local File formatter syntax**: syntax to display file info for each file in the local explorer. See [File explorer format](#file-explorer-format)
|
||||
- **Enable notifications?**: If set to `Yes`, notifications will be displayed.
|
||||
- **Notifications: minimum transfer size**: if transfer size is greater or equal than the specified value, notifications for transfer will be displayed. The accepted values are in format `{UNSIGNED} B/KB/MB/GB/TB/PB`
|
||||
- **SSH configuration path**: Set SSH configuration file to use when connecting to a SCP/SFTP server. If unset (empty) no file will be used. You can specify a path starting with `~` to indicate the home path (e.g. `~/.ssh/config`)
|
||||
- **SSH configuration path**: Set SSH configuration file to use when connecting to a SCP/SFTP server. If unset (empty) no file will be used. You can specify a path starting with `~` to indicate the home path (e.g. `~/.ssh/config`). The parameters supported by termscp are specified [HERE](https://github.com/veeso/ssh2-config#exposed-attributes).
|
||||
|
||||
### SSH Key Storage 🔐
|
||||
|
||||
@@ -471,9 +498,9 @@ In case the file is located on remote host, the file will be first downloaded in
|
||||
|
||||
termscp writes a log file for each session, which is written at
|
||||
|
||||
- `$HOME/.config/termscp/termscp.log` on Linux/BSD
|
||||
- `$HOME/Library/Application Support/termscp/termscp.log` on MacOs
|
||||
- `FOLDERID_RoamingAppData\termscp\termscp.log` on Windows
|
||||
- `$HOME/.cache/termscp/termscp.log` on Linux/BSD
|
||||
- `$HOME/Library/Caches/termscp/termscp.log` on MacOs
|
||||
- `FOLDERID_LocalAppData\termscp\termscp.log` on Windows
|
||||
|
||||
the log won't be rotated, but will just be truncated after each launch of termscp, so if you want to report an issue and you want to attach your log file, keep in mind to save the log file in a safe place before using termscp again.
|
||||
The logging by default reports in *INFO* level, so it is not very verbose.
|
||||
@@ -505,3 +532,28 @@ Termscp will send Desktop notifications for these kind of events:
|
||||
|
||||
❗ If you prefer to keep notifications turned off, you can just enter setup and set `Enable notifications?` to `No` 😉.
|
||||
❗ If you want to change the minimum transfer size to display notifications, you can change the value in the configuration with key `Notifications: minimum transfer size` and set it to whatever suits better for you 🙂.
|
||||
|
||||
---
|
||||
|
||||
## File watcher 🔭
|
||||
|
||||
The file watcher allows you to setup a list of paths to synchronize with the remote hosts.
|
||||
This means that whenever a change on the local file system will be detected on the synchronized path, the change will be automatically reported to the configured remote host path, within 5 seconds.
|
||||
|
||||
You can set as many paths to synchronize as you prefer:
|
||||
|
||||
1. Put the cursor on the local explorer on the directory/file you want to keep synchronized
|
||||
2. Go to the directory you want the changes to be reported to on the remote host
|
||||
3. Press `<T>`
|
||||
4. Answer `<YES>` to the radio popup
|
||||
|
||||
To unwatch, just press `<T>` on the local synchronized path (or to any of its subfolders)
|
||||
OR you can just press `<CTRL+T>` and press `<ENTER>` to the synchronized path you want to unwatch.
|
||||
|
||||
These changes will be reported to the remote host:
|
||||
|
||||
- New files, file changes
|
||||
- File moved/renamed
|
||||
- File removed/unlinked
|
||||
|
||||
> ❗ The watcher works only in one direction (local > remote). It is NOT possible to synchronize automatically the changes from remote to local.
|
||||
|
||||
@@ -8,7 +8,7 @@ Features:
|
||||
- SFTP
|
||||
- SCP
|
||||
- FTP and FTPS
|
||||
- Aws S3
|
||||
- S3
|
||||
- 🖥 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
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
<img src="/assets/images/termscp.svg" width="256" height="256" />
|
||||
</p>
|
||||
|
||||
<p align="center">~ 功能丰富的终端文件传输 ~</p>
|
||||
<p align="center">~ 功能丰富的终端文件传输工具 ~</p>
|
||||
<p align="center">
|
||||
<a href="https://veeso.github.io/termscp/" target="_blank">网站</a>
|
||||
<a href="https://termscp.veeso.dev" target="_blank">网站</a>
|
||||
·
|
||||
<a href="https://veeso.github.io/termscp/#get-started" target="_blank">安装</a>
|
||||
<a href="https://termscp.veeso.dev/#get-started" target="_blank">安装</a>
|
||||
·
|
||||
<a href="https://veeso.github.io/termscp/#user-manual" target="_blank">用户手册</a>
|
||||
<a href="https://termscp.veeso.dev/#user-manual" target="_blank">用户手册</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/veeso/termscp"
|
||||
><img
|
||||
height="20"
|
||||
src="/assets/images/flags/us.png"
|
||||
src="/assets/images/flags/gb.png"
|
||||
alt="English"
|
||||
/></a>
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">由 <a href="https://veeso.github.io/" target="_blank">@veeso</a> 开发</p>
|
||||
<p align="center">当前版本: 0.8.1 (22/03/2022)</p>
|
||||
<p align="center">由 <a href="https://veeso.dev/" target="_blank">@veeso</a> 开发</p>
|
||||
<p align="center">当前版本: 0.12.1 (06/07/2023)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
@@ -124,49 +124,53 @@
|
||||
|
||||
## 关于 termscp 🖥
|
||||
|
||||
termscp 是一个功能丰富的终端文件传输和浏览器,支持 SCP/SFTP/FTP/S3。 所以基本上是一个带有 TUI 的终端实用程序,用于连接到远程服务器以检索和上传文件并与本地文件系统进行交互。
|
||||
它与 **Linux**、**MacOS**、**FreeBSD** 和 **Windows** 兼容。
|
||||
termscp 是一个功能丰富的终端文件浏览和传输工具,支持 SCP/SFTP/FTP/S3。 作为一个带有 TUI 的命令行工具,它可以连接到远程服务器进行文件检索和上传,并能够与本地文件系统进行交互。
|
||||
|
||||
兼容 **Linux**、**MacOS**、**FreeBSD** 和 **Windows** 操作系统。
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 特征 🎁
|
||||
## 特性 🎁
|
||||
|
||||
- 📁 不同的通讯协议
|
||||
- 📁 支持多种通信协议
|
||||
- **SFTP**
|
||||
- **SCP**
|
||||
- **FTP** and **FTPS**
|
||||
- **S3**
|
||||
- 🖥 使用方便的 UI 在远程和本地机器文件系统上探索和操作
|
||||
- **SMB**
|
||||
- 🖥 使用便捷的 UI 在远程和本地文件系统上浏览和操作
|
||||
- 创建、删除、重命名、搜索、查看和编辑文件
|
||||
- ⭐ 通过内置书签和最近的连接连接到您最喜欢的主机
|
||||
- ⭐ 通过“内置书签”和“最近连接”快速连接到您的主机
|
||||
- 📝 使用您喜欢的应用程序查看和编辑文件
|
||||
- 💁 使用 SSH 密钥和用户名/密码进行 SFTP/SCP 身份验证
|
||||
- 🐧 与 Windows、Linux、FreeBSD 和 MacOS 兼容
|
||||
- 🎨 让它成为你的!
|
||||
- 🐧 兼容 Windows、Linux、FreeBSD 和 MacOS 操作系统
|
||||
- 🎨 丰富的个性化设置!
|
||||
- 主题
|
||||
- 自定义文件浏览器格式
|
||||
- 可定制的文本编辑器
|
||||
- 可定制的文件排序
|
||||
- 和许多其他参数...
|
||||
- 📫 传输大文件时通过桌面通知获得通知
|
||||
- 可选择的文本编辑器
|
||||
- 可选择的文件排序
|
||||
- 探索更多功能...
|
||||
- 📫 传输大文件时通过桌面通知获得提醒
|
||||
- 🔭 与远程主机文件更改保持同步
|
||||
- 🔐 将密码保存在操作系统密钥保管库中
|
||||
- 🦀 Rust 动力
|
||||
- 👀 开发时注意性能
|
||||
- 🦄 频繁的精彩更新
|
||||
- 🦀 由 Rust 提供强力支持
|
||||
- 👀 开发时更注重性能
|
||||
- 🦄 快速且精彩迭代
|
||||
|
||||
---
|
||||
|
||||
## 开始 🚀
|
||||
|
||||
如果您正在考虑安装termscp,我要感谢您💜! 我希望你会喜欢termscp!
|
||||
非常荣幸您能考虑安装termscp💜! 希望你会喜欢termscp!
|
||||
|
||||
如果您想为此项目做出贡献,请不要忘记查看我们的贡献指南。 [阅读更多](../../CONTRIBUTING.md)
|
||||
|
||||
如果您是 Linux、FreeBSD 或 MacOS 用户,这个简单的 shell 脚本将使用单个命令在您的系统上安装 termscp:
|
||||
如果您是 Linux、FreeBSD 或 MacOS 用户,使用以下简单的 shell 脚本通过单行指令在您的系统上安装 termscp:
|
||||
|
||||
```sh
|
||||
curl --proto '=https' --tlsv1.2 -sSLf "https://git.io/JBhDb" | sh
|
||||
curl -sSLf http://get-termscp.veeso.dev | sh
|
||||
```
|
||||
|
||||
如果您是 Windows 用户,则可以使用 [Chocolatey](https://chocolatey.org/) 安装 termscp:
|
||||
@@ -175,24 +179,26 @@ curl --proto '=https' --tlsv1.2 -sSLf "https://git.io/JBhDb" | sh
|
||||
choco install termscp
|
||||
```
|
||||
|
||||
如需更多信息或其他平台,请访问 [veeso.github.io](https://veeso.github.io/termscp/#get-started) 查看所有安装方法。
|
||||
如需更多信息或其他的平台支持,请访问 [termscp.veeso.dev](https://termscp.veeso.dev/termscp/#get-started) 查看所有安装方法。
|
||||
|
||||
⚠️ 如果您正在寻找如何更新 termscp 只需从 CLI 运行 termscp : `(sudo) termscp --update` ⚠️
|
||||
|
||||
### 要求 ❗
|
||||
### 依赖 ❗
|
||||
|
||||
- **Linux** 用户:
|
||||
- libssh
|
||||
- libdbus-1
|
||||
- pkg-config
|
||||
- libsmbclient
|
||||
- **FreeBSD** 用户:
|
||||
- libssh
|
||||
- dbus
|
||||
- pkgconf
|
||||
- libsmbclient
|
||||
|
||||
### 可选要求 ✔️
|
||||
### 可选项 ✔️
|
||||
|
||||
这些要求不是运行 termscp 的强制要求,而是要享受它的所有功能
|
||||
通过执行以下操作以享受软件的完整功能,但不做强制要求
|
||||
|
||||
- **Linux/FreeBSD** 用户:
|
||||
- 用 `V` **打开** 文件(至少其中之一)
|
||||
@@ -210,11 +216,11 @@ choco install termscp
|
||||
|
||||
## 支持我 ☕
|
||||
|
||||
如果您喜欢 termscp 并且希望看到该项目不断发展和改进,请考虑在 **Buy me a coffee** 上捐款以支持我🥳
|
||||
如果您喜欢 termscp 并且希望看到该项目不断发展和改进,请考虑在 **Buy me a coffee** 上赞赏以支持我🥳
|
||||
|
||||
[](https://ko-fi.com/veeso)
|
||||
|
||||
或者,如果您愿意,您也可以在 PayPal 上捐款:
|
||||
或者,如果您愿意,您也可以在 PayPal 上赞赏我:
|
||||
|
||||
[](https://www.paypal.me/chrisintin)
|
||||
|
||||
@@ -222,26 +228,27 @@ choco install termscp
|
||||
|
||||
## 用户手册和文档 📚
|
||||
|
||||
用户手册可以在[termscp的网站](https://veeso.github.io/termscp/#user-manual)上找到 或者在[Github](man.md)上。s
|
||||
用户手册可以在[termscp的网站](https://termscp.veeso.dev/termscp/#user-manual)或者在[Github](man.md)上找到。
|
||||
|
||||
---
|
||||
|
||||
## 贡献和问题 🤝🏻
|
||||
|
||||
欢迎贡献、错误报告、新功能和问题! 😉
|
||||
如果您有任何问题或疑虑,或者您想建议新功能,或者您只想改进termscp,请随时打开问题或 PR。
|
||||
欢迎贡献、bug报告、新功能和问题! 😉
|
||||
|
||||
如果您有任何问题或困惑,或者您想建议新功能,或者您只是想改进termscp,请随时打开 issue 或 PR。
|
||||
|
||||
请遵循 [我们的贡献指南](../../CONTRIBUTING.md)
|
||||
|
||||
---
|
||||
|
||||
## 变更日志 ⏳
|
||||
## 更新日志 ⏳
|
||||
|
||||
查看termscp的更新日志 [这里](../../CHANGELOG.md)
|
||||
查看termscp的 [更新日志](../../CHANGELOG.md)
|
||||
|
||||
---
|
||||
|
||||
## 供电 💪
|
||||
## 支持 💪
|
||||
|
||||
termscp 由这些很棒的项目提供支持:
|
||||
|
||||
@@ -262,9 +269,9 @@ termscp 由这些很棒的项目提供支持:
|
||||
|
||||
---
|
||||
|
||||
## 画廊 🎬
|
||||
## 演示 🎬
|
||||
|
||||
> 家
|
||||
> 首页
|
||||
|
||||

|
||||
|
||||
@@ -282,8 +289,8 @@ termscp 由这些很棒的项目提供支持:
|
||||
|
||||
---
|
||||
|
||||
## 执照 📃
|
||||
## 许可协议 📃
|
||||
|
||||
“termscp”在 MIT 许可下获得许可。
|
||||
“termscp”使用 MIT 许可。
|
||||
|
||||
您可以阅读整个许可证 [这里](../../LICENSE)
|
||||
您可以阅读整个 [许可证](../../LICENSE)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
- [用法](#用法)
|
||||
- [地址参数](#地址参数)
|
||||
- [AWS S3 地址参数](#aws-s3-地址参数)
|
||||
- [SMB 地址参数](#smb-地址参数)
|
||||
- [如何输入密码](#如何输入密码)
|
||||
- [S3 连接参数](#s3-连接参数)
|
||||
- [Aws S3 凭证](#aws-s3-凭证)
|
||||
@@ -28,6 +29,7 @@
|
||||
- [文本编辑器](#文本编辑器)
|
||||
- [日志](#日志)
|
||||
- [通知](#通知)
|
||||
- [文件观察者🔭](#文件观察者)
|
||||
|
||||
## 用法
|
||||
|
||||
@@ -35,7 +37,12 @@ termscp启动时可以使用以下选项:
|
||||
|
||||
`termscp [options]... [protocol://user@address:port:wrkdir] [local-wrkdir]`
|
||||
|
||||
或作为
|
||||
|
||||
`termscp [options]... -b [bookmark-name] [local-wrkdir]`
|
||||
|
||||
- `-P, --password <password>` 登陆密码
|
||||
- `-b, --address-as-bookmark` 将地址参数解析为书签名称
|
||||
- `-c, --config` 打开termscp时打开配置页面
|
||||
- `-q, --quiet` 禁用日志
|
||||
- `-t, --theme <path>` 导入自定义主题
|
||||
@@ -96,6 +103,22 @@ s3://<bucket-name>@<region>[:profile][:/wrkdir]
|
||||
s3://buckethead@eu-central-1:default:/assets
|
||||
```
|
||||
|
||||
#### SMB 地址参数
|
||||
|
||||
SMB 对 CLI 地址参数有不同的语法,无论您是在 Windows 还是其他系统上,这都是不同的:
|
||||
|
||||
**Windows** 句法:
|
||||
|
||||
```txt
|
||||
\\[username@]<server-name>\<share>[\path\...]
|
||||
```
|
||||
|
||||
**其他系统** 句法:
|
||||
|
||||
```txt
|
||||
smb://[username@]<server-name>[:port]/<share>[/path/.../]
|
||||
```
|
||||
|
||||
#### 如何输入密码
|
||||
|
||||
你可能已经注意到,url参数中没有办法直接附加密码,你可以通过以下三种方式提供密码:
|
||||
@@ -193,16 +216,20 @@ termscp中的文件资源管理器是指你与远程建立连接后可以看到
|
||||
| `<M>` | 选中文件 | Mark |
|
||||
| `<N>` | 使用键入的名称新建文件 | New |
|
||||
| `<O|F4>` | 编辑文件;参考文本编辑器文档 | Open |
|
||||
| `<P>` | 打开日志面板 | Panel |
|
||||
| `<Q|F10>` | 退出termscp | Quit |
|
||||
| `<R|F7>` | 重命名文件 | Rename |
|
||||
| `<S|F2>` | 另存为... | Save |
|
||||
| `<T>` | 显示所有同步路径 | Track |
|
||||
| `<U>` | 进入上层目录 | Upper |
|
||||
| `<V|F3>` | 使用默认方式打开文件 | View |
|
||||
| `<W>` | 使用指定程序打开文件 | With |
|
||||
| `<X>` | 运行命令 | eXecute |
|
||||
| `<Y>` | 是否开启同步浏览 | sYnc |
|
||||
| `<Z>` | 更改文件权限 | |
|
||||
| `<CTRL+A>` | 选中所有文件 | |
|
||||
| `<CTRL+C>` | 终止文件传输 | |
|
||||
| `<CTRL+T>` | 显示所有同步路径 | Track |
|
||||
|
||||
### 处理多个文件
|
||||
|
||||
@@ -321,7 +348,7 @@ termscp和书签一样,只需要保证这些路径是可访问的:
|
||||
- **Local File formatter syntax**:在本地资源管理器中显示每个文件的文件信息的语法。参见[资源管理器格式](#资源管理器格式)
|
||||
- **Enable notifications?**: 如果设置为 `Yes`,则会显示通知。
|
||||
- **Notifications: minimum transfer size**: 如果传输大小大于或等于指定值,将显示传输通知。 接受的值格式为 `{UNSIGNED} B/KB/MB/GB/TB/PB`
|
||||
- **SSH Configuration path**:设置连接到 SCP/SFTP 服务器时使用的 SSH 配置文件。 如果未设置(空),则不会使用任何文件。 你可以指定一个以 `~` 开头的路径来表示主路径(例如 `~/.ssh/config`)
|
||||
- **SSH Configuration path**:设置连接到 SCP/SFTP 服务器时使用的 SSH 配置文件。 如果未设置(空),则不会使用任何文件。 你可以指定一个以 `~` 开头的路径来表示主路径(例如 `~/.ssh/config`). 指定了 termscp 支持的参数 [HERE](https://github.com/veeso/ssh2-config#exposed-attributes).
|
||||
|
||||
### SSH Key Storage
|
||||
|
||||
@@ -463,9 +490,9 @@ Termscp有很多功能,你可能已经注意到了,其中之一就是可以
|
||||
|
||||
termscp会为每个会话创建一个日志文件,该文件在
|
||||
|
||||
- `$HOME/.config/termscp/termscp.log` -- Linux/BSD
|
||||
- `$HOME/Library/Application Support/termscp/termscp.log` -- MacOs
|
||||
- `FOLDERID_RoamingAppData\termscp\termscp.log` -- Windows
|
||||
- `$HOME/.cache/termscp/termscp.log` -- Linux/BSD
|
||||
- `$HOME/Library/Caches/termscp/termscp.log` -- MacOs
|
||||
- `FOLDERID_LocalAppData\termscp\termscp.log` -- Windows
|
||||
|
||||
日志不会被轮换,但只会在每次启动 termcp 后被截断,因此如果您想报告问题并希望附加您的日志文件,请记住在使用前将日志文件保存在安全的地方 再次termscp。
|
||||
默认情况下,日志记录在 *INFO* 级别报告,因此它不是很详细。
|
||||
@@ -497,3 +524,26 @@ termscp 将针对这些类型的事件发送桌面通知:
|
||||
|
||||
❗ 如果您希望保持关闭通知,您只需进入设置并将 `Enable notifications?` 设置为 `No`😉。
|
||||
❗ 如果您想更改最小传输大小以显示通知,您可以使用键 `Notifications: minimum transfer size` 更改配置中的值,并将其设置为更适合您的任何值🙂。
|
||||
|
||||
## 文件观察者🔭
|
||||
|
||||
文件观察器允许您设置与远程主机同步的路径列表。
|
||||
这意味着每当在同步路径上检测到本地文件系统的更改时,该更改将在 5 秒内自动报告给配置的远程主机路径。
|
||||
|
||||
您可以根据需要设置尽可能多的同步路径:
|
||||
|
||||
1.将光标放在本地资源管理器上要保持同步的目录/文件上
|
||||
2. 转到远程主机上要向其报告更改的目录
|
||||
3. 按`<T>`
|
||||
4. 对无线电弹出窗口回答 `<YES>`
|
||||
|
||||
要取消观看,只需在本地同步路径(或其任何子文件夹)上按 `<T>`
|
||||
或者,您可以按 `<CTRL + T>` 并按 `<ENTER>` 进入要取消观看的同步路径。
|
||||
|
||||
这些更改将报告给远程主机:
|
||||
|
||||
- 新文件,文件更改
|
||||
- 文件移动/重命名
|
||||
- 文件删除/取消链接
|
||||
|
||||
> ❗ 观察者只在一个方向工作(本地>远程)。不可能自动同步远程到本地的更改。
|
||||
|
||||
180
install.sh
@@ -8,10 +8,14 @@
|
||||
# -f, -y, --force, --yes
|
||||
# Skip the confirmation prompt during installation
|
||||
|
||||
TERMSCP_VERSION="0.8.1"
|
||||
TERMSCP_VERSION="0.12.1"
|
||||
GITHUB_URL="https://github.com/veeso/termscp/releases/download/v${TERMSCP_VERSION}"
|
||||
DEB_URL="${GITHUB_URL}/termscp_${TERMSCP_VERSION}_amd64.deb"
|
||||
RPM_URL="${GITHUB_URL}/termscp-${TERMSCP_VERSION}-1.x86_64.rpm"
|
||||
DEB_URL_AMD64="${GITHUB_URL}/termscp_${TERMSCP_VERSION}_amd64.deb"
|
||||
DEB_URL_AARCH64="${GITHUB_URL}/termscp_${TERMSCP_VERSION}_arm64.deb"
|
||||
RPM_URL_AMD64="${GITHUB_URL}/termscp-${TERMSCP_VERSION}-1.x86_64.rpm"
|
||||
RPM_URL_AARCH64="${GITHUB_URL}/termscp-${TERMSCP_VERSION}-1.aarch64.rpm"
|
||||
|
||||
PATH="$PATH:/usr/sbin"
|
||||
|
||||
set -eu
|
||||
printf "\n"
|
||||
@@ -28,6 +32,15 @@ NO_COLOR="$(tput sgr0 2>/dev/null || printf '')"
|
||||
|
||||
# Functions
|
||||
|
||||
set_termscp_version() {
|
||||
TERMSCP_VERSION="$1"
|
||||
GITHUB_URL="https://github.com/veeso/termscp/releases/download/v${TERMSCP_VERSION}"
|
||||
DEB_URL_AMD64="${GITHUB_URL}/termscp_${TERMSCP_VERSION}_amd64.deb"
|
||||
DEB_URL_AARCH64="${GITHUB_URL}/termscp_${TERMSCP_VERSION}_arm64.deb"
|
||||
RPM_URL_AMD64="${GITHUB_URL}/termscp-${TERMSCP_VERSION}-1.x86_64.rpm"
|
||||
RPM_URL_AARCH64="${GITHUB_URL}/termscp-${TERMSCP_VERSION}-1.aarch64.rpm"
|
||||
}
|
||||
|
||||
info() {
|
||||
printf '%s\n' "${BOLD}${GREY}>${NO_COLOR} $*"
|
||||
}
|
||||
@@ -92,16 +105,20 @@ test_writeable() {
|
||||
}
|
||||
|
||||
elevate_priv() {
|
||||
if ! has sudo; then
|
||||
error 'Could not find the command "sudo", needed to install termscp on your system.'
|
||||
info "If you are on Windows, please run your shell as an administrator, then"
|
||||
info "rerun this script. Otherwise, please run this script as root, or install"
|
||||
info "sudo."
|
||||
exit 1
|
||||
fi
|
||||
if ! sudo -v; then
|
||||
if has sudo; then
|
||||
if ! sudo -v; then
|
||||
error "Superuser not granted, aborting installation"
|
||||
exit 1
|
||||
fi
|
||||
sudo="sudo"
|
||||
elif has doas; then
|
||||
sudo="doas"
|
||||
else
|
||||
error 'Could not find the commands "sudo" or "doas", needed to install termscp on your system.'
|
||||
info "If you are on Windows, please run your shell as an administrator, then"
|
||||
info "rerun this script. Otherwise, please run this script as root, or install"
|
||||
info "sudo or doas."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -110,9 +127,7 @@ elevate_priv_ex() {
|
||||
if test_writeable "$check_dir"; then
|
||||
sudo=""
|
||||
else
|
||||
warn "Root permissions are required to install dependecies"
|
||||
elevate_priv
|
||||
sudo="sudo"
|
||||
fi
|
||||
echo $sudo
|
||||
}
|
||||
@@ -153,11 +168,6 @@ detect_arch() {
|
||||
arch="arm"
|
||||
fi
|
||||
|
||||
if [ "${arch}" != "x86_64" ]; then
|
||||
error "Unsupported arch ${arch}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%s' "${arch}"
|
||||
}
|
||||
|
||||
@@ -182,7 +192,7 @@ confirm() {
|
||||
# Installers
|
||||
|
||||
install_on_bsd() {
|
||||
try_with_cargo "packages for freeBSD are distribuited no more. Only cargo installations are supported."
|
||||
try_with_cargo "packages for freeBSD are distribuited no more. Only cargo installations are supported." "freebsd"
|
||||
}
|
||||
|
||||
install_on_arch_linux() {
|
||||
@@ -199,6 +209,18 @@ install_on_arch_linux() {
|
||||
$pkg -S termscp
|
||||
}
|
||||
|
||||
install_with_brew() {
|
||||
info "Installing termscp with brew"
|
||||
if has termscp; then
|
||||
info "Upgrading ${GREEN}termscp${NO_COLOR}…"
|
||||
# The OR is used since someone could have installed via cargo previously
|
||||
brew update && brew upgrade termscp || brew install veeso/termscp/termscp
|
||||
else
|
||||
info "Installing ${GREEN}termscp${NO_COLOR}…"
|
||||
brew install veeso/termscp/termscp
|
||||
fi
|
||||
}
|
||||
|
||||
install_on_linux() {
|
||||
local msg
|
||||
local sudo
|
||||
@@ -216,72 +238,63 @@ install_on_linux() {
|
||||
elif has pikaur; then
|
||||
install_on_arch_linux pikaur
|
||||
elif has dpkg; then
|
||||
if [ "${ARCH}" != "x86_64" ]; then # It's okay on AUR; not on other distros
|
||||
try_with_cargo "we don't distribute packages for ${ARCH} at the moment"
|
||||
case "${ARCH}" in
|
||||
x86_64) DEB_URL="$DEB_URL_AMD64" ;;
|
||||
aarch64) DEB_URL="$DEB_URL_AARCH64" ;;
|
||||
*) try_with_cargo "we don't distribute packages for ${ARCH} at the moment" && return $? ;;
|
||||
esac
|
||||
info "Detected dpkg on your system"
|
||||
info "Installing ${GREEN}termscp${NO_COLOR} via Debian package"
|
||||
archive=$(get_tmpfile "deb")
|
||||
download "${archive}" "${DEB_URL}"
|
||||
info "Downloaded debian package to ${archive}"
|
||||
if test_writeable "/usr/bin"; then
|
||||
sudo=""
|
||||
msg="Installing ${GREEN}termscp${NO_COLOR}, please wait…"
|
||||
else
|
||||
info "Detected dpkg on your system"
|
||||
info "Installing ${GREEN}termscp${NO_COLOR} via Debian package"
|
||||
archive=$(get_tmpfile "deb")
|
||||
download "${archive}" "${DEB_URL}"
|
||||
info "Downloaded debian package to ${archive}"
|
||||
if test_writeable "/usr/bin"; then
|
||||
sudo=""
|
||||
msg="Installing ${GREEN}termscp${NO_COLOR}, please wait…"
|
||||
else
|
||||
warn "Root permissions are required to install ${GREEN}termscp${NO_COLOR}…"
|
||||
elevate_priv
|
||||
sudo="sudo"
|
||||
msg="Installing ${GREEN}termscp${NO_COLOR} as root, please wait…"
|
||||
fi
|
||||
info "$msg"
|
||||
$sudo dpkg -i "${archive}"
|
||||
rm -f ${archive}
|
||||
warn "Root permissions are required to install ${GREEN}termscp${NO_COLOR}…"
|
||||
elevate_priv
|
||||
sudo="sudo"
|
||||
msg="Installing ${GREEN}termscp${NO_COLOR} as root, please wait…"
|
||||
fi
|
||||
info "$msg"
|
||||
$sudo dpkg -i "${archive}"
|
||||
rm -f ${archive}
|
||||
elif has rpm; then
|
||||
if [ "${ARCH}" != "x86_64" ]; then # It's okay on AUR; not on other distros
|
||||
try_with_cargo "we don't distribute packages for ${ARCH} at the moment"
|
||||
case "${ARCH}" in
|
||||
x86_64) RPM_URL="$RPM_URL_AMD64" ;;
|
||||
aarch64) RPM_URL="$RPM_URL_AARCH64" ;;
|
||||
*) try_with_cargo "we don't distribute packages for ${ARCH} at the moment" && return $? ;;
|
||||
esac
|
||||
info "Detected rpm on your system"
|
||||
info "Installing ${GREEN}termscp${NO_COLOR} via RPM package"
|
||||
archive=$(get_tmpfile "rpm")
|
||||
download "${archive}" "${RPM_URL}"
|
||||
info "Downloaded rpm package to ${archive}"
|
||||
if test_writeable "/usr/bin"; then
|
||||
sudo=""
|
||||
msg="Installing ${GREEN}termscp${NO_COLOR}, please wait…"
|
||||
else
|
||||
info "Detected rpm on your system"
|
||||
info "Installing ${GREEN}termscp${NO_COLOR} via RPM package"
|
||||
archive=$(get_tmpfile "rpm")
|
||||
download "${archive}" "${RPM_URL}"
|
||||
info "Downloaded rpm package to ${archive}"
|
||||
if test_writeable "/usr/bin"; then
|
||||
sudo=""
|
||||
msg="Installing ${GREEN}termscp${NO_COLOR}, please wait…"
|
||||
else
|
||||
warn "Root permissions are required to install ${GREEN}termscp${NO_COLOR}…"
|
||||
elevate_priv
|
||||
sudo="sudo"
|
||||
msg="Installing ${GREEN}termscp${NO_COLOR} as root, please wait…"
|
||||
fi
|
||||
info "$msg"
|
||||
$sudo rpm -U "${archive}"
|
||||
rm -f ${archive}
|
||||
warn "Root permissions are required to install ${GREEN}termscp${NO_COLOR}…"
|
||||
elevate_priv
|
||||
sudo="sudo"
|
||||
msg="Installing ${GREEN}termscp${NO_COLOR} as root, please wait…"
|
||||
fi
|
||||
info "$msg"
|
||||
$sudo rpm -U "${archive}"
|
||||
rm -f ${archive}
|
||||
elif has brew; then
|
||||
install_with_brew
|
||||
else
|
||||
try_with_cargo "No suitable installation method found for your Linux distribution; if you're running on Arch linux, please install an AUR package manager (such as yay). Currently only Arch, Debian based and Red Hat based distros are supported"
|
||||
try_with_cargo "No suitable installation method found for your Linux distribution; if you're running on Arch linux, please install an AUR package manager (such as yay). Currently only Arch, Debian based and Red Hat based distros are supported" "linux"
|
||||
fi
|
||||
}
|
||||
|
||||
install_on_macos() {
|
||||
if has brew; then
|
||||
# get homebrew formula name
|
||||
if [ "${ARCH}" != "x86_64" ]; then
|
||||
FORMULA="termscp"
|
||||
else
|
||||
FORMULA="termscp-m1"
|
||||
fi
|
||||
if has termscp; then
|
||||
info "Upgrading ${GREEN}termscp${NO_COLOR}…"
|
||||
# The OR is used since someone could have installed via cargo previously
|
||||
brew update && brew upgrade ${FORMULA} || brew install veeso/termscp/${FORMULA}
|
||||
else
|
||||
info "Installing ${GREEN}termscp${NO_COLOR}…"
|
||||
brew install veeso/termscp/${FORMULA}
|
||||
fi
|
||||
install_with_brew
|
||||
else
|
||||
try_with_cargo "brew is missing on your system; please install it from <https://brew.sh/>"
|
||||
try_with_cargo "brew is missing on your system; please install it from <https://brew.sh/>" "macos"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -291,14 +304,14 @@ install_bsd_cargo_deps() {
|
||||
set -e
|
||||
confirm "${YELLOW}libssh, gcc${NO_COLOR} are required to install ${GREEN}termscp${NO_COLOR}; would you like to proceed?"
|
||||
sudo="$(elevate_priv_ex /usr/local/bin)"
|
||||
$sudo pkg install -y curl wget libssh gcc dbus pkgconf
|
||||
$sudo pkg install -y curl wget libssh gcc dbus pkgconf libsmbclient
|
||||
info "Dependencies installed successfully"
|
||||
}
|
||||
|
||||
install_linux_cargo_deps() {
|
||||
local debian_deps="gcc pkg-config libssl-dev libssh2-1-dev libdbus-1-dev"
|
||||
local rpm_deps="gcc openssl pkgconfig libdbus-devel openssl-devel"
|
||||
local arch_deps="gcc openssl pkg-config dbus"
|
||||
local debian_deps="gcc pkg-config libdbus-1-dev libsmbclient-dev"
|
||||
local rpm_deps="gcc openssl pkgconfig libdbus-devel openssl-devel libsmbclient-devel"
|
||||
local arch_deps="gcc openssl pkg-config dbus smbclient"
|
||||
local deps_cmd=""
|
||||
# Get pkg manager
|
||||
if has apt; then
|
||||
@@ -352,11 +365,12 @@ install_cargo() {
|
||||
|
||||
try_with_cargo() {
|
||||
err="$1"
|
||||
platform="$2"
|
||||
# Install cargo
|
||||
install_cargo
|
||||
if has cargo; then
|
||||
info "Installing ${GREEN}termscp${NO_COLOR} via Cargo…"
|
||||
case $PLATFORM in
|
||||
case $platform in
|
||||
"freebsd")
|
||||
install_bsd_cargo_deps
|
||||
cargo install --locked --no-default-features termscp
|
||||
@@ -398,6 +412,7 @@ fi
|
||||
|
||||
# parse argv variables
|
||||
while [ "$#" -gt 0 ]; do
|
||||
echo $1
|
||||
case "$1" in
|
||||
|
||||
-V | --verbose)
|
||||
@@ -416,7 +431,10 @@ while [ "$#" -gt 0 ]; do
|
||||
FORCE="${1#*=}"
|
||||
shift 1
|
||||
;;
|
||||
|
||||
-v=* | --version=*)
|
||||
set_termscp_version "${1#*=}"
|
||||
shift 1
|
||||
;;
|
||||
*)
|
||||
error "Unknown option: $1"
|
||||
exit 1
|
||||
@@ -458,8 +476,8 @@ case $PLATFORM in
|
||||
esac
|
||||
|
||||
completed "Congratulations! Termscp has successfully been installed on your system!"
|
||||
info "If you're a new user, you might be interested in reading the user manual <https://veeso.github.io/termscp/#user-manual>"
|
||||
info "While if you've just updated your termscp version, you can find the changelog at this link <https://veeso.github.io/termscp/#changelog>"
|
||||
info "If you're a new user, you might be interested in reading the user manual <https://termscp.veeso.dev/#user-manual>"
|
||||
info "While if you've just updated your termscp version, you can find the changelog at this link <https://termscp.veeso.dev/#changelog>"
|
||||
info "Remember that if you encounter any issue, you can report them on Github <https://github.com/veeso/termscp/issues/new>"
|
||||
info "Feel free to open an issue also if you have an idea which could improve the project"
|
||||
info "If you want to support the project, please, consider a little donation <https://ko-fi.com/veeso>"
|
||||
|
||||
2
rustfmt.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
group_imports = "StdExternalCrate"
|
||||
imports_granularity = "Module"
|
||||
BIN
site/assets/images/explorer.gif
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
site/assets/images/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 879 B |
BIN
site/assets/images/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
site/assets/images/favicon-96x96.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
site/assets/images/og_preview.jpg
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
site/assets/images/termscp.webp
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
site/assets/videos/explorer.mp4
Normal file
74
site/changelog.html
Normal file
@@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en_US" class="dark">
|
||||
|
||||
<head>
|
||||
<title>
|
||||
termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3/SMB | 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" />
|
||||
<meta name="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" />
|
||||
<meta property="og:title" content="termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3 | termscp" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta property="og:image" content="https://termscp.veeso.dev/assets/images/og_preview.jpg" />
|
||||
<meta property="og:image:type" content="image/jpg" />
|
||||
<meta property="og:image:width" content="1280" />
|
||||
<meta property="og:image:height" content="640" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="termscp" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content="https://termscp.veeso.dev" />
|
||||
<!-- Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.11.0/devicon.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
|
||||
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
|
||||
crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/fontawesome.min.css"
|
||||
integrity="sha512-OdEXQYCOldjqUEsuMKsZRj93Ht23QRlhIb8E/X0sbwZhme8eUw6g8q7AdxGJKakcBbv7+/PX0Gc2btf7Ru8cZA=="
|
||||
crossorigin="anonymous" />
|
||||
<!-- tailwind -->
|
||||
<link href="output.css" rel="stylesheet" />
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="assets/images/favicon-96x96.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon-16x16.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="layout" class="dark:bg-brand dark:text-gray-100">
|
||||
<!-- Menu -->
|
||||
<header id="menu"></header>
|
||||
<main>
|
||||
<div id="main" class="w-8/12 sm:w-full mx-auto pt-4 pb-8"></div>
|
||||
</main>
|
||||
<footer id="footer"></footer>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<!-- Showdown JS-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
|
||||
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<!-- Local -->
|
||||
<script src="js/lang.min.js"></script>
|
||||
<script src="js/core.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
<script src="js/resolvers.js"></script>
|
||||
<!-- ko-fi -->
|
||||
<script src="https://storage.ko-fi.com/cdn/scripts/overlay-widget.js"></script>
|
||||
<script>
|
||||
kofiWidgetOverlay.draw("veeso", {
|
||||
type: "floating-chat",
|
||||
"floating-chat.donateButton.text": "Support me",
|
||||
"floating-chat.donateButton.background-color": "#31363b",
|
||||
"floating-chat.donateButton.text-color": "#fff",
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
87
site/css/markdown.css
Normal file
@@ -0,0 +1,87 @@
|
||||
.markdown {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.markdown a {
|
||||
color: dodgerblue;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.markdown img {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
.markdown img {
|
||||
display: block;
|
||||
width: 60%;
|
||||
margin-left: 20%;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown blockquote {
|
||||
border-left: 0.25em solid #ccc;
|
||||
font-size: 90%;
|
||||
padding: 0.1em;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
.markdown pre code {
|
||||
background-color: inherit;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.markdown code {
|
||||
background-color: #eee;
|
||||
border-radius: 6px;
|
||||
font-size: 85%;
|
||||
padding: 0.2em 0.4em;
|
||||
}
|
||||
|
||||
:is(.dark) .markdown code {
|
||||
background-color: #404040;
|
||||
}
|
||||
|
||||
.markdown table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
display: block;
|
||||
height: fit-content;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.markdown table tr {
|
||||
border-top: 1px solid #c6cbd1;
|
||||
}
|
||||
|
||||
.markdown table td,
|
||||
.markdown table th {
|
||||
border: 1px solid #c6cbd1;
|
||||
padding: 6px 13px;
|
||||
}
|
||||
75
site/get-started.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en_US" class="dark">
|
||||
|
||||
<head>
|
||||
<title>
|
||||
get started with termscp | termscp
|
||||
</title>
|
||||
<meta property="og:description"
|
||||
content="Install termscp within a couple of minutes with the shell script" />
|
||||
<meta name="description"
|
||||
content="Install termscp within a couple of minutes with the shell script" />
|
||||
<meta property="og:title" content="get started with termscp | termscp" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta property="og:image" content="https://termscp.veeso.dev/assets/images/og_preview.jpg" />
|
||||
<meta property="og:image:type" content="image/jpg" />
|
||||
<meta property="og:image:width" content="1280" />
|
||||
<meta property="og:image:height" content="640" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="termscp" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content="https://termscp.veeso.dev" />
|
||||
<!-- Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.11.0/devicon.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
|
||||
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
|
||||
crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/fontawesome.min.css"
|
||||
integrity="sha512-OdEXQYCOldjqUEsuMKsZRj93Ht23QRlhIb8E/X0sbwZhme8eUw6g8q7AdxGJKakcBbv7+/PX0Gc2btf7Ru8cZA=="
|
||||
crossorigin="anonymous" />
|
||||
<!-- tailwind -->
|
||||
<link href="output.css" rel="stylesheet" />
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="assets/images/favicon-96x96.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon-16x16.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="layout" class="dark:bg-brand dark:text-gray-100">
|
||||
|
||||
<!-- Menu -->
|
||||
<header id="menu"></header>
|
||||
<main>
|
||||
<div id="main" class="w-8/12 sm:w-full mx-auto pt-4 pb-8"></div>
|
||||
</main>
|
||||
<footer id="footer"></footer>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<!-- Showdown JS-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
|
||||
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<!-- Local -->
|
||||
<script src="js/lang.min.js"></script>
|
||||
<script src="js/core.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
<script src="js/resolvers.js"></script>
|
||||
<!-- ko-fi -->
|
||||
<script src="https://storage.ko-fi.com/cdn/scripts/overlay-widget.js"></script>
|
||||
<script>
|
||||
kofiWidgetOverlay.draw("veeso", {
|
||||
type: "floating-chat",
|
||||
"floating-chat.donateButton.text": "Support me",
|
||||
"floating-chat.donateButton.background-color": "#31363b",
|
||||
"floating-chat.donateButton.text-color": "#fff",
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
38
site/html/components/footer.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<div class="flex flex-col bg-brand text-gray-200 justify-around items-center gap-12 p-8 sm:pb-24">
|
||||
<div class="flex flex-row justify-around items-center gap-16 w-3/6 sm:w-full">
|
||||
<div>
|
||||
<a href="https://github.com/veeso/termscp" class="text-3xl sm:text-xl no-underline" target="_blank"><i
|
||||
class="devicon-github-original"></i></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://crates.io/crates/termscp" class="text-3xl sm:text-xl no-underline" target="_blank"><i
|
||||
class="devicon-rust-plain"></i></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://community.chocolatey.org/packages/termscp" class="text-3xl sm:text-xl no-underline"
|
||||
target="_blank"><i class="devicon-windows8-plain"></i></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://ko-fi.com/veeso" target="_blank" class="text-3xl sm:text-xl no-underline"><i
|
||||
class="fa fa-coffee"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-around gap-1 items-center w-full">
|
||||
<!-- vat number -->
|
||||
<p class="text-sm font-thin">
|
||||
<span>P.IVA IT03104140300 </span>
|
||||
</p>
|
||||
<p class="text-xs font-thin">
|
||||
<span>Via Antonio Marangoni 33, 33100, Udine (UD)</span>
|
||||
</p>
|
||||
<!-- Copyright -->
|
||||
<p class="text-xs font-thin">
|
||||
<span>Christian Visintin © </span><span resolve-copyright></span>
|
||||
<span> | </span>
|
||||
<a class="text-xs font-thin" href="https://veeso.dev/en/privacy" target="_blank">Privacy policy</a>
|
||||
<span> | </span>
|
||||
<a class="text-xs font-thin" href="https://veeso.dev/en/cookie-policy" target="_blank">Cookie policy</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
97
site/html/components/menu.html
Normal file
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<div
|
||||
class="flex flex-row fixed bg-gray-100 dark:bg-brand text-brand dark:text-gray-100 border-b border-gray-400 dark:border-gray-800 items-center justify-between w-screen h-auto py-4 px-8">
|
||||
<!-- Desktop menu-->
|
||||
<div class="sm:hidden flex flex-row justify-start items-center gap-12 w-full">
|
||||
<h2 class="lg:text-xl md:text-lg dark:text-gray-100">
|
||||
<a href="/">
|
||||
<img class="w-[32px] h-auto m-auto inline-block mr-2" alt="logo"
|
||||
src="assets/images/termscp.webp" /> termscp
|
||||
</a>
|
||||
</h2>
|
||||
<h2 class="lg:text-xl md:text-lg dark:text-gray-100">
|
||||
<a translate="menu.getStarted" href="/get-started.html"></a>
|
||||
</h2>
|
||||
<h2 class="lg:text-xl md:text-lg dark:text-gray-100">
|
||||
<a translate="menu.updates" href="/updates.html"></a>
|
||||
</h2>
|
||||
<h2 class="lg:text-xl md:text-lg dark:text-gray-100">
|
||||
<a translate="menu.manual" href="/user-manual.html"></a>
|
||||
</h2>
|
||||
<h2 class="lg:text-xl md:text-lg dark:text-gray-100">
|
||||
<a translate="menu.changelog" href="/changelog.html"></a>
|
||||
</h2>
|
||||
<!-- End region -->
|
||||
<div class="flex flex-row self-end justify-end items-center gap-8 flex-1">
|
||||
<!-- Dark/light theme toggle -->
|
||||
<button id="theme-toggle" type="button" onclick="toggleTheme()"
|
||||
class="text-brand dark:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg lg:text-2xl md:text-lg p-2.5">
|
||||
<svg id="theme-toggle-dark-icon" class="w-5 h-5 hidden" fill="currentColor" viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
|
||||
</svg>
|
||||
<svg id="theme-toggle-light-icon" class="w-5 h-5 hidden" fill="currentColor" viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
|
||||
fill-rule="evenodd" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<!-- Socials -->
|
||||
<div>
|
||||
<a href="https://github.com/veeso/termscp" class="lg:text-2xl md:text-lg dark:text-gray-100" target="_blank"><i
|
||||
class="devicon-github-original"></i></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://crates.io/crates/termscp" class="lg:text-2xl md:text-lg dark:text-gray-100" target="_blank"><i
|
||||
class="devicon-rust-plain"></i></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://www.linkedin.com/in/christian-visintin/" class="lg:text-2xl md:text-lg dark:text-gray-100"
|
||||
target="_blank"><i class="devicon-linkedin-plain"></i></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://twitter.com/veeso_dev" target="_blank" class="lg:text-2xl md:text-lg dark:text-gray-100"><i
|
||||
class="devicon-twitter-plain"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Mobile topbar -->
|
||||
<div class="sm:flex hidden flex-row justify-start items-center gap-8 w-full">
|
||||
<h2 class="text-2xl dark:text-gray-100">
|
||||
<a href="/">
|
||||
<img class="w-[32px] h-auto m-auto inline-block mr-2" alt="logo"
|
||||
src="assets/images/termscp.webp" /> termscp
|
||||
</a>
|
||||
</h2>
|
||||
<div id="menu-icon" class="justify-self-end flex-1 flex justify-end">
|
||||
<i onclick="onToggleMenu()" class="fa fa-bars text-2xl cursor-pointer"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Mobile menu -->
|
||||
<div id="mobile-menu"
|
||||
class="hidden transition-all fixed top-0 left-0 h-screen bg-brand text-white w-screen flex-col justify-start items-center p-8">
|
||||
<div class="flex flex-row justify-end items-end w-full">
|
||||
<div id="menu-icon" class="justify-self-end flex-1 flex justify-end">
|
||||
<i onclick="onToggleMenu()" class="fa fa-bars text-2xl cursor-pointer"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-start items-center w-full gap-4">
|
||||
<h2 class="text-2xl text-white">
|
||||
<a translate="menu.intro" href="/"></a>
|
||||
</h2>
|
||||
<h2 class="text-2xl text-white">
|
||||
<a translate="menu.getStarted" href="/get-started.html"></a>
|
||||
</h2>
|
||||
<h2 class="text-2xl text-white">
|
||||
<a translate="menu.updates" href="/updates.html"></a>
|
||||
</h2>
|
||||
<h2 class="text-2xl text-white">
|
||||
<a translate="menu.manual" href="/user-manual.html"></a>
|
||||
</h2>
|
||||
<h2 class="text-2xl text-white">
|
||||
<a translate="menu.changelog" href="/changelog.html"></a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
173
site/html/get-started.html
Normal file
@@ -0,0 +1,173 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<body>
|
||||
<section id="start" class="flex flex-col mx-auto items-center justify-center w-full px-12 gap-8 dark:text-gray-100">
|
||||
<h1 translate="getStarted.title" class="text-3xl font-thin">Get started</h1>
|
||||
<section class="w-full">
|
||||
<h2 class="text-2xl font-thin">
|
||||
<i class="fa fa-rocket"></i> <span translate="getStarted.quickSetup">Quick setup</span>
|
||||
</h2>
|
||||
<div class="installation">
|
||||
<div class="p-4 my-4 text-sm text-blue-800 rounded-lg bg-blue-50">
|
||||
<p class="text-lg">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<span translate="getStarted.suggested">We strongly suggest this method to install termscp</span>
|
||||
</p>
|
||||
</div>
|
||||
<p translate="getStarted.posixUsers">
|
||||
If you are a Linux, a FreeBSD or a MacOS user, you can install termscp
|
||||
via this simple command , which will use a shell script installer:
|
||||
</p>
|
||||
<pre><span class="function">curl</span> --proto <span class="string">'=https'</span> --tlsv1.2 -sSLf <span class="string">"https://git.io/JBhDb"</span> | sh</pre>
|
||||
</div>
|
||||
</section>
|
||||
<section class="w-full">
|
||||
<h2 class="text-2xl font-thin">
|
||||
<i class="devicon-windows8-plain"></i> <span translate="getStarted.windows.title">Windows users</span>
|
||||
</h2>
|
||||
<div class="installation">
|
||||
<p>
|
||||
<span translate="getStarted.windows.intro">You can install termscp on Windows via</span>
|
||||
<a href="https://community.chocolatey.org/packages/termscp" target="_blank">Chocolatey</a>
|
||||
</p>
|
||||
<pre><span class="function">choco</span> install <span class="string">termscp</span></pre>
|
||||
<p>
|
||||
<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.1.nupkg"
|
||||
target="_blank">Github</a>
|
||||
<span translate="getStarted.windows.then">and then, from the ZIP directory, install it via</span>
|
||||
</p>
|
||||
<pre><span class="function">choco</span> install <span class="string">termscp</span> -s .</pre>
|
||||
</div>
|
||||
</section>
|
||||
<section class="w-full">
|
||||
<h2 class="text-2xl font-thin">
|
||||
<i class="devicon-linux-plain"></i> <span translate="getStarted.linuxUsers">Linux users</span>
|
||||
</h2>
|
||||
<div class="sub-system">
|
||||
<div class="p-4 my-4 text-sm text-yellow-800 rounded-lg bg-yellow-50">
|
||||
<p class="text-lg">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span translate="getStarted.notConfident">Opt for these methods instead if you don't feel confident using
|
||||
the shell script</span>
|
||||
</p>
|
||||
</div>
|
||||
<h3>
|
||||
<i class="devicon-linux-plain"></i> <span translate="getStarted.arch.title">Arch derived users</span>
|
||||
</h3>
|
||||
<div class="installation">
|
||||
<p>
|
||||
<span translate="getStarted.arch.intro">On Arch Linux based distros, you can install termscp using an AUR
|
||||
package manager such as</span>
|
||||
<a href="https://github.com/Jguer/yay" target="_blank">yay</a>
|
||||
<span translate="getStarted.arch.then">then run:</span>
|
||||
</p>
|
||||
<pre><span class="function">yay</span> -S <span class="string">termscp</span></pre>
|
||||
</div>
|
||||
<h3>
|
||||
<i class="devicon-debian-plain"></i> <span translate="getStarted.debian.title">Debian derived
|
||||
users</span>
|
||||
</h3>
|
||||
<div class="installation">
|
||||
<p translate="getStarted.debian.body">
|
||||
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.1_amd64.deb</span>
|
||||
sudo <span class="function">dpkg</span> -i <span class="string">termscp.deb</span></pre>
|
||||
</div>
|
||||
<h3>
|
||||
<i class="devicon-redhat-plain"></i> <span translate="getStarted.redhat.title">Redhat derived
|
||||
users</span>
|
||||
</h3>
|
||||
<div class="installation">
|
||||
<p translate="getStarted.redhat.body">
|
||||
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.1-1.x86_64.rpm</span>
|
||||
sudo <span class="function">rpm</span> -U <span class="string">termscp.rpm</span></pre>
|
||||
</div>
|
||||
<h3>
|
||||
<span>Brew</span>
|
||||
</h3>
|
||||
<div class="installation">
|
||||
<p>
|
||||
<span translate="getStarted.macos.install">Install termscp via</span>
|
||||
<a href="https://brew.sh/" target="_blank">Brew</a>
|
||||
</p>
|
||||
<pre><span class="function">brew</span> install <span class="string">veeso/termscp/termscp</span></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="w-full">
|
||||
<h2 class="text-2xl font-thin">
|
||||
<i class="devicon-apple-plain"></i> <span translate="getStarted.macos.title">MacOS users</span>
|
||||
</h2>
|
||||
<div class="installation">
|
||||
<div class="p-4 my-4 text-sm text-yellow-800 rounded-lg bg-yellow-50">
|
||||
<p class="text-lg">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span translate="getStarted.notConfident">Opt for these methods instead if you don't feel confident using
|
||||
the shell script</span>
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
<span translate="getStarted.macos.install">Install termscp via</span>
|
||||
<a href="https://brew.sh/" target="_blank">Brew</a>
|
||||
</p>
|
||||
<pre><span class="function">brew</span> install <span class="string">veeso/termscp/termscp</span></pre>
|
||||
</div>
|
||||
</section>
|
||||
<section class="w-full">
|
||||
<h2 class="text-2xl font-thin">
|
||||
<i class="devicon-rust-plain"></i> <span translate="getStarted.cargo.title">Install with Cargo</span>
|
||||
</h2>
|
||||
<div class="installation">
|
||||
<div class="p-4 my-4 text-sm text-yellow-800 rounded-lg bg-yellow-50">
|
||||
<p class="text-lg">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span translate="getStarted.noBinary">Opt for this method instead if binaries for your platform are not
|
||||
available</span>
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
<span translate="getStarted.cargo.body">If a package is not available for your system, you can opt to
|
||||
install termscp via</span>
|
||||
<a href="https://www.rust-lang.org/tools/install" target="_blank">Cargo</a>.
|
||||
<span translate="getStarted.cargo.requirements">To install termscp via Cargo, these requirements must be
|
||||
satisfied:</span>
|
||||
</p>
|
||||
<ul class="list-disc px-8">
|
||||
<li>
|
||||
Linux:
|
||||
<ul class="list-disc px-12">
|
||||
<li>pkg-config</li>
|
||||
<li>libdbus-1</li>
|
||||
<li>libsmbclient</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
FreeBSD:
|
||||
<ul class="list-disc px-12">
|
||||
<li>dbus</li>
|
||||
<li>pkg-conf</li>
|
||||
<li>gcc</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p translate="getStarted.cargo.install">Then you can install it via</p>
|
||||
<pre><span class="function">cargo</span> install --locked <span class="string">termscp</span></pre>
|
||||
<p translate="getStarted.cargo.noKeyring" class="pt-4 pb-2">
|
||||
Or if you don't want to have support for keyring or you're building on
|
||||
*BSD:
|
||||
</p>
|
||||
<pre><span class="function">cargo</span> install --locked --no-default-features --features smb <span class="string">termscp</span></pre>
|
||||
<p translate="getStarted.cargo.noSMB" class="pt-4 pb-2"></p>
|
||||
<pre><span class="function">cargo</span> install --locked --no-default-features --features with-keyring <span class="string">termscp</span></pre>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</body>
|
||||
83
site/html/home.html
Normal file
@@ -0,0 +1,83 @@
|
||||
<!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">
|
||||
<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
|
||||
</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.1 is NOW out! Download it from</span>
|
||||
<a href="/get-started.html" translate="intro.here">here!</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 sm:grid-cols-1 gap-8 py-8 w-4/6 sm:w-full">
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.handy.title" class="text-center text-lg">Handy UI</h3>
|
||||
<p translate="intro.features.handy.body" class="text-center">
|
||||
Explore and operate on the remote and on the local machine file system
|
||||
with a handy UI.
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.crossPlatform.title" class="text-center text-lg">Cross platform</h3>
|
||||
<p translate="intro.features.crossPlatform.body" class="text-center">
|
||||
Runs on Windows, MacOS, Linux and BSD
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.customizable.title" class="text-center text-lg">Customizable</h3>
|
||||
<p translate="intro.features.customizable.body" class="text-center">
|
||||
Customize the file explorer, the text editor to use and default
|
||||
options
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.bookmarks.title" class="text-center text-lg">Bookmarks</h3>
|
||||
<p translate="intro.features.bookmarks.body" class="text-center">
|
||||
Connect to your favourite hosts through built-in bookmarks and recent
|
||||
connections support
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.security.title" class="text-center text-lg">Security first</h3>
|
||||
<p translate="intro.features.security.body" class="text-center">
|
||||
Save your password into your operating system key vault
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.performance.title" class="text-center text-lg">Eye on performance</h3>
|
||||
<p translate="intro.features.performance.body" class="text-center">
|
||||
termscp has been developed keeping an eye on performance to prevent
|
||||
cpu usage
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-4/6 sm:w-5/6">
|
||||
<video autoplay muted loop>
|
||||
<source src="assets/videos/explorer.mp4" type="video/mp4" resolve-video-fallback="assets/images/explorer.gif" />
|
||||
</video>
|
||||
</div>
|
||||
<hr class="h-px my-8 bg-gray-800 border-0" />
|
||||
<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>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="hook">
|
||||
<h3 class="text-center text-gray-500 dark:text-gray-100 font-light text-xl">
|
||||
<a href="/user-manual.html" class="no-underline hover:underline" translate="intro.footer.manual">User manual</a>
|
||||
</h3>
|
||||
</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>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
97
site/html/updates.html
Normal file
@@ -0,0 +1,97 @@
|
||||
<head>
|
||||
<link rel="stylesheet" href="css/updates.css" />
|
||||
</head>
|
||||
<body>
|
||||
<section id="updates" class="flex flex-col mx-auto items-center justify-center w-full px-12 gap-8 dark:text-gray-100">
|
||||
<h1 translate="updates.title" class="text-3xl font-thin">Keeping termscp up to date</h1>
|
||||
<!-- Reasons -->
|
||||
<section>
|
||||
<h2 class="text-2xl font-thin">
|
||||
<i class="fa fa-question-circle"></i> <span translate="updates.reasons.title">Why should you install
|
||||
updates</span>
|
||||
</h2>
|
||||
<div class="wall-of-text">
|
||||
<p translate="updates.reasons.wallOfText" class="text-gray-700 dark:text-gray-300">
|
||||
Termscp is an application that is still in its early stage of
|
||||
development, the first version has been released in december in 2020
|
||||
and practically there's only one
|
||||
<a href="https://termscp.veeso.dev/" target="_blank">guy</a> working on
|
||||
it and there's still a lot of work to do in order to improve it and
|
||||
make it fast and reliable. Along to this, you should also consider
|
||||
that since it's an application which works with network protocols and
|
||||
is intended to manipulate secrets and credentials there may always be
|
||||
a security issue. I can't guarantee there's no security issues in the
|
||||
versions I've released in these months, and if there are they might
|
||||
not be even my fault, but they might be contained in the libraries
|
||||
termscp relies on. Because of this, it's always VERY important to keep
|
||||
termscp up to date. To prove how much I care about it, just consider
|
||||
that I've implemented something that many other open source
|
||||
applications won't do: the update check. Whenever you start termscp
|
||||
(unless if deactivated in configuration) termscp will always check if
|
||||
there's a new version available and will notify you immediately. In
|
||||
addition to security concerns, each major update will bring many
|
||||
awesome features 🦄 you can't miss and the application is getting more
|
||||
reliable and stable after each update 😄
|
||||
</p>
|
||||
<p class="italic text-xl text-gray-700 dark:text-gray-300">
|
||||
<span>TL;DR</span>
|
||||
<span translate="updates.reasons.tldr"></span>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<!-- Gui method -->
|
||||
<section>
|
||||
<h2 class="text-2xl font-thin"><i class="fa fa-desktop"></i> <span translate="updates.gui.title">GUI method</span></h2>
|
||||
<div class="installation">
|
||||
<p translate="updates.gui.body" class="text-gray-700 dark:text-gray-300">
|
||||
The GUI method just consists in starting termscp with no options, you
|
||||
then should be in front of the authentication form. If there's an
|
||||
update available a message like "termscp x.y.z is OUT! Update and read
|
||||
release notes with CTRL+R". All you have to do at this point to update
|
||||
termscp, is:
|
||||
</p>
|
||||
<ol class="list-decimal px-8 text-gray-700 dark:text-gray-300">
|
||||
<li translate="updates.gui.steps.st">press CTRL+R. The release notes should now be displayed.</li>
|
||||
<li translate="updates.gui.steps.nd">Select "YES" in the "Install update?" radio input</li>
|
||||
<li translate="updates.gui.steps.rd">Press "ENTER"</li>
|
||||
</ol>
|
||||
<p translate="updates.gui.then" class="text-gray-700 dark:text-gray-300">
|
||||
If everything worked correctly a green message "termscp x.y.z has been
|
||||
installed!" will be displayed. Just restart termscp and enjoy the
|
||||
update 😄
|
||||
</p>
|
||||
<div class="p-4 my-4 text-sm text-yellow-800 rounded-lg bg-yellow-50">
|
||||
<p>
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span translate="updates.gui.pex">
|
||||
If you have previously installed termscp via Deb/RPM package, you
|
||||
may need to use the CLI method running termscp with sudo
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- CLI method -->
|
||||
<section>
|
||||
<h2 class="text-2xl font-thin"><i class="fa fa-glasses"></i> <span translate="updates.cli.title">CLI method</span></h2>
|
||||
<div class="installation">
|
||||
<p translate="updates.cli.body" class="text-gray-700 dark:text-gray-300">
|
||||
If you prefer, you can install a new update just using the dedicated
|
||||
CLI option:
|
||||
</p>
|
||||
<pre><span class="function">termscp</span> --update</pre>
|
||||
<div class="p-4 my-4 text-sm text-yellow-800 rounded-lg bg-yellow-50">
|
||||
<p>
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span translate="updates.cli.pex">Run with sudo if necessary (Debian/FreeBSD/RedHat users)</span>
|
||||
</p>
|
||||
</div>
|
||||
<p translate="updates.cli.then" class="text-gray-700 dark:text-gray-300">
|
||||
Once started, you will be prompted whether to install or not the
|
||||
update. Confirm the installation and ta-dah, the new version of
|
||||
termscp should now be available on your machine
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</body>
|
||||
75
site/index.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en_US" class="dark">
|
||||
|
||||
<head>
|
||||
<title>
|
||||
termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3/SMB | 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." />
|
||||
<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" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta property="og:image" content="https://termscp.veeso.dev/assets/images/og_preview.jpg" />
|
||||
<meta property="og:image:type" content="image/jpg" />
|
||||
<meta property="og:image:width" content="1280" />
|
||||
<meta property="og:image:height" content="640" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="termscp" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content="https://termscp.veeso.dev" />
|
||||
<!-- Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.11.0/devicon.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
|
||||
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
|
||||
crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/fontawesome.min.css"
|
||||
integrity="sha512-OdEXQYCOldjqUEsuMKsZRj93Ht23QRlhIb8E/X0sbwZhme8eUw6g8q7AdxGJKakcBbv7+/PX0Gc2btf7Ru8cZA=="
|
||||
crossorigin="anonymous" />
|
||||
<!-- tailwind -->
|
||||
<link href="output.css" rel="stylesheet" />
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="assets/images/favicon-96x96.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon-16x16.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="layout" class="dark:bg-brand dark:text-gray-100">
|
||||
|
||||
<!-- Menu -->
|
||||
<header id="menu"></header>
|
||||
<main>
|
||||
<div id="main" class="w-8/12 sm:w-full mx-auto pt-4 pb-8"></div>
|
||||
</main>
|
||||
<footer id="footer"></footer>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<!-- Showdown JS-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
|
||||
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<!-- Local -->
|
||||
<script src="js/lang.min.js"></script>
|
||||
<script src="js/core.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
<script src="js/resolvers.js"></script>
|
||||
<!-- ko-fi -->
|
||||
<script src="https://storage.ko-fi.com/cdn/scripts/overlay-widget.js"></script>
|
||||
<script>
|
||||
kofiWidgetOverlay.draw("veeso", {
|
||||
type: "floating-chat",
|
||||
"floating-chat.donateButton.text": "Support me",
|
||||
"floating-chat.donateButton.background-color": "#31363b",
|
||||
"floating-chat.donateButton.text-color": "#fff",
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
132
site/input.css
Normal file
@@ -0,0 +1,132 @@
|
||||
@import "css/markdown.css";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: "Sora";
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/sora/v11/xMQ9uFFYT72X5wkB_18qmnndmSdSnx2BAfO5mnuyOo1l_iMwWa-xsaQ.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
|
||||
U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: "Sora";
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/sora/v11/xMQ9uFFYT72X5wkB_18qmnndmSdSnx2BAfO5mnuyOo1l_iMwV6-x.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||
U+FEFF, U+FFFD;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: "Sora";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/sora/v11/xMQ9uFFYT72X5wkB_18qmnndmSdSnx2BAfO5mnuyOo1l_iMwWa-xsaQ.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
|
||||
U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: "Sora";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/sora/v11/xMQ9uFFYT72X5wkB_18qmnndmSdSnx2BAfO5mnuyOo1l_iMwV6-x.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||
U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Sora", sans-serif;
|
||||
margin: 0;
|
||||
min-width: 100vw;
|
||||
overflow-x: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
padding-top: 5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
input {
|
||||
font-family: "Sora", sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: "Sora", sans-serif;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
font-size: 85%;
|
||||
line-height: 1.45;
|
||||
color: #d0d0d0;
|
||||
background-color: #222629;
|
||||
border-radius: 3px;
|
||||
word-wrap: normal;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
pre .function {
|
||||
color: #f08d49;
|
||||
}
|
||||
|
||||
pre .string {
|
||||
color: #7ec699;
|
||||
}
|
||||
131
site/js/core.js
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* @description return navigator language. If language is not supported default will be returned
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
function getNavigatorLanguage() {
|
||||
let lang = navigator.language;
|
||||
// Complete lang
|
||||
if (languageSupported(lang)) {
|
||||
return lang;
|
||||
}
|
||||
// Reduced lang
|
||||
lang = lang.split(/[-_]/)[0] || "en";
|
||||
if (!languageSupported(lang)) {
|
||||
return "en";
|
||||
}
|
||||
return lang;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description check whether provided language is supported by the website
|
||||
* @param {string} lang
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function languageSupported(lang) {
|
||||
return ["en", "zh-CN", "it", "fr", "es"].includes(lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description update website language
|
||||
* @param {string} lang
|
||||
*/
|
||||
function setSiteLanguage(lang) {
|
||||
setLanguage(lang);
|
||||
}
|
||||
|
||||
const converter = new showdown.Converter({ tables: true });
|
||||
|
||||
/**
|
||||
* @description load page associated to hash
|
||||
* @param {string} hash
|
||||
*/
|
||||
function loadPage(path) {
|
||||
switch (path) {
|
||||
case "/":
|
||||
case "/index.html":
|
||||
loadHtml("home.html");
|
||||
break;
|
||||
case "/get-started.html":
|
||||
loadHtml("get-started.html");
|
||||
break;
|
||||
case "/user-manual.html":
|
||||
loadUserManual();
|
||||
break;
|
||||
case "/updates.html":
|
||||
loadHtml("updates.html");
|
||||
break;
|
||||
case "/changelog.html":
|
||||
loadMarkdown(
|
||||
"https://raw.githubusercontent.com/veeso/termscp/main/CHANGELOG.md"
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function loadHtml(page) {
|
||||
const url = "html/" + page;
|
||||
$("#main").load(url, function () {
|
||||
onPageLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
function loadMenu() {
|
||||
$("#menu").load("html/components/menu.html", function () {
|
||||
onPageLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
function loadFooter() {
|
||||
$("#footer").load("html/components/footer.html", function () {
|
||||
onPageLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
function loadMarkdown(page) {
|
||||
getMarkdown(page, function (md) {
|
||||
const div = jQuery("<div/>", {
|
||||
id: page,
|
||||
class: "container markdown",
|
||||
});
|
||||
div.html(converter.makeHtml(md));
|
||||
$("#main").empty();
|
||||
$("#main").append(div);
|
||||
onPageLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description get markdown and pass result to onLoaded
|
||||
* @param {string} url
|
||||
* @param {function} onLoaded
|
||||
*/
|
||||
function getMarkdown(url, onLoaded) {
|
||||
$.ajax({
|
||||
url,
|
||||
type: "GET",
|
||||
dataType: "text",
|
||||
success: onLoaded,
|
||||
});
|
||||
}
|
||||
|
||||
function loadUserManual() {
|
||||
// Load language
|
||||
const lang = getNavigatorLanguage();
|
||||
if (lang === "en") {
|
||||
loadMarkdown(
|
||||
`https://raw.githubusercontent.com/veeso/termscp/main/docs/man.md`
|
||||
);
|
||||
} else {
|
||||
loadMarkdown(
|
||||
`https://raw.githubusercontent.com/veeso/termscp/main/docs/${lang}/man.md`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// startup
|
||||
$(function () {
|
||||
loadPage(window.location.pathname);
|
||||
loadMenu();
|
||||
loadFooter();
|
||||
});
|
||||
81
site/js/events.js
Normal file
@@ -0,0 +1,81 @@
|
||||
function onPageLoaded() {
|
||||
reloadTranslations();
|
||||
setThemeToggle();
|
||||
setTheme(getTheme());
|
||||
}
|
||||
|
||||
function onToggleMenu() {
|
||||
const mobileMenu = $("#mobile-menu");
|
||||
let wasVisible = false;
|
||||
// if not visible set flex and slide in, otherwise slide out
|
||||
if (!mobileMenu.is(":visible")) {
|
||||
mobileMenu.css("display", "flex");
|
||||
mobileMenu.addClass("animate__animated animate__slideInLeft");
|
||||
} else {
|
||||
mobileMenu.addClass("animate__animated animate__slideOutLeft");
|
||||
wasVisible = true;
|
||||
}
|
||||
|
||||
// on animation end remove animation, if visible set hidden
|
||||
mobileMenu.on("animationend", () => {
|
||||
mobileMenu.removeClass(
|
||||
"animate__animated animate__slideOutLeft animate__slideInLeft"
|
||||
);
|
||||
if (wasVisible) {
|
||||
mobileMenu.css("display", "none");
|
||||
}
|
||||
mobileMenu.off("animationend");
|
||||
});
|
||||
}
|
||||
|
||||
function getTheme() {
|
||||
const theme = localStorage.getItem("theme");
|
||||
|
||||
if (!theme) {
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "theme-dark"
|
||||
: "theme-light";
|
||||
}
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
||||
function setThemeToggle() {
|
||||
if (getTheme() === "theme-dark") {
|
||||
$("#theme-toggle-dark-icon").css("display", "block");
|
||||
$("#theme-toggle-light-icon").css("display", "none");
|
||||
} else {
|
||||
$("#theme-toggle-dark-icon").css("display", "none");
|
||||
$("#theme-toggle-light-icon").css("display", "block");
|
||||
}
|
||||
}
|
||||
|
||||
// function to set a given theme/color-scheme
|
||||
function setTheme(themeName) {
|
||||
localStorage.setItem("theme", themeName);
|
||||
if (themeName === "theme-dark") {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
setThemeToggle();
|
||||
}
|
||||
|
||||
// function to toggle between light and dark theme
|
||||
function toggleTheme() {
|
||||
console.log("theme", getTheme());
|
||||
if (getTheme() === "theme-dark") {
|
||||
setTheme("theme-light");
|
||||
} else {
|
||||
setTheme("theme-dark");
|
||||
}
|
||||
}
|
||||
|
||||
// Startup
|
||||
$(function () {
|
||||
// Init language
|
||||
setSiteLanguage(getNavigatorLanguage());
|
||||
|
||||
// init theme
|
||||
setTheme(getTheme());
|
||||
});
|
||||
1
site/js/lang.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
var currentLanguage=null;var languagePath="lang/";var currentLanguageDict=null;function setLanguage(lang){currentLanguage=lang;const jsonFile=languagePath+currentLanguage+".json";$.getJSON(jsonFile,function(langData){currentLanguageDict=flatDict(langData);reloadTranslations()})}function reloadTranslations(){$("[translate]").each(function(){const translationAttr=$(this).attr("translate");$(this).text(getInstantTranslation(translationAttr))})}function getInstantTranslation(key){if(currentLanguageDict!==null&&key in currentLanguageDict){return currentLanguageDict[key]}else{return"{{ "+key+" }}"}}function flatDict(dict){const iterNode=(flatten,path,node)=>{for(const key of Object.keys(node)){const child=node[key];const childKey=path?path+"."+key:key;if(typeof child==="object"){flatten=iterNode(flatten,childKey,child)}else{flatten[childKey]=child}}return flatten};return iterNode({},null,dict)}
|
||||
32
site/js/resolvers.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @description resolve copyright year
|
||||
*/
|
||||
|
||||
function resolveCopyright() {
|
||||
const year = new Date().getFullYear();
|
||||
$("[resolve-copyright]").each(function () {
|
||||
$(this).text(year);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description resolve video fallback source in case fails. Uses an image instead
|
||||
*/
|
||||
function resolveVideoFallback() {
|
||||
$("[resolve-video-fallback]").each(function () {
|
||||
const fallback = $(this).attr("resolve-video-fallback");
|
||||
// Add listener
|
||||
$(this).on("error", function () {
|
||||
const image = document.createElement("img");
|
||||
image.src = fallback;
|
||||
image.classList = ["preview"];
|
||||
$(this).parent().replaceWith(image);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// init
|
||||
$(function () {
|
||||
resolveCopyright();
|
||||
resolveVideoFallback();
|
||||
});
|
||||
115
site/lang/en.json
Normal file
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"menu": {
|
||||
"desc": "A feature rich terminal UI file transfer",
|
||||
"intro": "Intro",
|
||||
"getStarted": "Get started",
|
||||
"updates": "Install updates",
|
||||
"manual": "User manual",
|
||||
"changelog": "Release history",
|
||||
"author": "About the author",
|
||||
"support": "Support me"
|
||||
},
|
||||
"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.1 is NOW out! Download it from",
|
||||
"here": "here",
|
||||
"features": {
|
||||
"handy": {
|
||||
"title": "Handy UI",
|
||||
"body": "Explore and operate on the remote and on the local machine file system with a handy UI."
|
||||
},
|
||||
"crossPlatform": {
|
||||
"title": "Cross platform",
|
||||
"body": "Runs on Windows, MacOS, Linux and FreeBSD"
|
||||
},
|
||||
"customizable": {
|
||||
"title": "Customizable",
|
||||
"body": "Customize the file explorer, the UI and many other parameters..."
|
||||
},
|
||||
"bookmarks": {
|
||||
"title": "Bookmarks",
|
||||
"body": "Connect to your favourite hosts through built-in bookmarks and recent connections"
|
||||
},
|
||||
"security": {
|
||||
"title": "Security first",
|
||||
"body": "Save your passwords into your operating system key vault"
|
||||
},
|
||||
"performance": {
|
||||
"title": "Eye on performance",
|
||||
"body": "termscp has been developed keeping an eye on performance to prevent high cpu usage"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"getStarted": "Get started",
|
||||
"manual": "User manual",
|
||||
"updates": "Install updates"
|
||||
}
|
||||
},
|
||||
"getStarted": {
|
||||
"title": "Get started",
|
||||
"quickSetup": "Quick setup",
|
||||
"suggested": "We strongly suggest this method to install termscp",
|
||||
"posixUsers": "If you are a Linux, a FreeBSD or a MacOS user, you can install termscp via this simple command , which will use a shell script installer:",
|
||||
"windows": {
|
||||
"title": "Windows users",
|
||||
"intro": "You can install termscp on Windows via",
|
||||
"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",
|
||||
"then": "and then, from the ZIP directory, install it via"
|
||||
},
|
||||
"linuxUsers": "Linux users",
|
||||
"notConfident": "Opt for these methods instead if you don't feel confident using the shell script",
|
||||
"noBinary": "Opt for this method instead if binaries for your platform are not available or you want to select features",
|
||||
"arch": {
|
||||
"title": "Arch derived users",
|
||||
"intro": "On Arch Linux based distros, you can install termscp using an AUR package manager such as",
|
||||
"then": "then run"
|
||||
},
|
||||
"debian": {
|
||||
"title": "Debian derived users",
|
||||
"body": "On Debian based distros, you can install termscp using the Deb package via:"
|
||||
},
|
||||
"redhat": {
|
||||
"title": "Redhat derived users",
|
||||
"body": "On RedHat based distros, you can install termscp using the RPM package via:"
|
||||
},
|
||||
"macos": {
|
||||
"title": "MacOS users",
|
||||
"install": "Install termscp via"
|
||||
},
|
||||
"cargo": {
|
||||
"title": "Install with Cargo",
|
||||
"body": "If a package is not available for your system, you can opt to install termscp via",
|
||||
"requirements": "To install termscp via Cargo, these requirements must be satisfied",
|
||||
"install": "Then you can install termscp via",
|
||||
"noKeyring": "Or if you don't want to have support for keyring or you're building on *BSD:",
|
||||
"noSMB": "Or if you want to disable SMB:"
|
||||
}
|
||||
},
|
||||
"updates": {
|
||||
"title": "Keeping termscp up to date",
|
||||
"disclaimer": " Updating termscp with this method is only available for 0.7.x versions or higher. If you have an older version, you have to install updates using the",
|
||||
"reasons": {
|
||||
"title": "Why you should install updates",
|
||||
"wallOfText": "Termscp is an application that is still in its early stage of development, the first version has been released in december in 2020 and practically there's only one guy working on it and there's still a lot of work to do in order to improve it and make it fast and reliable. Along to this, you should also consider that since it's an application which works with network protocols and is intended to manipulate secrets and credentials there may always be a security issue. I can't guarantee there's no security issues in the versions I've released in these months, and if there are they might not be even my fault, but they might be contained in the libraries termscp relies on. Because of this, it's always VERY important to keep termscp up to date. To prove how much I care about it, just consider that I've implemented something that many other open source applications won't do: the update check. Whenever you start termscp (unless if deactivated in configuration) termscp will always check if there's a new version available and will notify you immediately. In addition to security concerns, each major update will bring many awesome features 🦄 you can't miss and the application is getting more reliable and stable after each update 😄",
|
||||
"tldr": "Possible security issues; new awesome features; performance and stability; Bugfixes"
|
||||
},
|
||||
"gui": {
|
||||
"title": "GUI method",
|
||||
"body": "The GUI method just consists in starting termscp with no options, you then should be in front of the authentication form. If there's an update available a message like \"termscp x.y.z is OUT! Update and read release notes with CTRL+R\". All you have to do at this point to update termscp, is:",
|
||||
"steps": {
|
||||
"st": "press CTRL+R. The release notes should now be displayed",
|
||||
"nd": "Select \"YES\" in the \"Install update?\" radio input",
|
||||
"rd": "Press \"Enter\""
|
||||
},
|
||||
"then": "If everything worked correctly a green message \"termscp x.y.z has been installed!\" will be displayed. Just restart termscp and enjoy the update 😄",
|
||||
"pex": " If you have previously installed termscp via Deb/RPM package, you may need to use the CLI method running termscp with sudo"
|
||||
},
|
||||
"cli": {
|
||||
"title": "CLI method",
|
||||
"body": "If you prefer, you can install a new update just using the dedicated CLI option:",
|
||||
"pex": "Run with sudo if necessary (installed with RPM/DEB)",
|
||||
"then": "Once started, you will be prompted whether to install or not the update. Confirm the installation and ta-dah, the new version of termscp should now be available on your machine"
|
||||
}
|
||||
}
|
||||
}
|
||||
115
site/lang/es.json
Normal file
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"menu": {
|
||||
"desc": "Una transferencia de archivos de terminal rica en funciones",
|
||||
"intro": "Sobre termscp",
|
||||
"getStarted": "Para iniciar",
|
||||
"updates": "Actualizaciones",
|
||||
"manual": "Manual de usuario",
|
||||
"changelog": "Historial de versiones",
|
||||
"author": "Sobre el autor",
|
||||
"support": "Apoyame"
|
||||
},
|
||||
"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.1 ya está disponible! Descárgalo desde",
|
||||
"here": "aquì",
|
||||
"features": {
|
||||
"handy": {
|
||||
"title": "Interfaz de usuario práctica",
|
||||
"body": "Explore y opere en el sistema de archivos de la máquina local y remota con una interfaz de usuario práctica."
|
||||
},
|
||||
"crossPlatform": {
|
||||
"title": "Multiplataforma",
|
||||
"body": "Funciona en Windows, MacOS, Linux y FreeBSD"
|
||||
},
|
||||
"customizable": {
|
||||
"title": "Personalizable",
|
||||
"body": "Personalice el explorador de archivos, la interfaz de usuario y muchos otros parámetros ..."
|
||||
},
|
||||
"bookmarks": {
|
||||
"title": "Marcadores",
|
||||
"body": "Conéctese a sus hosts favoritos a través de marcadores integrados y conexiones recientes"
|
||||
},
|
||||
"security": {
|
||||
"title": "Seguridad primero",
|
||||
"body": "Guarde sus contraseñas en la bóveda de claves de su sistema operativo"
|
||||
},
|
||||
"performance": {
|
||||
"title": "Ojo en el rendimiento",
|
||||
"body": "termscp se ha desarrollado teniendo en cuenta el rendimiento para evitar un uso elevado de la CPU"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"getStarted": "Para iniciar",
|
||||
"manual": "Manual de usuario",
|
||||
"updates": "Actualizaciones"
|
||||
}
|
||||
},
|
||||
"getStarted": {
|
||||
"title": "Para iniciar",
|
||||
"quickSetup": "Configuración rápida",
|
||||
"suggested": "Recomendamos encarecidamente este método para instalar termscp",
|
||||
"posixUsers": "Si es un usuario de Linux, FreeBSD o MacOS, puede instalar termscp a través de este sencillo comando, que utilizará un instalador de scripts de shell:",
|
||||
"windows": {
|
||||
"title": "Usuarios Windows",
|
||||
"intro": "Puede instalar termscp en Windows a través de",
|
||||
"moderation": "Tenga en cuenta que la moderación de Chocolatey puede tardar hasta algunas semanas desde la última versión, por lo que si la última versión aún no está disponible, puede instalarla descargando el archivo ZIP de",
|
||||
"then": "y luego, desde el directorio ZIP, instálelo a través de"
|
||||
},
|
||||
"linuxUsers": "Usuarios Linux",
|
||||
"notConfident": "En su lugar, opte por estos métodos si no se siente seguro al usar el script de shell",
|
||||
"noBinary": "Opte por este método en su lugar si los binarios para su plataforma no están disponibles",
|
||||
"arch": {
|
||||
"title": "Usuarios derivadas Arch",
|
||||
"intro": "En las distribuciones basadas en Arch Linux, puede instalar termscp usando un administrador de paquetes AUR como",
|
||||
"then": "then run"
|
||||
},
|
||||
"debian": {
|
||||
"title": "Usuarios derivadas Debian",
|
||||
"body": "En las distribuciones basadas en Debian, puede instalar termscp usando el paquete Deb a través de:"
|
||||
},
|
||||
"redhat": {
|
||||
"title": "Usuarios derivadas RedHat",
|
||||
"body": "En las distribuciones basadas en RedHat, puede instalar termscp usando el paquete RPM a través de:"
|
||||
},
|
||||
"macos": {
|
||||
"title": "Usuarios MacOS",
|
||||
"install": "Instalar termscp a través de"
|
||||
},
|
||||
"cargo": {
|
||||
"title": "Instalar con Cargo",
|
||||
"body": "Si un paquete no está disponible para su sistema, puede optar por instalar termscp a través de",
|
||||
"requirements": "Para instalar termscp a través de Cargo, se deben cumplir estos requisitos",
|
||||
"install": "Entonces puedes instalar termscp a través de",
|
||||
"noKeyring": "O si no desea tener soporte para llavero o está construyendo sobre *BSD:",
|
||||
"noSMB": "O si no desea tener soporte para SMB:"
|
||||
}
|
||||
},
|
||||
"updates": {
|
||||
"title": "Mantener termscp actualizado",
|
||||
"disclaimer": " La actualización de termscp con este método solo está disponible para las versiones 0.7.xo superiores. Si tiene una versión anterior, debe instalar actualizaciones utilizando el",
|
||||
"reasons": {
|
||||
"title": "Why you should install updates",
|
||||
"wallOfText": "Termscp es una aplicación que aún se encuentra en su etapa inicial de desarrollo, la primera versión se lanzó en diciembre de 2020 y prácticamente solo hay un chico trabajando en ella y aún hay mucho trabajo por hacer para mejorarla y hacerla. rápido y confiable. Junto con esto, también debe considerar que, dado que es una aplicación que funciona con protocolos de red y está destinada a manipular secretos y credenciales, siempre puede haber un problema de seguridad. No puedo garantizar que no haya problemas de seguridad en las versiones que lancé en estos meses, y si los hay, puede que ni siquiera sea mi culpa, pero pueden estar contenidos en las bibliotecas en las que se basa termscp. Debido a esto, siempre es MUY importante mantener el termcp actualizado. Para demostrar cuánto me importa, solo considere que he implementado algo que muchas otras aplicaciones de código abierto no harán: la verificación de actualizaciones. Siempre que inicie termscp (a menos que esté desactivado en la configuración), termscp siempre comprobará si hay una nueva versión disponible y le notificará inmediatamente. Además de las preocupaciones de seguridad, cada actualización importante traerá muchas características increíbles 🦄 no puede perderse y la aplicación se vuelve más confiable y estable después de cada actualización 😄",
|
||||
"tldr": "Posibles problemas de seguridad; nuevas funciones asombrosas; rendimiento y estabilidad; Corrección de errores"
|
||||
},
|
||||
"gui": {
|
||||
"title": "Método GUI",
|
||||
"body": "El método GUI solo consiste en iniciar termscp sin opciones, luego debe estar frente al formulario de autenticación. Si hay una actualización disponible, aparecerá un mensaje como \"termscp x.y.z is OUT! Install update and read release notes with <CTRL + R>\". Todo lo que tiene que hacer en este punto para actualizar termscp es:",
|
||||
"steps": {
|
||||
"st": "presione CTRL + R. Las notas de la versión deberían mostrarse ahora",
|
||||
"nd": "Seleccione \"Yes\" en la entrada de radio \"Install update? \"",
|
||||
"rd": "presione \"Enter\""
|
||||
},
|
||||
"then": "If everything worked correctly a green message \"termscp x.y.z has been installed!\" will be displayed. Just restart termscp and enjoy the update 😄",
|
||||
"pex": " Si ha instalado previamente termscp a través del paquete Deb / RPM, es posible que deba utilizar el método CLI ejecutando termscp con sudo"
|
||||
},
|
||||
"cli": {
|
||||
"title": "Método CLI",
|
||||
"body": "Si lo prefiere, puede instalar una nueva actualización simplemente usando la opción CLI dedicada:",
|
||||
"pex": "Ejecute con sudo si es necesario (instalado con RPM / DEB)",
|
||||
"then": "Una vez iniciado, se le preguntará si desea instalar o no la actualización. Confirme la instalación y ta-dah, la nueva versión de termscp ahora debería estar disponible en su máquina"
|
||||
}
|
||||
}
|
||||
}
|
||||
115
site/lang/fr.json
Normal file
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"menu": {
|
||||
"desc": "Un file transfer de terminal riche en fonctionnalités",
|
||||
"intro": "Intro",
|
||||
"getStarted": "Pour commencer",
|
||||
"updates": "Mise à jours",
|
||||
"manual": "Manuel utilisateur",
|
||||
"changelog": "Changelog",
|
||||
"author": "A propos de l'auteur",
|
||||
"support": "Me soutenir"
|
||||
},
|
||||
"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.1 est maintenant sorti! Télécharge-le depuis",
|
||||
"here": "ici",
|
||||
"features": {
|
||||
"handy": {
|
||||
"title": "Interface utilisateur pratique",
|
||||
"body": "Explorez et utilisez le système de fichiers distant et local avec une interface utilisateur pratique."
|
||||
},
|
||||
"crossPlatform": {
|
||||
"title": "Multi-plateforme",
|
||||
"body": "Fonctionne sur Windows, MacOS, Linux et FreeBSD"
|
||||
},
|
||||
"customizable": {
|
||||
"title": "Personnalisable",
|
||||
"body": "Personnalisez l'explorateur de fichiers, l'interface utilisateur et bien d'autres paramètres..."
|
||||
},
|
||||
"bookmarks": {
|
||||
"title": "Signets",
|
||||
"body": "Connectez-vous à vos hôtes préférés via des signets intégrés et des connexions récentes"
|
||||
},
|
||||
"security": {
|
||||
"title": "Sécurité avant tout",
|
||||
"body": "Enregistrez vos mots de passe dans le coffre-fort de votre système"
|
||||
},
|
||||
"performance": {
|
||||
"title": "Regard sur les performances",
|
||||
"body": "termscp a été développé en gardant un œil sur les performances pour éviter une utilisation élevée du processeur"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"getStarted": "Pour commencer",
|
||||
"manual": "Manuel d'utilisateur",
|
||||
"updates": "Mise à jour"
|
||||
}
|
||||
},
|
||||
"getStarted": {
|
||||
"title": "Pour commencer",
|
||||
"quickSetup": "Installation rapide",
|
||||
"suggested": "Nous suggérons fortement cette méthode pour installer termscp",
|
||||
"posixUsers": "Si vous êtes un utilisateur Linux, FreeBSD ou MacOS, vous pouvez installer termscp via cette simple commande , qui utilisera un installateur de script shell :",
|
||||
"windows": {
|
||||
"title": "Utilisateurs Windows",
|
||||
"intro": "Vous pouvez installer termscp sur Windows via",
|
||||
"moderation": "Considérez que la modération Chocolatey peut prendre jusqu'à quelques semaines depuis la dernière version, donc si la dernière version n'est pas encore disponible, vous pouvez l'installer en téléchargeant le fichier ZIP depuis",
|
||||
"then": "puis, depuis le répertoire ZIP, installez-le via"
|
||||
},
|
||||
"linuxUsers": "Utilisateurs Linux",
|
||||
"notConfident": "Optez plutôt pour ces méthodes si vous ne vous sentez pas à l'aise avec le script shell",
|
||||
"noBinary": "Optez plutôt pour cette méthode si les binaires pour votre plate-forme ne sont pas disponibles",
|
||||
"arch": {
|
||||
"title": "Utilisateurs dérivés Arch",
|
||||
"intro": "Sur les distributions basées sur Arch Linux, vous pouvez installer termscp à l'aide d'un gestionnaire de packages AUR tel que",
|
||||
"then": "puis, installez-le via"
|
||||
},
|
||||
"debian": {
|
||||
"title": "Utilisateurs dérivés Debian",
|
||||
"body": "Sur les distributions basées sur Debian, vous pouvez installer termscp en utilisant le paquet Deb via :"
|
||||
},
|
||||
"redhat": {
|
||||
"title": "Utilisateurs dérivés RedHat",
|
||||
"body": "Sur les distributions basées sur RedHat, vous pouvez installer termscp à l'aide du package RPM via :"
|
||||
},
|
||||
"macos": {
|
||||
"title": "Utilisateurs MacOS",
|
||||
"install": "Installez termscp via"
|
||||
},
|
||||
"cargo": {
|
||||
"title": "Installer avec Cargo",
|
||||
"body": "Si un package n'est pas disponible pour votre système, vous pouvez choisir d'installer Termscp via",
|
||||
"requirements": "Pour installer termscp via Cargo, ces conditions doivent être remplies",
|
||||
"install": "Ensuite, vous pouvez installer termscp via",
|
||||
"noKeyring": "Ou si vous ne voulez pas avoir de support pour le trousseau de clés ou si vous construisez sur *BSD :",
|
||||
"noSMB": "Ou si vous ne voulez pas avoir de support pour SMB:"
|
||||
}
|
||||
},
|
||||
"updates": {
|
||||
"title": "Tenir à jour termscp",
|
||||
"disclaimer": " La mise à jour de termscp avec cette méthode n'est disponible que pour les versions 0.7.x ou supérieures. Si vous avez une ancienne version, vous devez installer les mises à jour en utilisant le",
|
||||
"reasons": {
|
||||
"title": "Pourquoi devriez-vous installer les mises à jour",
|
||||
"wallOfText": "termscp est une application qui est encore à ses débuts de développement, la première version est sortie en décembre 2020 et pratiquement il n'y a qu'un seul gars qui travaille dessus et il y a encore beaucoup de travail à faire pour l'améliorer et le faire rapide et fiable. Parallèlement à cela, vous devez également considérer que, puisqu'il s'agit d'une application qui fonctionne avec des protocoles réseau et est destinée à manipuler des secrets et des informations d'identification, il peut toujours y avoir un problème de sécurité. Je ne peux pas garantir qu'il n'y ait pas de problèmes de sécurité dans les versions que j'ai publiées au cours de ces mois, et s'il y en a, ce n'est peut-être même pas de ma faute, mais ils pourraient être contenus dans les bibliothèques sur lesquelles s'appuie Termscp. Pour cette raison, il est toujours TRÈS important de maintenir termscp à jour. Pour prouver à quel point j'y tiens, considérez simplement que j'ai implémenté quelque chose que de nombreuses autres applications open source ne feront pas : la vérification des mises à jour. Chaque fois que vous démarrez termscp (sauf s'il est désactivé dans la configuration), termscp vérifiera toujours si une nouvelle version est disponible et vous en informera immédiatement. En plus des problèmes de sécurité, chaque mise à jour majeure apportera de nombreuses fonctionnalités impressionnantes 🦄 vous ne pouvez pas manquer et l'application devient plus fiable et stable après chaque mise à jour 😄",
|
||||
"tldr": "Problèmes de sécurité possibles ; de nouvelles fonctionnalités impressionnantes ; performances et stabilité; Corrections de bugs"
|
||||
},
|
||||
"gui": {
|
||||
"title": "Méthode GUI",
|
||||
"body": "La méthode GUI consiste simplement à démarrer termscp sans options, vous devriez alors être devant le formulaire d'authentification. S'il y a une mise à jour disponible, un message comme \"termscp x.y.z is now OUT ! Install updated and read the release notes with CTRL+R\". Tout ce que vous avez à faire à ce stade pour mettre à jour termscp, c'est :",
|
||||
"steps": {
|
||||
"st": "appuyez sur CTRL+R. Les notes de version devraient maintenant être affichées",
|
||||
"nd": "Sélectionnez \"YES\" dans l'entrée radio \"Install update?\"",
|
||||
"rd": "appuyez \"Enter\""
|
||||
},
|
||||
"then": "Si tout a fonctionné correctement, un message vert \"termscp x.y.z has been installed!\" s'affichera. Redémarrez simplement Termscp et profitez de la mise à jour",
|
||||
"pex": " Si vous avez déjà installé termscp via le package Deb/RPM, vous devrez peut-être utiliser la méthode CLI exécutant termscp avec sudo"
|
||||
},
|
||||
"cli": {
|
||||
"title": "Méthode CLI",
|
||||
"body": "Si vous préférez, vous pouvez installer le mise à jour avec l'option CLI dédié :",
|
||||
"pex": "Exécuter avec sudo si nécessaire (installé avec RPM/DEB)",
|
||||
"then": "Une fois démarré, vous serez invité à installer ou non la mise à jour. Confirmez l'installation et ta-dah, la nouvelle version de termscp devrait maintenant être disponible sur votre machine"
|
||||
}
|
||||
}
|
||||
}
|
||||
115
site/lang/it.json
Normal file
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"menu": {
|
||||
"desc": "Un file transfer ricco di funzionalità",
|
||||
"intro": "Intro",
|
||||
"getStarted": "Per iniziare",
|
||||
"updates": "Aggiornamenti",
|
||||
"manual": "Manuale",
|
||||
"changelog": "Rilasci",
|
||||
"author": "Sull'autore",
|
||||
"support": "Supportami"
|
||||
},
|
||||
"intro": {
|
||||
"caption": "Un file transfer ed explorer ricco di funzionalità con supporto per SFTP/SCP/FTP/S3",
|
||||
"getStarted": "Installa termscp →",
|
||||
"versionAlert": "termscp 0.12.1 è ORA disponbile! Scaricalo da",
|
||||
"here": "qui",
|
||||
"features": {
|
||||
"handy": {
|
||||
"title": "UI ergonomica",
|
||||
"body": "Naviga e lavora sulla macchina remote e locale attraverso una UI di facile utilizzo"
|
||||
},
|
||||
"crossPlatform": {
|
||||
"title": "Multi piattaforma",
|
||||
"body": "Gira su Windows, MacOS, Linux e FreeBSD"
|
||||
},
|
||||
"customizable": {
|
||||
"title": "Personalizzabile",
|
||||
"body": "Personalizza l'explorer, l'interfaccia e molto altro..."
|
||||
},
|
||||
"bookmarks": {
|
||||
"title": "Preferiti",
|
||||
"body": "Connetitit ai tuoi host preferiti e accedi in velocemente alle tue ultime sessioni"
|
||||
},
|
||||
"security": {
|
||||
"title": "Sicurezza in prima classe",
|
||||
"body": "Salva le tue password nel vault del tuo sistema operativo"
|
||||
},
|
||||
"performance": {
|
||||
"title": "Focus sulle performance",
|
||||
"body": "termscp è stato sviluppato tenendo conto delle performance, per garantirti il minor consumo di risore possibile"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"getStarted": "Per iniziare",
|
||||
"manual": "Manuale utente",
|
||||
"updates": "Aggiornamenti"
|
||||
}
|
||||
},
|
||||
"getStarted": {
|
||||
"title": "Installazione",
|
||||
"quickSetup": "Installazione veloce",
|
||||
"suggested": "Suggeriamo questo metodo per installare termscp",
|
||||
"posixUsers": "Se sei un utente Linux, MacOS o *BSD puoi installare termscp con questo semplice comando, che utilizzerà un semplice script per installare l'applicazione;",
|
||||
"windows": {
|
||||
"title": "Utenti Windows",
|
||||
"intro": "Puoi installare termscp su Windows tramite",
|
||||
"moderation": "Attenzione che i moderatori di chocolatey potrebbero metterci qualche settimana ad approvare l'ultima versione, quindi se l'ultima versione non fosse ancora disponibile, puoi installarla tramite il file ZIP da",
|
||||
"then": "e poi, dalla directory dello ZIP, lo installi con"
|
||||
},
|
||||
"linuxUsers": "Utenti Linux",
|
||||
"notConfident": "Opta per questi metodi nel caso non ti fidi dello script install.sh",
|
||||
"noBinary": "Opta per questo metodo nel caso non fosse disponibile un binario per la tua piattaforma",
|
||||
"arch": {
|
||||
"title": "Utenti derivate Arch",
|
||||
"intro": "Sulle distro derivate da Arch puoi installare termscp tramite un package manager AUR come",
|
||||
"then": "e poi lanciare"
|
||||
},
|
||||
"debian": {
|
||||
"title": "Utenti derivate Debian",
|
||||
"body": "Sulle distro derivate Debian puoi installare termscp tramite il pacchetto Deb, con:"
|
||||
},
|
||||
"redhat": {
|
||||
"title": "Utenti derivate RedHat",
|
||||
"body": "Sulle distro derivate RedHat puoi installare termscp tramite il pacchetto RPM, con:"
|
||||
},
|
||||
"macos": {
|
||||
"title": "Utenti MacOS",
|
||||
"install": "Installa termscp con"
|
||||
},
|
||||
"cargo": {
|
||||
"title": "Installa con Cargo",
|
||||
"body": "Se un pacchetto non è disponibile per il tuo sistema, puoi installare termscp con cargo con",
|
||||
"requirements": "Per installare con Cargo, questi requisiti di sistema devono essere soddisfatti",
|
||||
"install": "Infine puoi installare termscp con",
|
||||
"noKeyring": "O se non hai il supporto a DBUS o sei un utente *BSD:",
|
||||
"noSMB": "O se vuoi disabilitare il supporto per SMB:"
|
||||
}
|
||||
},
|
||||
"updates": {
|
||||
"title": "Mantenere termscp all'ultima versione",
|
||||
"disclaimer": "L'aggiornamento di termscp con il metodo descritto è disponibile solo per le versioni suaccessive o uguali alla 0.7.x. Nel caso tu abbia una versione precedente, puoi aggiornare termscp tramite il",
|
||||
"reasons": {
|
||||
"title": "Perché dovresti aggiornare termscp",
|
||||
"wallOfText": "Termscp è un'applicazione che è ancora all'inizio del suo ciclo di vita, la prima versione è infatti risalente a dicembre 2020 e praticamente c'è solo una persona che ci lavora. C'è ancora molto da fare prima di renderlo veloce, sicuro al 100% ed affidabile. Oltre a questo devi considerare che lavorando con protocolli di rete che utilizzano autenticazione ci potrebbero sempre essere delle falle di sicurezza nelle librerie su cui ci appoggiamo e non ho modo ad oggi di garantire che non ce ne siano. Per questo motivo è sempre importante mantenere termscp all'ultima versione e leggere i changelog del progetto. Tanto per capirci su quanto ci tenga a mantenere le versioni all'ultima versione, questo è uno dei pochi progetti CLI ad avere la possibilità di installarlo direttamente dall'applicazione Comunque oltre a possibili falle di sicurezza e bug, ad ogni aggiornamento introdurrò nuove fantastiche funzionalità 🦄 da non perdere 😄",
|
||||
"tldr": "Possibili problemi di sicurezza; nuove features; performance e stabilità; bug fix"
|
||||
},
|
||||
"gui": {
|
||||
"title": "Metodo da interfaccia grafica",
|
||||
"body": "Il metodo da interfaccia grafica richiede di lanciare termscp senza argomenti, a questo punto nella pagina dell'autenticazione dovresti essere notificato di una nuova versione disponibile, tramite un messaggio tipo \"termscp x.y.z is OUT! Update and read release notes with CTRL+R\". A questo punto ti basterà:",
|
||||
"steps": {
|
||||
"st": "Premere CTRL+R. Le note di rilascio saranno visualizzate in un popup",
|
||||
"nd": "Seleziona \"YES\" nel dialogo radio \"Install update?\"",
|
||||
"rd": "Premi \"Invio\""
|
||||
},
|
||||
"then": "Se tutto è andato a buon fine un messaggio con scritto \"termscp x.y.z has been installed!\" sarà visualizzato. A questo punto riavvia termscp e goditi le novità 😄",
|
||||
"pex": " Se in precedenza hai installato termscp con pacchetto Deb/RPM, dovresti aver bisogno di lanciare termscp da utente sudo. Nel caso ti consiglio il metodo CLI."
|
||||
},
|
||||
"cli": {
|
||||
"title": "Metodo da linea di comando",
|
||||
"body": "Se preferisci o devi installare con sudo, puoi installare termscp lanciandolo con l'opzione da linea di comando:",
|
||||
"pex": "Lancialo con sudo se necessario (tipo se l'hai installato con pacchetto RPM/DEB)",
|
||||
"then": "Una volta lanciato, se c'è un aggiornamento disponibile ti chiederà se procedere. Conferma e a questo punto dovrebbe installarlo. Se tutto è andato a buon fine, riavviando termscp dovrebbe essere l'ultima versione."
|
||||
}
|
||||
}
|
||||
}
|
||||
115
site/lang/zh-CN.json
Normal file
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"menu": {
|
||||
"desc": "功能丰富的终端文件传输",
|
||||
"intro": "介绍",
|
||||
"getStarted": "开始",
|
||||
"updates": "安装更新",
|
||||
"manual": "用户手册",
|
||||
"changelog": "发布历史",
|
||||
"author": "关于作者",
|
||||
"support": "支持我"
|
||||
},
|
||||
"intro": {
|
||||
"caption": "功能丰富的终端 UI 文件传输和浏览器,支持 SCP/SFTP/FTP/S3",
|
||||
"getStarted": "开始 →",
|
||||
"versionAlert": "termscp 0.12.1 现已发布! 从下载",
|
||||
"here": "这里",
|
||||
"features": {
|
||||
"handy": {
|
||||
"title": "方便的用户界面",
|
||||
"body": "使用方便的 UI 在远程和本地机器文件系统上探索和操作。"
|
||||
},
|
||||
"crossPlatform": {
|
||||
"title": "跨平台",
|
||||
"body": "在 Windows、MacOS、Linux 和 FreeBSD 上运行"
|
||||
},
|
||||
"customizable": {
|
||||
"title": "可定制",
|
||||
"body": "自定义文件浏览器、UI 和许多其他参数..."
|
||||
},
|
||||
"bookmarks": {
|
||||
"title": "书签",
|
||||
"body": "通过内置书签和最近的连接连接到您最喜欢的主机"
|
||||
},
|
||||
"security": {
|
||||
"title": "安全第一",
|
||||
"body": "将您的密码保存到您的操作系统密钥保管库中"
|
||||
},
|
||||
"performance": {
|
||||
"title": "关注性能",
|
||||
"body": "已经开发了 termscp 关注性能以防止高 CPU 使用率"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"getStarted": "开始",
|
||||
"manual": "用户手册",
|
||||
"updates": "安装更新"
|
||||
}
|
||||
},
|
||||
"getStarted": {
|
||||
"title": "开始",
|
||||
"quickSetup": "快速设置",
|
||||
"suggested": "我们强烈建议使用这种方法来安装termscp",
|
||||
"posixUsers": "如果您是 Linux、FreeBSD 或 MacOS 用户,您可以通过这个简单的命令安装 termscp,它将使用 shell 脚本安装程序:",
|
||||
"windows": {
|
||||
"title": "Windows 用户",
|
||||
"intro": "安装",
|
||||
"moderation": "考虑到 Chocolatey 审核自上次发布以来可能需要长达数周的时间,因此如果最新版本尚不可用,您可以从以下位置下载 ZIP 文件进行安装",
|
||||
"then": "然后,从 ZIP 目录,安装它"
|
||||
},
|
||||
"linuxUsers": "Linux 用户",
|
||||
"notConfident": "如果您对使用 shell 脚本没有信心,请选择这些方法",
|
||||
"noBinary": "如果您的平台的二进制文件不可用,请改用此方法",
|
||||
"arch": {
|
||||
"title": "Arch派生用户",
|
||||
"intro": "在基于 Arch Linux 的发行版上,您可以使用 AUR 包管理器安装 termscp,例如",
|
||||
"then": "然后运行"
|
||||
},
|
||||
"debian": {
|
||||
"title": "Debian派生用户",
|
||||
"body": "在基于 Debian 的发行版上,您可以通过以下方式使用 Deb 包安装 termscp:"
|
||||
},
|
||||
"redhat": {
|
||||
"title": "Redhat派生用户",
|
||||
"body": "在基于 RedHat 的发行版上,您可以通过以下方式使用 RPM 包安装 termscp:"
|
||||
},
|
||||
"macos": {
|
||||
"title": "MacOS 用户",
|
||||
"install": "通过以下方式安装termscp:"
|
||||
},
|
||||
"cargo": {
|
||||
"title": "使用“Cargo”安装",
|
||||
"body": "如果您的系统没有可用的软件包,您可以选择通过以下方式安装 termscp:",
|
||||
"requirements": "要通过 Cargo 安装termscp,必须满足这些要求",
|
||||
"install": "然后你可以通过安装termscp",
|
||||
"noKeyring": "或者,如果您不想支持密钥环,或者您正在构建 *BSD:",
|
||||
"noSMB": "或者如果你想禁用 SMB :"
|
||||
}
|
||||
},
|
||||
"updates": {
|
||||
"title": "使termscp保持最新",
|
||||
"disclaimer": " 使用此方法更新 termscp 仅适用于 0.7.x 版本或更高版本。 如果您有旧版本,则必须使用",
|
||||
"reasons": {
|
||||
"title": "为什么要安装更新",
|
||||
"wallOfText": "termscp 是一个仍处于早期开发阶段的应用程序,第一个版本已于 2020 年 12 月发布,实际上只有一个人在开发它,为了改进它并使其成功,还有很多工作要做快速可靠。除此之外,您还应该考虑到,由于它是一个使用网络协议的应用程序,旨在操纵机密和凭据,因此可能始终存在安全问题。我不能保证我这几个月发布的版本没有安全问题,如果有,它们甚至可能不是我的错,但它们可能包含在 termscp 依赖的库中。因此,保持termscp 最新总是非常重要的。为了证明我有多关心它,请考虑一下我已经实现了许多其他开源应用程序不会做的事情:更新检查。每当您启动termscp(除非在配置中停用)termscp 将始终检查是否有新版本可用并立即通知您。除了安全问题,每次重大更新都会带来许多很棒的功能🦄你不能错过,每次更新后应用程序都变得更加可靠和稳定😄",
|
||||
"tldr": "可能的安全问题; 新的很棒的功能; 性能和稳定性; Bug修复"
|
||||
},
|
||||
"gui": {
|
||||
"title": "图形用户界面方式",
|
||||
"body": "GUI 方法只包括启动没有选项的 termscp,然后您应该位于身份验证表单的前面。 如果有可用更新,则会显示类似\"termscp x.y.z is OUT! Update and read release notes with CTRL+R\"之类的消息。 此时更新termscp所需要做的就是:",
|
||||
"steps": {
|
||||
"st": "按 CTRL+R。 现在应该显示发行说明",
|
||||
"nd": "在 \"Install update?\" 单选输入中选择 \"YES\"",
|
||||
"rd": "按 \"Enter\""
|
||||
},
|
||||
"then": "termscp x.y.z has been installed!”。 只需重新启动termscp并享受更新😄",
|
||||
"pex": " 如果您之前已经通过 Deb/RPM 包安装了 termscp,您可能需要使用 CLI 方法通过 sudo 运行 termscp"
|
||||
},
|
||||
"cli": {
|
||||
"title": "命令行方式",
|
||||
"body": "如果您愿意,可以仅使用专用 CLI 选项安装新更新:",
|
||||
"pex": "如有必要,使用 sudo 运行(使用 RPM/DEB 安装)",
|
||||
"then": "启动后,系统将提示您是否安装更新。 确认安装和 ta-dah,新版本的termscp 现在应该可以在你的机器上使用了"
|
||||
}
|
||||
}
|
||||
}
|
||||
3
site/output.css
Normal file
2
site/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
User-Agent: *
|
||||
Allow: /
|
||||
29
site/sitemap.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
||||
<url>
|
||||
<loc>https://termscp.veeso.dev/</loc>
|
||||
<lastmod>2023-07-05</lastmod>
|
||||
<priority>1.00</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://termscp.veeso.dev/get-started.html</loc>
|
||||
<lastmod>2023-07-05</lastmod>
|
||||
<priority>0.95</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://termscp.veeso.dev/updates.html</loc>
|
||||
<lastmod>2023-07-05</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://termscp.veeso.dev/user-manual.html</loc>
|
||||
<lastmod>2023-07-05</lastmod>
|
||||
<priority>0.90</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://termscp.veeso.dev/changelog.html</loc>
|
||||
<lastmod>2023-07-05</lastmod>
|
||||
<priority>0.50</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
75
site/updates.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en_US" class="dark">
|
||||
|
||||
<head>
|
||||
<title>
|
||||
Install termscp updates | termscp
|
||||
</title>
|
||||
<meta property="og:description"
|
||||
content="How to update termscp. Keep termscp up to date with the built-in command." />
|
||||
<meta name="description"
|
||||
content="How to update termscp. Keep termscp up to date with the built-in command." />
|
||||
<meta property="og:title" content="Install termscp updates | termscp" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta property="og:image" content="https://termscp.veeso.dev/assets/images/og_preview.jpg" />
|
||||
<meta property="og:image:type" content="image/jpg" />
|
||||
<meta property="og:image:width" content="1280" />
|
||||
<meta property="og:image:height" content="640" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="termscp" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content="https://termscp.veeso.dev" />
|
||||
<!-- Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.11.0/devicon.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
|
||||
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
|
||||
crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/fontawesome.min.css"
|
||||
integrity="sha512-OdEXQYCOldjqUEsuMKsZRj93Ht23QRlhIb8E/X0sbwZhme8eUw6g8q7AdxGJKakcBbv7+/PX0Gc2btf7Ru8cZA=="
|
||||
crossorigin="anonymous" />
|
||||
<!-- tailwind -->
|
||||
<link href="output.css" rel="stylesheet" />
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="assets/images/favicon-96x96.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon-16x16.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="layout" class="dark:bg-brand dark:text-gray-100">
|
||||
|
||||
<!-- Menu -->
|
||||
<header id="menu"></header>
|
||||
<main>
|
||||
<div id="main" class="w-8/12 sm:w-full mx-auto pt-4 pb-8"></div>
|
||||
</main>
|
||||
<footer id="footer"></footer>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<!-- Showdown JS-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
|
||||
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<!-- Local -->
|
||||
<script src="js/lang.min.js"></script>
|
||||
<script src="js/core.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
<script src="js/resolvers.js"></script>
|
||||
<!-- ko-fi -->
|
||||
<script src="https://storage.ko-fi.com/cdn/scripts/overlay-widget.js"></script>
|
||||
<script>
|
||||
kofiWidgetOverlay.draw("veeso", {
|
||||
type: "floating-chat",
|
||||
"floating-chat.donateButton.text": "Support me",
|
||||
"floating-chat.donateButton.background-color": "#31363b",
|
||||
"floating-chat.donateButton.text-color": "#fff",
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
75
site/user-manual.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en_US" class="dark">
|
||||
|
||||
<head>
|
||||
<title>
|
||||
termscp user manual | termscp
|
||||
</title>
|
||||
<meta property="og:description"
|
||||
content="How to use termscp. User manual for termscp. How to setup ssh keys in termscp? How to set color themes in termscp?" />
|
||||
<meta name="description"
|
||||
content="How to use termscp. User manual for termscp. How to setup ssh keys in termscp? How to set color themes in termscp?" />
|
||||
<meta property="og:title" content="termscp user manual | termscp" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta property="og:image" content="https://termscp.veeso.dev/assets/images/og_preview.jpg" />
|
||||
<meta property="og:image:type" content="image/jpg" />
|
||||
<meta property="og:image:width" content="1280" />
|
||||
<meta property="og:image:height" content="640" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="termscp" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content="https://termscp.veeso.dev" />
|
||||
<!-- Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.11.0/devicon.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
|
||||
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
|
||||
crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/fontawesome.min.css"
|
||||
integrity="sha512-OdEXQYCOldjqUEsuMKsZRj93Ht23QRlhIb8E/X0sbwZhme8eUw6g8q7AdxGJKakcBbv7+/PX0Gc2btf7Ru8cZA=="
|
||||
crossorigin="anonymous" />
|
||||
<!-- tailwind -->
|
||||
<link href="output.css" rel="stylesheet" />
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="assets/images/favicon-96x96.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon-16x16.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="layout" class="dark:bg-brand dark:text-gray-100">
|
||||
|
||||
<!-- Menu -->
|
||||
<header id="menu"></header>
|
||||
<main>
|
||||
<div id="main" class="w-8/12 sm:w-full mx-auto pt-4 pb-8"></div>
|
||||
</main>
|
||||
<footer id="footer"></footer>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<!-- Showdown JS-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
|
||||
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<!-- Local -->
|
||||
<script src="js/lang.min.js"></script>
|
||||
<script src="js/core.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
<script src="js/resolvers.js"></script>
|
||||
<!-- ko-fi -->
|
||||
<script src="https://storage.ko-fi.com/cdn/scripts/overlay-widget.js"></script>
|
||||
<script>
|
||||
kofiWidgetOverlay.draw("veeso", {
|
||||
type: "floating-chat",
|
||||
"floating-chat.donateButton.text": "Support me",
|
||||
"floating-chat.donateButton.background-color": "#31363b",
|
||||
"floating-chat.donateButton.text-color": "#fff",
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -2,47 +2,27 @@
|
||||
//!
|
||||
//! `activity_manager` is the module which provides run methods and handling for activities
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// Deps
|
||||
use crate::filetransfer::FileTransferParams;
|
||||
use crate::host::{HostError, Localhost};
|
||||
use crate::system::config_client::ConfigClient;
|
||||
use crate::system::environment;
|
||||
use crate::system::theme_provider::ThemeProvider;
|
||||
use crate::ui::activities::{
|
||||
auth::AuthActivity, filetransfer::FileTransferActivity, setup::SetupActivity, Activity,
|
||||
ExitReason,
|
||||
};
|
||||
use crate::ui::context::Context;
|
||||
|
||||
// Namespaces
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
/// ### NextActivity
|
||||
///
|
||||
use remotefs_ssh::SshKeyStorage as SshKeyStorageTrait;
|
||||
|
||||
use crate::filetransfer::{FileTransferParams, FileTransferProtocol};
|
||||
use crate::host::{HostError, Localhost};
|
||||
use crate::system::bookmarks_client::BookmarksClient;
|
||||
use crate::system::config_client::ConfigClient;
|
||||
use crate::system::environment;
|
||||
use crate::system::sshkey_storage::SshKeyStorage;
|
||||
use crate::system::theme_provider::ThemeProvider;
|
||||
use crate::ui::activities::auth::AuthActivity;
|
||||
use crate::ui::activities::filetransfer::FileTransferActivity;
|
||||
use crate::ui::activities::setup::SetupActivity;
|
||||
use crate::ui::activities::{Activity, ExitReason};
|
||||
use crate::ui::context::Context;
|
||||
use crate::utils::{fmt, tty};
|
||||
|
||||
/// NextActivity identifies the next identity to run once the current has ended
|
||||
pub enum NextActivity {
|
||||
Authentication,
|
||||
@@ -50,21 +30,18 @@ pub enum NextActivity {
|
||||
SetupActivity,
|
||||
}
|
||||
|
||||
/// ### ActivityManager
|
||||
///
|
||||
/// The activity manager takes care of running activities and handling them until the application has ended
|
||||
pub struct ActivityManager {
|
||||
context: Option<Context>,
|
||||
ticks: Duration,
|
||||
local_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl ActivityManager {
|
||||
/// Initializes a new Activity Manager
|
||||
pub fn new(local_dir: &Path, ticks: Duration) -> Result<ActivityManager, HostError> {
|
||||
pub fn new(ticks: Duration) -> Result<ActivityManager, HostError> {
|
||||
// Prepare Context
|
||||
// Initialize configuration client
|
||||
let (config_client, error): (ConfigClient, Option<String>) =
|
||||
let (config_client, error_config): (ConfigClient, Option<String>) =
|
||||
match Self::init_config_client() {
|
||||
Ok(cli) => (cli, None),
|
||||
Err(err) => {
|
||||
@@ -72,19 +49,102 @@ impl ActivityManager {
|
||||
(ConfigClient::degraded(), Some(err))
|
||||
}
|
||||
};
|
||||
let (bookmarks_client, error_bookmark) = match Self::init_bookmarks_client() {
|
||||
Ok(cli) => (cli, None),
|
||||
Err(err) => (None, Some(err)),
|
||||
};
|
||||
let error = error_config.or(error_bookmark);
|
||||
let theme_provider: ThemeProvider = Self::init_theme_provider();
|
||||
let ctx: Context = Context::new(config_client, theme_provider, error);
|
||||
let ctx: Context = Context::new(bookmarks_client, config_client, theme_provider, error);
|
||||
Ok(ActivityManager {
|
||||
context: Some(ctx),
|
||||
local_dir: local_dir.to_path_buf(),
|
||||
ticks,
|
||||
})
|
||||
}
|
||||
|
||||
/// Set file transfer params
|
||||
pub fn set_filetransfer_params(&mut self, params: FileTransferParams) {
|
||||
pub fn set_filetransfer_params(
|
||||
&mut self,
|
||||
mut params: FileTransferParams,
|
||||
password: Option<&str>,
|
||||
) -> Result<(), String> {
|
||||
// Set password if provided
|
||||
if params.password_missing() {
|
||||
if let Some(password) = password {
|
||||
params.set_default_secret(password.to_string());
|
||||
} else if matches!(
|
||||
params.protocol,
|
||||
FileTransferProtocol::Scp | FileTransferProtocol::Sftp,
|
||||
) && params.params.generic_params().is_some()
|
||||
{
|
||||
// * if protocol is SCP or SFTP check whether a SSH key is registered for this remote, in case not ask password
|
||||
let storage = SshKeyStorage::from(self.context.as_ref().unwrap().config());
|
||||
let generic_params = params.params.generic_params().unwrap();
|
||||
if storage
|
||||
.resolve(
|
||||
&generic_params.address,
|
||||
&generic_params
|
||||
.username
|
||||
.clone()
|
||||
.unwrap_or(whoami::username()),
|
||||
)
|
||||
.is_none()
|
||||
{
|
||||
debug!(
|
||||
"storage could not find any suitable key for {}... prompting for password",
|
||||
generic_params.address
|
||||
);
|
||||
self.prompt_password(&mut params)?;
|
||||
} else {
|
||||
debug!(
|
||||
"a key is already set for {}; password is not required",
|
||||
generic_params.address
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.prompt_password(&mut params)?;
|
||||
}
|
||||
}
|
||||
// Put params into the context
|
||||
self.context.as_mut().unwrap().set_ftparams(params);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prompt user for password to set into params.
|
||||
fn prompt_password(&self, params: &mut FileTransferParams) -> Result<(), String> {
|
||||
match tty::read_secret_from_tty("Password: ") {
|
||||
Err(err) => Err(format!("Could not read password: {err}")),
|
||||
Ok(Some(secret)) => {
|
||||
debug!(
|
||||
"Read password from tty: {}",
|
||||
fmt::shadow_password(secret.as_str())
|
||||
);
|
||||
params.set_default_secret(secret);
|
||||
Ok(())
|
||||
}
|
||||
Ok(None) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve provided bookmark name and set it as file transfer params.
|
||||
/// Returns error if bookmark is not found
|
||||
pub fn resolve_bookmark_name(
|
||||
&mut self,
|
||||
bookmark_name: &str,
|
||||
password: Option<&str>,
|
||||
) -> Result<(), String> {
|
||||
if let Some(bookmarks_client) = self.context.as_mut().unwrap().bookmarks_client_mut() {
|
||||
match bookmarks_client.get_bookmark(bookmark_name) {
|
||||
None => Err(format!(
|
||||
r#"Could not resolve bookmark name: "{bookmark_name}" no such bookmark"#
|
||||
)),
|
||||
Some(params) => self.set_filetransfer_params(params, password),
|
||||
}
|
||||
} else {
|
||||
Err(String::from(
|
||||
"Could not resolve bookmark name: bookmarks client not initialized",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
@@ -181,13 +241,24 @@ impl ActivityManager {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// get local path:
|
||||
// - if set in file transfer params, get it from there
|
||||
// - otherwise is env current dir
|
||||
// - otherwise is /
|
||||
let local_wrkdir = ft_params
|
||||
.local_path
|
||||
.clone()
|
||||
.or(std::env::current_dir().ok())
|
||||
.unwrap_or(PathBuf::from("/"));
|
||||
|
||||
// Prepare activity
|
||||
let host: Localhost = match Localhost::new(self.local_dir.clone()) {
|
||||
let host: Localhost = match Localhost::new(local_wrkdir) {
|
||||
Ok(host) => host,
|
||||
Err(err) => {
|
||||
// Set error in context
|
||||
error!("Failed to initialize localhost: {}", err);
|
||||
ctx.set_error(format!("Could not initialize localhost: {}", err));
|
||||
ctx.set_error(format!("Could not initialize localhost: {err}"));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
@@ -256,6 +327,33 @@ impl ActivityManager {
|
||||
|
||||
// -- misc
|
||||
|
||||
fn init_bookmarks_client() -> Result<Option<BookmarksClient>, String> {
|
||||
// Get config dir
|
||||
match environment::init_config_dir() {
|
||||
Ok(path) => {
|
||||
// If some configure client, otherwise do nothing; don't bother users telling them that bookmarks are not supported on their system.
|
||||
if let Some(config_dir_path) = path {
|
||||
let bookmarks_file: PathBuf =
|
||||
environment::get_bookmarks_paths(config_dir_path.as_path());
|
||||
// Initialize client
|
||||
BookmarksClient::new(bookmarks_file.as_path(), config_dir_path.as_path(), 16)
|
||||
.map(Option::Some)
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Could not initialize bookmarks (at \"{}\", \"{}\"): {}",
|
||||
bookmarks_file.display(),
|
||||
config_dir_path.display(),
|
||||
e
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize configuration client
|
||||
fn init_config_client() -> Result<ConfigClient, String> {
|
||||
// Get config dir
|
||||
@@ -268,7 +366,7 @@ impl ActivityManager {
|
||||
environment::get_config_paths(config_dir.as_path());
|
||||
match ConfigClient::new(config_path.as_path(), ssh_dir.as_path()) {
|
||||
Ok(cli) => Ok(cli),
|
||||
Err(err) => Err(format!("Could not read configuration: {}", err)),
|
||||
Err(err) => Err(format!("Could not read configuration: {err}")),
|
||||
}
|
||||
}
|
||||
None => Err(String::from(
|
||||
@@ -277,8 +375,7 @@ impl ActivityManager {
|
||||
}
|
||||
}
|
||||
Err(err) => Err(format!(
|
||||
"Could not initialize configuration directory: {}",
|
||||
err
|
||||
"Could not initialize configuration directory: {err}"
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
127
src/cli_opts.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
//! ## CLI opts
|
||||
//!
|
||||
//! defines the types for main.rs types
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use argh::FromArgs;
|
||||
|
||||
use crate::activity_manager::NextActivity;
|
||||
use crate::filetransfer::FileTransferParams;
|
||||
use crate::system::logging::LogLevel;
|
||||
|
||||
pub enum Task {
|
||||
Activity(NextActivity),
|
||||
ImportTheme(PathBuf),
|
||||
InstallUpdate,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(description = "
|
||||
where positional can be:
|
||||
- [address] [local-wrkdir]
|
||||
OR
|
||||
- [bookmark-Name] [local-wrkdir]
|
||||
|
||||
Address syntax can be:
|
||||
|
||||
- `protocol://user@address:port:wrkdir` for protocols such as Sftp, Scp, Ftp
|
||||
- `s3://bucket-name@region:profile:/wrkdir` for Aws S3 protocol
|
||||
- `\\\\<server>[:port]\\<share>[\\path]` for SMB (on Windows)
|
||||
- `smb://[user@]<server>[:port]</share>[/path]` for SMB (on other systems)
|
||||
|
||||
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"
|
||||
)]
|
||||
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")]
|
||||
pub debug: bool,
|
||||
#[argh(option, short = 'P', description = "provide password from CLI")]
|
||||
pub password: Option<String>,
|
||||
#[argh(switch, short = 'q', description = "disable logging")]
|
||||
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"
|
||||
)]
|
||||
pub ticks: u64,
|
||||
#[argh(switch, short = 'v', description = "print version")]
|
||||
pub version: bool,
|
||||
// -- positional
|
||||
#[argh(
|
||||
positional,
|
||||
description = "protocol://user@address:port:wrkdir local-wrkdir"
|
||||
)]
|
||||
pub positional: Vec<String>,
|
||||
}
|
||||
|
||||
pub struct RunOpts {
|
||||
pub remote: Remote,
|
||||
pub ticks: Duration,
|
||||
pub log_level: LogLevel,
|
||||
pub task: Task,
|
||||
}
|
||||
|
||||
impl Default for RunOpts {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
remote: Remote::None,
|
||||
ticks: Duration::from_millis(10),
|
||||
log_level: LogLevel::Info,
|
||||
task: Task::Activity(NextActivity::Authentication),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Remote {
|
||||
Bookmark(BookmarkParams),
|
||||
Host(HostParams),
|
||||
None,
|
||||
}
|
||||
|
||||
pub struct BookmarkParams {
|
||||
pub name: String,
|
||||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
pub struct HostParams {
|
||||
pub params: FileTransferParams,
|
||||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
impl BookmarkParams {
|
||||
pub fn new<S: AsRef<str>>(name: S, password: Option<S>) -> Self {
|
||||
Self {
|
||||
name: name.as_ref().to_string(),
|
||||
password: password.map(|x| x.as_ref().to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HostParams {
|
||||
pub fn new<S: AsRef<str>>(params: FileTransferParams, password: Option<S>) -> Self {
|
||||
Self {
|
||||
params,
|
||||
password: password.map(|x| x.as_ref().to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,36 +2,18 @@
|
||||
//!
|
||||
//! `bookmarks` is the module which provides data types and de/serializer for bookmarks
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
use crate::filetransfer::params::{AwsS3Params, GenericProtocolParams, ProtocolParams};
|
||||
use crate::filetransfer::{FileTransferParams, FileTransferProtocol};
|
||||
|
||||
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::de::Error as DeError;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::filetransfer::params::{
|
||||
AwsS3Params, GenericProtocolParams, ProtocolParams, SmbParams as TransferSmbParams,
|
||||
};
|
||||
use crate::filetransfer::{FileTransferParams, FileTransferProtocol};
|
||||
|
||||
/// UserHosts contains all the hosts saved by the user in the data storage
|
||||
/// It contains both `Bookmark`
|
||||
#[derive(Deserialize, Serialize, Debug, Default)]
|
||||
@@ -41,7 +23,7 @@ pub struct UserHosts {
|
||||
}
|
||||
|
||||
/// Bookmark describes a single bookmark entry in the user hosts storage
|
||||
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
|
||||
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)]
|
||||
pub struct Bookmark {
|
||||
#[serde(
|
||||
deserialize_with = "deserialize_protocol",
|
||||
@@ -56,12 +38,19 @@ pub struct Bookmark {
|
||||
pub username: Option<String>,
|
||||
/// Password is optional; base64, aes-128 encrypted password
|
||||
pub password: Option<String>,
|
||||
/// Remote folder to connect to (serde rename for legacy reasons)
|
||||
#[serde(rename = "directory")]
|
||||
pub remote_path: Option<PathBuf>,
|
||||
/// local folder to open at startup
|
||||
pub local_path: Option<PathBuf>,
|
||||
/// S3 params; optional. When used other fields are empty for sure
|
||||
pub s3: Option<S3Params>,
|
||||
/// SMB params; optional. Extra params required for SMB protocol
|
||||
pub smb: Option<SmbParams>,
|
||||
}
|
||||
|
||||
/// Connection parameters for Aws s3 protocol
|
||||
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Default)]
|
||||
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq, Default)]
|
||||
pub struct S3Params {
|
||||
pub bucket: String,
|
||||
pub region: Option<String>,
|
||||
@@ -73,11 +62,20 @@ pub struct S3Params {
|
||||
pub new_path_style: Option<bool>,
|
||||
}
|
||||
|
||||
/// Extra Connection parameters for SMB protocol
|
||||
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq, Default)]
|
||||
pub struct SmbParams {
|
||||
pub share: String,
|
||||
pub workgroup: Option<String>,
|
||||
}
|
||||
|
||||
// -- impls
|
||||
|
||||
impl From<FileTransferParams> for Bookmark {
|
||||
fn from(params: FileTransferParams) -> Self {
|
||||
let protocol: FileTransferProtocol = params.protocol;
|
||||
let protocol = params.protocol;
|
||||
let remote_path = params.remote_path;
|
||||
let local_path = params.local_path;
|
||||
// Create generic or others
|
||||
match params.params {
|
||||
ProtocolParams::Generic(params) => Self {
|
||||
@@ -86,7 +84,10 @@ impl From<FileTransferParams> for Bookmark {
|
||||
port: Some(params.port),
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
remote_path,
|
||||
local_path,
|
||||
s3: None,
|
||||
smb: None,
|
||||
},
|
||||
ProtocolParams::AwsS3(params) => Self {
|
||||
protocol,
|
||||
@@ -94,7 +95,24 @@ impl From<FileTransferParams> for Bookmark {
|
||||
port: None,
|
||||
username: None,
|
||||
password: None,
|
||||
remote_path,
|
||||
local_path,
|
||||
s3: Some(S3Params::from(params)),
|
||||
smb: None,
|
||||
},
|
||||
ProtocolParams::Smb(params) => Self {
|
||||
smb: Some(SmbParams::from(params.clone())),
|
||||
protocol,
|
||||
address: Some(params.address),
|
||||
#[cfg(unix)]
|
||||
port: Some(params.port),
|
||||
#[cfg(windows)]
|
||||
port: None,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
remote_path,
|
||||
local_path,
|
||||
s3: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -109,15 +127,43 @@ impl From<Bookmark> for FileTransferParams {
|
||||
let params = AwsS3Params::from(params);
|
||||
Self::new(FileTransferProtocol::AwsS3, ProtocolParams::AwsS3(params))
|
||||
}
|
||||
protocol => {
|
||||
FileTransferProtocol::Ftp(_)
|
||||
| FileTransferProtocol::Scp
|
||||
| FileTransferProtocol::Sftp => {
|
||||
let params = GenericProtocolParams::default()
|
||||
.address(bookmark.address.unwrap_or_default())
|
||||
.port(bookmark.port.unwrap_or(22))
|
||||
.username(bookmark.username)
|
||||
.password(bookmark.password);
|
||||
Self::new(protocol, ProtocolParams::Generic(params))
|
||||
Self::new(bookmark.protocol, ProtocolParams::Generic(params))
|
||||
}
|
||||
#[cfg(unix)]
|
||||
FileTransferProtocol::Smb => {
|
||||
let params = TransferSmbParams::new(
|
||||
bookmark.address.unwrap_or_default(),
|
||||
bookmark.smb.clone().map(|x| x.share).unwrap_or_default(),
|
||||
)
|
||||
.port(bookmark.port.unwrap_or(445))
|
||||
.username(bookmark.username)
|
||||
.password(bookmark.password)
|
||||
.workgroup(bookmark.smb.and_then(|x| x.workgroup));
|
||||
|
||||
Self::new(bookmark.protocol, ProtocolParams::Smb(params))
|
||||
}
|
||||
#[cfg(windows)]
|
||||
FileTransferProtocol::Smb => {
|
||||
let params = TransferSmbParams::new(
|
||||
bookmark.address.unwrap_or_default(),
|
||||
bookmark.smb.clone().map(|x| x.share).unwrap_or_default(),
|
||||
)
|
||||
.username(bookmark.username)
|
||||
.password(bookmark.password);
|
||||
|
||||
Self::new(bookmark.protocol, ProtocolParams::Smb(params))
|
||||
}
|
||||
}
|
||||
.remote_path(bookmark.remote_path) // Set entry remote_path
|
||||
.local_path(bookmark.local_path) // Set entry local path
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,13 +191,33 @@ impl From<S3Params> for AwsS3Params {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl From<TransferSmbParams> for SmbParams {
|
||||
fn from(params: TransferSmbParams) -> Self {
|
||||
Self {
|
||||
share: params.share,
|
||||
workgroup: params.workgroup,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl From<TransferSmbParams> for SmbParams {
|
||||
fn from(params: TransferSmbParams) -> Self {
|
||||
Self {
|
||||
share: params.share,
|
||||
workgroup: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_protocol<'de, D>(deserializer: D) -> Result<FileTransferProtocol, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s: &str = Deserialize::deserialize(deserializer)?;
|
||||
let s: String = Deserialize::deserialize(deserializer)?;
|
||||
// Parse color
|
||||
match FileTransferProtocol::from_str(s) {
|
||||
match FileTransferProtocol::from_str(&s) {
|
||||
Err(err) => Err(DeError::custom(err)),
|
||||
Ok(protocol) => Ok(protocol),
|
||||
}
|
||||
@@ -169,9 +235,10 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_bookmarks_default() {
|
||||
let bookmarks: UserHosts = UserHosts::default();
|
||||
@@ -187,7 +254,10 @@ mod tests {
|
||||
protocol: FileTransferProtocol::Sftp,
|
||||
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 recent: Bookmark = Bookmark {
|
||||
address: Some(String::from("192.168.1.2")),
|
||||
@@ -195,7 +265,10 @@ mod tests {
|
||||
protocol: FileTransferProtocol::Scp,
|
||||
username: Some(String::from("admin")),
|
||||
password: Some(String::from("password")),
|
||||
remote_path: Some(PathBuf::from("/home")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: None,
|
||||
smb: None,
|
||||
};
|
||||
let mut bookmarks: HashMap<String, Bookmark> = HashMap::with_capacity(1);
|
||||
bookmarks.insert(String::from("test"), bookmark);
|
||||
@@ -209,6 +282,14 @@ mod tests {
|
||||
assert_eq!(bookmark.protocol, FileTransferProtocol::Sftp);
|
||||
assert_eq!(bookmark.username.as_deref().unwrap(), "root");
|
||||
assert_eq!(bookmark.password.as_deref().unwrap(), "password");
|
||||
assert_eq!(
|
||||
bookmark.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/tmp")
|
||||
);
|
||||
assert_eq!(
|
||||
bookmark.local_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/usr")
|
||||
);
|
||||
let bookmark: &Bookmark = hosts
|
||||
.recents
|
||||
.get(&String::from("ISO20201218T181432"))
|
||||
@@ -218,6 +299,14 @@ mod tests {
|
||||
assert_eq!(bookmark.protocol, FileTransferProtocol::Scp);
|
||||
assert_eq!(bookmark.username.as_deref().unwrap(), "admin");
|
||||
assert_eq!(bookmark.password.as_deref().unwrap(), "password");
|
||||
assert_eq!(
|
||||
bookmark.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/home")
|
||||
);
|
||||
assert_eq!(
|
||||
bookmark.local_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/usr")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -228,13 +317,23 @@ mod tests {
|
||||
username: Some(String::from("root")),
|
||||
password: Some(String::from("omar")),
|
||||
});
|
||||
let params: FileTransferParams = FileTransferParams::new(FileTransferProtocol::Scp, params);
|
||||
let params: FileTransferParams = FileTransferParams::new(FileTransferProtocol::Scp, params)
|
||||
.remote_path(Some(PathBuf::from("/home")))
|
||||
.local_path(Some(PathBuf::from("/tmp")));
|
||||
let bookmark = Bookmark::from(params);
|
||||
assert_eq!(bookmark.protocol, FileTransferProtocol::Scp);
|
||||
assert_eq!(bookmark.address.as_deref().unwrap(), "127.0.0.1");
|
||||
assert_eq!(bookmark.port.unwrap(), 10222);
|
||||
assert_eq!(bookmark.username.as_deref().unwrap(), "root");
|
||||
assert_eq!(bookmark.password.as_deref().unwrap(), "omar");
|
||||
assert_eq!(
|
||||
bookmark.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/home")
|
||||
);
|
||||
assert_eq!(
|
||||
bookmark.local_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/tmp")
|
||||
);
|
||||
assert!(bookmark.s3.is_none());
|
||||
}
|
||||
|
||||
@@ -269,10 +368,21 @@ mod tests {
|
||||
protocol: FileTransferProtocol::Sftp,
|
||||
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::Sftp);
|
||||
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.generic_params().unwrap();
|
||||
assert_eq!(gparams.address.as_str(), "192.168.1.1");
|
||||
assert_eq!(gparams.port, 22);
|
||||
@@ -288,6 +398,8 @@ mod tests {
|
||||
port: None,
|
||||
username: None,
|
||||
password: None,
|
||||
remote_path: Some(PathBuf::from("/tmp")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: Some(S3Params {
|
||||
bucket: String::from("veeso"),
|
||||
region: Some(String::from("eu-west-1")),
|
||||
@@ -297,9 +409,18 @@ mod tests {
|
||||
secret_access_key: Some(String::from("pluto")),
|
||||
new_path_style: Some(true),
|
||||
}),
|
||||
smb: None,
|
||||
};
|
||||
let params = FileTransferParams::from(bookmark);
|
||||
assert_eq!(params.protocol, FileTransferProtocol::AwsS3);
|
||||
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.s3_params().unwrap();
|
||||
assert_eq!(gparams.bucket_name.as_str(), "veeso");
|
||||
assert_eq!(gparams.region.as_deref().unwrap(), "eu-west-1");
|
||||
@@ -309,4 +430,74 @@ mod tests {
|
||||
assert_eq!(gparams.secret_access_key.as_deref().unwrap(), "pluto");
|
||||
assert_eq!(gparams.new_path_style, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn should_get_ftparams_from_smb_bookmark() {
|
||||
let bookmark: Bookmark = Bookmark {
|
||||
protocol: FileTransferProtocol::Smb,
|
||||
address: Some("localhost".to_string()),
|
||||
port: Some(445),
|
||||
username: Some("foo".to_string()),
|
||||
password: Some("bar".to_string()),
|
||||
remote_path: Some(PathBuf::from("/tmp")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: None,
|
||||
smb: Some(SmbParams {
|
||||
share: "test".to_string(),
|
||||
workgroup: Some("testone".to_string()),
|
||||
}),
|
||||
};
|
||||
|
||||
let params = FileTransferParams::from(bookmark);
|
||||
assert_eq!(params.protocol, FileTransferProtocol::Smb);
|
||||
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 smb_params = params.params.smb_params().unwrap();
|
||||
assert_eq!(smb_params.address.as_str(), "localhost");
|
||||
assert_eq!(smb_params.port, 445);
|
||||
assert_eq!(smb_params.share.as_str(), "test");
|
||||
assert_eq!(smb_params.password.as_deref().unwrap(), "bar");
|
||||
assert_eq!(smb_params.username.as_deref().unwrap(), "foo");
|
||||
assert_eq!(smb_params.workgroup.as_deref().unwrap(), "testone");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn should_get_ftparams_from_smb_bookmark() {
|
||||
let bookmark: Bookmark = Bookmark {
|
||||
protocol: FileTransferProtocol::Smb,
|
||||
address: Some("localhost".to_string()),
|
||||
port: Some(445),
|
||||
username: None,
|
||||
password: None,
|
||||
remote_path: Some(PathBuf::from("/tmp")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: None,
|
||||
smb: Some(SmbParams {
|
||||
share: "test".to_string(),
|
||||
workgroup: None,
|
||||
}),
|
||||
};
|
||||
|
||||
let params = FileTransferParams::from(bookmark);
|
||||
assert_eq!(params.protocol, FileTransferProtocol::Smb);
|
||||
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 smb_params = params.params.smb_params().unwrap();
|
||||
assert_eq!(smb_params.address.as_str(), "localhost");
|
||||
assert_eq!(smb_params.share.as_str(), "test");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,29 +2,6 @@
|
||||
//!
|
||||
//! `config` is the module which provides access to all the termscp configurations
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// export
|
||||
pub use params::*;
|
||||
|
||||
|
||||
@@ -2,36 +2,14 @@
|
||||
//!
|
||||
//! `config` is the module which provides access to termscp configuration
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// Locals
|
||||
use crate::filetransfer::FileTransferProtocol;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
// Ext
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::filetransfer::FileTransferProtocol;
|
||||
|
||||
pub const DEFAULT_NOTIFICATION_TRANSFER_THRESHOLD: u64 = 536870912; // 512MB
|
||||
|
||||
@@ -59,7 +37,7 @@ pub struct UserInterfaceConfig {
|
||||
pub notification_threshold: Option<u64>, // @! Since 0.7.0; Default 512MB
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Default)]
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
/// Contains configuratio related to remote hosts
|
||||
pub struct RemoteConfig {
|
||||
/// Ssh configuration path. If NONE, won't be read
|
||||
@@ -69,6 +47,26 @@ pub struct RemoteConfig {
|
||||
pub ssh_keys: HashMap<String, PathBuf>,
|
||||
}
|
||||
|
||||
impl Default for RemoteConfig {
|
||||
fn default() -> Self {
|
||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("/root"));
|
||||
let mut ssh_config_path = "~/.ssh/config".to_string();
|
||||
ssh_config_path = ssh_config_path.replacen('~', &home_dir.to_string_lossy(), 1);
|
||||
|
||||
// check if ssh config path exists first
|
||||
let ssh_config_path = if PathBuf::from(&ssh_config_path).exists() {
|
||||
Some(ssh_config_path)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self {
|
||||
ssh_config: ssh_config_path,
|
||||
ssh_keys: HashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for UserInterfaceConfig {
|
||||
fn default() -> Self {
|
||||
UserInterfaceConfig {
|
||||
@@ -94,9 +92,10 @@ impl Default for UserInterfaceConfig {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_config_mod_new() {
|
||||
let mut keys: HashMap<String, PathBuf> = HashMap::with_capacity(1);
|
||||
@@ -129,7 +128,7 @@ mod tests {
|
||||
assert_eq!(ui.file_fmt, Some(String::from("{NAME}")));
|
||||
let cfg: UserConfig = UserConfig {
|
||||
user_interface: ui,
|
||||
remote: remote,
|
||||
remote,
|
||||
};
|
||||
assert_eq!(
|
||||
*cfg.remote
|
||||
|
||||
@@ -2,31 +2,10 @@
|
||||
//!
|
||||
//! `serialization` provides serialization and deserialization for configurations
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Contains the error for serializer/deserializer
|
||||
@@ -72,8 +51,6 @@ impl std::fmt::Display for SerializerError {
|
||||
}
|
||||
}
|
||||
|
||||
/// ### serialize
|
||||
///
|
||||
/// Serialize `UserHosts` into TOML and write content to writable
|
||||
pub fn serialize<S>(serializable: &S, mut writable: Box<dyn Write>) -> Result<(), SerializerError>
|
||||
where
|
||||
@@ -100,8 +77,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// ### deserialize
|
||||
///
|
||||
/// Read data from readable and deserialize its content as TOML
|
||||
pub fn deserialize<S>(mut readable: Box<dyn Read>) -> Result<S, SerializerError>
|
||||
where
|
||||
@@ -132,30 +107,30 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Seek;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::config::bookmarks::{Bookmark, S3Params, UserHosts};
|
||||
use pretty_assertions::assert_eq;
|
||||
use tuirealm::tui::style::Color;
|
||||
|
||||
use super::*;
|
||||
use crate::config::bookmarks::{Bookmark, S3Params, SmbParams, UserHosts};
|
||||
use crate::config::params::UserConfig;
|
||||
use crate::config::themes::Theme;
|
||||
use crate::filetransfer::FileTransferProtocol;
|
||||
use crate::utils::test_helpers::create_file_ioers;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::HashMap;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use std::path::PathBuf;
|
||||
use tuirealm::tui::style::Color;
|
||||
|
||||
#[test]
|
||||
fn test_config_serialization_errors() {
|
||||
let error: SerializerError = SerializerError::new(SerializerErrorKind::Syntax);
|
||||
assert!(error.msg.is_none());
|
||||
assert_eq!(format!("{}", error), String::from("Syntax error"));
|
||||
assert_eq!(format!("{error}"), String::from("Syntax error"));
|
||||
let error: SerializerError =
|
||||
SerializerError::new_ex(SerializerErrorKind::Syntax, String::from("bad syntax"));
|
||||
assert!(error.msg.is_some());
|
||||
assert_eq!(
|
||||
format!("{}", error),
|
||||
format!("{error}"),
|
||||
String::from("Syntax error (bad syntax)")
|
||||
);
|
||||
// Fmt
|
||||
@@ -182,7 +157,7 @@ mod tests {
|
||||
fn test_config_serialization_params_deserialize_ok() {
|
||||
let toml_file: tempfile::NamedTempFile = create_good_toml_bookmarks_params();
|
||||
toml_file.as_file().sync_all().unwrap();
|
||||
toml_file.as_file().seek(SeekFrom::Start(0)).unwrap();
|
||||
toml_file.as_file().rewind().unwrap();
|
||||
// Parse
|
||||
let cfg = deserialize(Box::new(toml_file));
|
||||
assert!(cfg.is_ok());
|
||||
@@ -232,7 +207,7 @@ mod tests {
|
||||
fn test_config_serialization_params_deserialize_ok_no_opts() {
|
||||
let toml_file: tempfile::NamedTempFile = create_good_toml_bookmarks_params_no_opts();
|
||||
toml_file.as_file().sync_all().unwrap();
|
||||
toml_file.as_file().seek(SeekFrom::Start(0)).unwrap();
|
||||
toml_file.as_file().rewind().unwrap();
|
||||
// Parse
|
||||
let cfg = deserialize(Box::new(toml_file));
|
||||
assert!(cfg.is_ok());
|
||||
@@ -272,7 +247,7 @@ mod tests {
|
||||
fn test_config_serialization_params_deserialize_nok() {
|
||||
let toml_file: tempfile::NamedTempFile = create_bad_toml_bookmarks_params();
|
||||
toml_file.as_file().sync_all().unwrap();
|
||||
toml_file.as_file().seek(SeekFrom::Start(0)).unwrap();
|
||||
toml_file.as_file().rewind().unwrap();
|
||||
// Parse
|
||||
assert!(deserialize::<UserConfig>(Box::new(toml_file)).is_err());
|
||||
}
|
||||
@@ -291,7 +266,7 @@ mod tests {
|
||||
assert!(serialize(&cfg, writer).is_ok());
|
||||
// Reload configuration and check if it's ok
|
||||
toml_file.as_file().sync_all().unwrap();
|
||||
toml_file.as_file().seek(SeekFrom::Start(0)).unwrap();
|
||||
toml_file.as_file().rewind().unwrap();
|
||||
assert!(deserialize::<UserConfig>(Box::new(toml_file)).is_ok());
|
||||
}
|
||||
|
||||
@@ -376,7 +351,7 @@ mod tests {
|
||||
fn test_config_serializer_bookmarks_serializer_deserialize_ok() {
|
||||
let toml_file: tempfile::NamedTempFile = create_good_toml_bookmarks();
|
||||
toml_file.as_file().sync_all().unwrap();
|
||||
toml_file.as_file().seek(SeekFrom::Start(0)).unwrap();
|
||||
toml_file.as_file().rewind().unwrap();
|
||||
// Parse
|
||||
let hosts = deserialize(Box::new(toml_file));
|
||||
assert!(hosts.is_ok());
|
||||
@@ -391,7 +366,7 @@ mod tests {
|
||||
assert_eq!(host.username.as_deref().unwrap(), "root");
|
||||
assert_eq!(host.password, None);
|
||||
// Verify bookmarks
|
||||
assert_eq!(hosts.bookmarks.len(), 4);
|
||||
assert_eq!(hosts.bookmarks.len(), 5);
|
||||
let host: &Bookmark = hosts.bookmarks.get("raspberrypi2").unwrap();
|
||||
assert_eq!(host.address.as_deref().unwrap(), "192.168.1.31");
|
||||
assert_eq!(host.port.unwrap(), 22);
|
||||
@@ -404,6 +379,10 @@ mod tests {
|
||||
assert_eq!(host.protocol, FileTransferProtocol::Sftp);
|
||||
assert_eq!(host.username.as_deref().unwrap(), "cvisintin");
|
||||
assert_eq!(host.password.as_deref().unwrap(), "mysecret");
|
||||
assert_eq!(
|
||||
host.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/tmp")
|
||||
);
|
||||
let host: &Bookmark = hosts.bookmarks.get("aws-server-prod1").unwrap();
|
||||
assert_eq!(host.address.as_deref().unwrap(), "51.23.67.12");
|
||||
assert_eq!(host.port.unwrap(), 21);
|
||||
@@ -425,13 +404,27 @@ mod tests {
|
||||
assert_eq!(s3.access_key.as_deref().unwrap(), "pippo");
|
||||
assert_eq!(s3.secret_access_key.as_deref().unwrap(), "pluto");
|
||||
assert_eq!(s3.new_path_style.unwrap(), true);
|
||||
|
||||
// smb
|
||||
let host = hosts.bookmarks.get("smb").unwrap();
|
||||
assert_eq!(host.address.as_deref().unwrap(), "localhost");
|
||||
assert_eq!(host.port.unwrap(), 445);
|
||||
#[cfg(unix)]
|
||||
assert_eq!(host.username.as_deref().unwrap(), "test");
|
||||
#[cfg(unix)]
|
||||
assert_eq!(host.password.as_deref().unwrap(), "test");
|
||||
|
||||
let smb = host.smb.as_ref().unwrap();
|
||||
assert_eq!(smb.share.as_str(), "temp");
|
||||
#[cfg(unix)]
|
||||
assert_eq!(smb.workgroup.as_deref().unwrap(), "test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_serializer_bookmarks_serializer_deserialize_nok() {
|
||||
let toml_file: tempfile::NamedTempFile = create_bad_toml_bookmarks();
|
||||
toml_file.as_file().sync_all().unwrap();
|
||||
toml_file.as_file().seek(SeekFrom::Start(0)).unwrap();
|
||||
toml_file.as_file().rewind().unwrap();
|
||||
// Parse
|
||||
assert!(deserialize::<UserHosts>(Box::new(toml_file)).is_err());
|
||||
}
|
||||
@@ -448,7 +441,10 @@ mod tests {
|
||||
protocol: FileTransferProtocol::Sftp,
|
||||
username: Some(String::from("root")),
|
||||
password: None,
|
||||
remote_path: None,
|
||||
local_path: None,
|
||||
s3: None,
|
||||
smb: None,
|
||||
},
|
||||
);
|
||||
bookmarks.insert(
|
||||
@@ -459,7 +455,10 @@ mod tests {
|
||||
protocol: FileTransferProtocol::Sftp,
|
||||
username: Some(String::from("cvisintin")),
|
||||
password: Some(String::from("password")),
|
||||
remote_path: Some(PathBuf::from("/tmp")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: None,
|
||||
smb: None,
|
||||
},
|
||||
);
|
||||
bookmarks.insert(
|
||||
@@ -470,6 +469,8 @@ mod tests {
|
||||
protocol: FileTransferProtocol::AwsS3,
|
||||
username: None,
|
||||
password: None,
|
||||
remote_path: None,
|
||||
local_path: None,
|
||||
s3: Some(S3Params {
|
||||
bucket: "veeso".to_string(),
|
||||
region: Some("eu-west-1".to_string()),
|
||||
@@ -479,6 +480,25 @@ mod tests {
|
||||
secret_access_key: None,
|
||||
new_path_style: None,
|
||||
}),
|
||||
smb: None,
|
||||
},
|
||||
);
|
||||
let smb_params: Option<SmbParams> = Some(SmbParams {
|
||||
share: "test".to_string(),
|
||||
workgroup: None,
|
||||
});
|
||||
bookmarks.insert(
|
||||
String::from("smb"),
|
||||
Bookmark {
|
||||
address: Some("localhost".to_string()),
|
||||
port: Some(445),
|
||||
protocol: FileTransferProtocol::Smb,
|
||||
username: None,
|
||||
password: None,
|
||||
remote_path: None,
|
||||
local_path: None,
|
||||
s3: None,
|
||||
smb: smb_params,
|
||||
},
|
||||
);
|
||||
let mut recents: HashMap<String, Bookmark> = HashMap::with_capacity(1);
|
||||
@@ -490,7 +510,10 @@ mod tests {
|
||||
protocol: FileTransferProtocol::Scp,
|
||||
username: Some(String::from("omar")),
|
||||
password: Some(String::from("aaa")),
|
||||
remote_path: Some(PathBuf::from("/tmp")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: None,
|
||||
smb: None,
|
||||
},
|
||||
);
|
||||
let tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
|
||||
@@ -515,11 +538,11 @@ mod tests {
|
||||
fn test_config_serialization_theme_deserialize() {
|
||||
let toml_file = create_good_toml_theme();
|
||||
toml_file.as_file().sync_all().unwrap();
|
||||
toml_file.as_file().seek(SeekFrom::Start(0)).unwrap();
|
||||
toml_file.as_file().rewind().unwrap();
|
||||
assert!(deserialize::<Theme>(Box::new(toml_file)).is_ok());
|
||||
let toml_file = create_bad_toml_theme();
|
||||
toml_file.as_file().sync_all().unwrap();
|
||||
toml_file.as_file().seek(SeekFrom::Start(0)).unwrap();
|
||||
toml_file.as_file().rewind().unwrap();
|
||||
assert!(deserialize::<Theme>(Box::new(toml_file)).is_err());
|
||||
}
|
||||
|
||||
@@ -529,7 +552,7 @@ mod tests {
|
||||
let file_content: &str = r#"
|
||||
[bookmarks]
|
||||
raspberrypi2 = { address = "192.168.1.31", port = 22, protocol = "SFTP", username = "root", password = "mypassword" }
|
||||
msi-estrem = { address = "192.168.1.30", port = 22, protocol = "SFTP", username = "cvisintin", password = "mysecret" }
|
||||
msi-estrem = { address = "192.168.1.30", port = 22, protocol = "SFTP", username = "cvisintin", password = "mysecret", directory = "/tmp", local_path = "/usr" }
|
||||
aws-server-prod1 = { address = "51.23.67.12", port = 21, protocol = "FTPS", username = "aws001" }
|
||||
|
||||
[bookmarks.my-bucket]
|
||||
@@ -544,6 +567,17 @@ mod tests {
|
||||
secret_access_key = "pluto"
|
||||
new_path_style = true
|
||||
|
||||
[bookmarks.smb]
|
||||
protocol = "SMB"
|
||||
address = "localhost"
|
||||
port = 445
|
||||
username = "test"
|
||||
password = "test"
|
||||
|
||||
[bookmarks.smb.smb]
|
||||
share = "temp"
|
||||
workgroup = "test"
|
||||
|
||||
[recents]
|
||||
ISO20201215T094000Z = { address = "172.16.104.10", port = 22, protocol = "SCP", username = "root" }
|
||||
"#;
|
||||
|
||||
@@ -2,40 +2,17 @@
|
||||
//!
|
||||
//! `themes` is the module which provides the themes configurations and the serializers
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// locals
|
||||
use crate::utils::fmt::fmt_color;
|
||||
use crate::utils::parser::parse_color;
|
||||
// ext
|
||||
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde::de::Error as DeError;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use tuirealm::tui::style::Color;
|
||||
|
||||
/// ### Theme
|
||||
///
|
||||
use crate::utils::fmt::fmt_color;
|
||||
use crate::utils::parser::parse_color;
|
||||
|
||||
/// Theme contains all the colors lookup table for termscp
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct Theme {
|
||||
// -- auth
|
||||
#[serde(
|
||||
@@ -217,9 +194,9 @@ fn deserialize_color<'de, D>(deserializer: D) -> Result<Color, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s: &str = Deserialize::deserialize(deserializer)?;
|
||||
let s: String = Deserialize::deserialize(deserializer)?;
|
||||
// Parse color
|
||||
match parse_color(s) {
|
||||
match parse_color(&s) {
|
||||
None => Err(DeError::custom("Invalid color")),
|
||||
Some(color) => Ok(color),
|
||||
}
|
||||
@@ -236,10 +213,10 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_config_themes_default() {
|
||||
let theme: Theme = Theme::default();
|
||||
|
||||
@@ -2,35 +2,13 @@
|
||||
//!
|
||||
//! `builder` is the module which provides a builder for FileExplorer
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// Locals
|
||||
use super::formatter::Formatter;
|
||||
use super::{ExplorerOpts, FileExplorer, FileSorting, GroupDirs};
|
||||
// Ext
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use super::formatter::Formatter;
|
||||
use super::{ExplorerOpts, FileExplorer, FileSorting, GroupDirs};
|
||||
|
||||
/// Struct used to create a `FileExplorer`
|
||||
pub struct FileExplorerBuilder {
|
||||
explorer: Option<FileExplorer>,
|
||||
@@ -99,10 +77,10 @@ impl FileExplorerBuilder {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_fs_explorer_builder_new_default() {
|
||||
let explorer: FileExplorer = FileExplorerBuilder::new().build();
|
||||
|
||||
@@ -2,40 +2,21 @@
|
||||
//!
|
||||
//! `formatter` is the module which provides formatting utilities for `FileExplorer`
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// Locals
|
||||
use crate::utils::fmt::{fmt_path_elide, fmt_pex, fmt_time};
|
||||
use crate::utils::path::diff_paths;
|
||||
// Ext
|
||||
use bytesize::ByteSize;
|
||||
use regex::Regex;
|
||||
use remotefs::File;
|
||||
use std::path::PathBuf;
|
||||
use std::time::UNIX_EPOCH;
|
||||
#[cfg(target_family = "unix")]
|
||||
|
||||
// Ext
|
||||
use bytesize::ByteSize;
|
||||
use lazy_regex::{Lazy, Regex};
|
||||
use remotefs::File;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
#[cfg(unix)]
|
||||
use users::{get_group_by_gid, get_user_by_uid};
|
||||
|
||||
use crate::utils::fmt::{fmt_path_elide, fmt_pex, fmt_time};
|
||||
use crate::utils::path::diff_paths;
|
||||
use crate::utils::string::secure_substring;
|
||||
// Types
|
||||
// FmtCallback: Formatter, fsentry: &File, cur_str, prefix, length, extra
|
||||
type FmtCallback = fn(&Formatter, &File, &str, &str, Option<&usize>, Option<&String>) -> String;
|
||||
@@ -53,17 +34,14 @@ const FMT_KEY_SYMLINK: &str = "SYMLINK";
|
||||
const FMT_KEY_USER: &str = "USER";
|
||||
// Default
|
||||
const FMT_DEFAULT_STX: &str = "{NAME} {PEX} {USER} {SIZE} {MTIME}";
|
||||
// Regex
|
||||
lazy_static! {
|
||||
/**
|
||||
* Regex matches:
|
||||
* - group 0: KEY NAME
|
||||
* - group 1?: LENGTH
|
||||
* - group 2?: EXTRA
|
||||
*/
|
||||
static ref FMT_KEY_REGEX: Regex = Regex::new(r"\{(.*?)\}").ok().unwrap();
|
||||
static ref FMT_ATTR_REGEX: Regex = Regex::new(r"(?:([A-Z]+))(:?([0-9]+))?(:?(.+))?").ok().unwrap();
|
||||
}
|
||||
/**
|
||||
* Regex matches:
|
||||
* - group 0: KEY NAME
|
||||
* - group 1?: LENGTH
|
||||
* - group 2?: EXTRA
|
||||
*/
|
||||
static FMT_KEY_REGEX: Lazy<Regex> = lazy_regex!(r"\{(.*?)\}");
|
||||
static FMT_ATTR_REGEX: Lazy<Regex> = lazy_regex!(r"(?:([A-Z]+))(:?([0-9]+))?(:?(.+))?");
|
||||
|
||||
/// Call Chain block is a block in a chain of functions which are called in order to format the File.
|
||||
/// A callChain is instantiated starting from the Formatter syntax and the regex, once the groups are found
|
||||
@@ -233,7 +211,7 @@ impl Formatter {
|
||||
_fmt_extra: Option<&String>,
|
||||
) -> String {
|
||||
// Get username
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
let group: String = match fsentry.metadata().gid {
|
||||
Some(gid) => match get_group_by_gid(gid) {
|
||||
Some(user) => user.name().to_string_lossy().to_string(),
|
||||
@@ -241,7 +219,7 @@ impl Formatter {
|
||||
},
|
||||
None => 0.to_string(),
|
||||
};
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(windows)]
|
||||
let group: String = match fsentry.metadata().gid {
|
||||
Some(gid) => gid.to_string(),
|
||||
None => 0.to_string(),
|
||||
@@ -303,15 +281,15 @@ impl Formatter {
|
||||
true => file_len - 2,
|
||||
false => file_len - 1,
|
||||
};
|
||||
let mut name: String = match name.len() >= file_len {
|
||||
let mut name: String = match name.width() >= file_len {
|
||||
false => name,
|
||||
true => format!("{}…", &name[0..last_idx]),
|
||||
true => format!("{}…", secure_substring(&name, 0, last_idx)),
|
||||
};
|
||||
if fsentry.is_dir() {
|
||||
name.push('/');
|
||||
}
|
||||
// Add to cur str, prefix and the key value
|
||||
format!("{}{}{:0width$}", cur_str, prefix, name, width = file_len)
|
||||
format!("{cur_str}{prefix}{name:0file_len$}")
|
||||
}
|
||||
|
||||
/// Format path
|
||||
@@ -371,7 +349,7 @@ impl Formatter {
|
||||
),
|
||||
}
|
||||
// Add to cur str, prefix and the key value
|
||||
format!("{}{}{:10}", cur_str, prefix, pex)
|
||||
format!("{cur_str}{prefix}{pex:10}")
|
||||
}
|
||||
|
||||
/// Format file size
|
||||
@@ -387,7 +365,7 @@ impl Formatter {
|
||||
// Get byte size
|
||||
let size: ByteSize = ByteSize(fsentry.metadata().size);
|
||||
// Add to cur str, prefix and the key value
|
||||
format!("{}{}{:10}", cur_str, prefix, size.to_string())
|
||||
format!("{cur_str}{prefix}{size:10}")
|
||||
} else if fsentry.metadata().symlink.is_some() {
|
||||
let size = ByteSize(
|
||||
fsentry
|
||||
@@ -398,10 +376,10 @@ impl Formatter {
|
||||
.to_string_lossy()
|
||||
.len() as u64,
|
||||
);
|
||||
format!("{}{}{:10}", cur_str, prefix, size.to_string())
|
||||
format!("{cur_str}{prefix}{size:10}")
|
||||
} else {
|
||||
// Add to cur str, prefix and the key value
|
||||
format!("{}{} ", cur_str, prefix)
|
||||
format!("{cur_str}{prefix} ")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,7 +399,7 @@ impl Formatter {
|
||||
};
|
||||
// Replace `FMT_KEY_NAME` with name
|
||||
match fsentry.metadata().symlink.as_deref() {
|
||||
None => format!("{}{} ", cur_str, prefix),
|
||||
None => format!("{cur_str}{prefix} "),
|
||||
Some(p) => format!(
|
||||
"{}{}-> {:0width$}",
|
||||
cur_str,
|
||||
@@ -442,7 +420,7 @@ impl Formatter {
|
||||
_fmt_extra: Option<&String>,
|
||||
) -> String {
|
||||
// Get username
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
let username: String = match fsentry.metadata().uid {
|
||||
Some(uid) => match get_user_by_uid(uid) {
|
||||
Some(user) => user.name().to_string_lossy().to_string(),
|
||||
@@ -450,13 +428,13 @@ impl Formatter {
|
||||
},
|
||||
None => 0.to_string(),
|
||||
};
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(windows)]
|
||||
let username: String = match fsentry.metadata().uid {
|
||||
Some(uid) => uid.to_string(),
|
||||
None => 0.to_string(),
|
||||
};
|
||||
// Add to cur str, prefix and the key value
|
||||
format!("{}{}{:12}", cur_str, prefix, username)
|
||||
format!("{cur_str}{prefix}{username:12}")
|
||||
}
|
||||
|
||||
/// Fallback function in case the format key is unknown
|
||||
@@ -470,7 +448,7 @@ impl Formatter {
|
||||
_fmt_extra: Option<&String>,
|
||||
) -> String {
|
||||
// Add to cur str and prefix
|
||||
format!("{}{}", cur_str, prefix)
|
||||
format!("{cur_str}{prefix}")
|
||||
}
|
||||
|
||||
// Static
|
||||
@@ -545,12 +523,13 @@ impl Formatter {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use remotefs::fs::{File, FileType, Metadata, UnixPex};
|
||||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_fs_explorer_formatter_callchain() {
|
||||
@@ -613,7 +592,7 @@ mod tests {
|
||||
mode: Some(UnixPex::from(0o644)),
|
||||
},
|
||||
};
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
assert_eq!(
|
||||
formatter.fmt(&entry),
|
||||
format!(
|
||||
@@ -621,7 +600,7 @@ mod tests {
|
||||
fmt_time(t, "%b %d %Y %H:%M")
|
||||
)
|
||||
);
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(windows)]
|
||||
assert_eq!(
|
||||
formatter.fmt(&entry),
|
||||
format!(
|
||||
@@ -644,7 +623,7 @@ mod tests {
|
||||
mode: Some(UnixPex::from(0o644)),
|
||||
},
|
||||
};
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
assert_eq!(
|
||||
formatter.fmt(&entry),
|
||||
format!(
|
||||
@@ -652,7 +631,7 @@ mod tests {
|
||||
fmt_time(t, "%b %d %Y %H:%M")
|
||||
)
|
||||
);
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(windows)]
|
||||
assert_eq!(
|
||||
formatter.fmt(&entry),
|
||||
format!(
|
||||
@@ -675,7 +654,7 @@ mod tests {
|
||||
mode: None,
|
||||
},
|
||||
};
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
assert_eq!(
|
||||
formatter.fmt(&entry),
|
||||
format!(
|
||||
@@ -683,7 +662,7 @@ mod tests {
|
||||
fmt_time(t, "%b %d %Y %H:%M")
|
||||
)
|
||||
);
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(windows)]
|
||||
assert_eq!(
|
||||
formatter.fmt(&entry),
|
||||
format!(
|
||||
@@ -706,7 +685,7 @@ mod tests {
|
||||
mode: None,
|
||||
},
|
||||
};
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
assert_eq!(
|
||||
formatter.fmt(&entry),
|
||||
format!(
|
||||
@@ -714,7 +693,7 @@ mod tests {
|
||||
fmt_time(t, "%b %d %Y %H:%M")
|
||||
)
|
||||
);
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(windows)]
|
||||
assert_eq!(
|
||||
formatter.fmt(&entry),
|
||||
format!(
|
||||
@@ -744,7 +723,7 @@ mod tests {
|
||||
mode: Some(UnixPex::from(0o755)),
|
||||
},
|
||||
};
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
assert_eq!(
|
||||
formatter.fmt(&entry),
|
||||
format!(
|
||||
@@ -752,7 +731,7 @@ mod tests {
|
||||
fmt_time(t, "%b %d %Y %H:%M")
|
||||
)
|
||||
);
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(windows)]
|
||||
assert_eq!(
|
||||
formatter.fmt(&entry),
|
||||
format!(
|
||||
@@ -775,7 +754,7 @@ mod tests {
|
||||
mode: None,
|
||||
},
|
||||
};
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
assert_eq!(
|
||||
formatter.fmt(&entry),
|
||||
format!(
|
||||
@@ -783,7 +762,7 @@ mod tests {
|
||||
fmt_time(t, "%b %d %Y %H:%M")
|
||||
)
|
||||
);
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(windows)]
|
||||
assert_eq!(
|
||||
formatter.fmt(&entry),
|
||||
format!(
|
||||
@@ -885,7 +864,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
fn should_fmt_path() {
|
||||
let t: SystemTime = SystemTime::now();
|
||||
let entry = File {
|
||||
@@ -916,6 +895,113 @@ mod tests {
|
||||
assert_eq!(formatter.fmt(&entry).as_str(), "File path: c/bar.txt");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn should_fmt_utf8_path() {
|
||||
let t: SystemTime = SystemTime::now();
|
||||
let entry = File {
|
||||
path: PathBuf::from("/tmp/a/b/c/россия"),
|
||||
metadata: Metadata {
|
||||
accessed: Some(t),
|
||||
created: Some(t),
|
||||
modified: Some(t),
|
||||
file_type: FileType::Symlink,
|
||||
size: 8192,
|
||||
symlink: Some(PathBuf::from("project.info")),
|
||||
uid: None,
|
||||
gid: None,
|
||||
mode: Some(UnixPex::from(0o644)),
|
||||
},
|
||||
};
|
||||
let formatter: Formatter = Formatter::new("File path: {PATH}");
|
||||
assert_eq!(
|
||||
formatter.fmt(&entry).as_str(),
|
||||
"File path: /tmp/a/b/c/россия"
|
||||
);
|
||||
let formatter: Formatter = Formatter::new("File path: {PATH:8}");
|
||||
assert_eq!(formatter.fmt(&entry).as_str(), "File path: /tmp/…/c/россия");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fmt_short_ascii_name() {
|
||||
let entry = File {
|
||||
path: PathBuf::from("/tmp/foo.txt"),
|
||||
metadata: Metadata {
|
||||
accessed: None,
|
||||
created: None,
|
||||
modified: None,
|
||||
file_type: FileType::File,
|
||||
size: 8192,
|
||||
symlink: None,
|
||||
uid: None,
|
||||
gid: None,
|
||||
mode: None,
|
||||
},
|
||||
};
|
||||
let formatter: Formatter = Formatter::new("{NAME:8}");
|
||||
assert_eq!(formatter.fmt(&entry).as_str(), "foo.txt ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fmt_exceeding_length_ascii_name() {
|
||||
let entry = File {
|
||||
path: PathBuf::from("/tmp/christian-visintin.txt"),
|
||||
metadata: Metadata {
|
||||
accessed: None,
|
||||
created: None,
|
||||
modified: None,
|
||||
file_type: FileType::File,
|
||||
size: 8192,
|
||||
symlink: None,
|
||||
uid: None,
|
||||
gid: None,
|
||||
mode: None,
|
||||
},
|
||||
};
|
||||
let formatter: Formatter = Formatter::new("{NAME:8}");
|
||||
assert_eq!(formatter.fmt(&entry).as_str(), "christi…");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fmt_short_utf8_name() {
|
||||
let entry = File {
|
||||
path: PathBuf::from("/tmp/россия"),
|
||||
metadata: Metadata {
|
||||
accessed: None,
|
||||
created: None,
|
||||
modified: None,
|
||||
file_type: FileType::File,
|
||||
size: 8192,
|
||||
symlink: None,
|
||||
uid: None,
|
||||
gid: None,
|
||||
mode: None,
|
||||
},
|
||||
};
|
||||
let formatter: Formatter = Formatter::new("{NAME:8}");
|
||||
assert_eq!(formatter.fmt(&entry).as_str(), "россия ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fmt_long_utf8_name() {
|
||||
let entry = File {
|
||||
path: PathBuf::from("/tmp/喵喵喵喵喵喵喵喵喵喵喵喵喵喵喵喵喵喵喵喵喵喵"),
|
||||
metadata: Metadata {
|
||||
accessed: None,
|
||||
created: None,
|
||||
modified: None,
|
||||
file_type: FileType::File,
|
||||
size: 8192,
|
||||
symlink: None,
|
||||
uid: None,
|
||||
gid: None,
|
||||
mode: None,
|
||||
},
|
||||
};
|
||||
let formatter: Formatter = Formatter::new("{NAME:8}");
|
||||
assert_eq!(formatter.fmt(&entry).as_str(), "喵喵喵喵喵喵喵…");
|
||||
}
|
||||
|
||||
/// Dummy formatter, just yelds an 'A' at the end of the current string
|
||||
fn dummy_fmt(
|
||||
_fmt: &Formatter,
|
||||
@@ -925,6 +1011,6 @@ mod tests {
|
||||
_fmt_len: Option<&usize>,
|
||||
_fmt_extra: Option<&String>,
|
||||
) -> String {
|
||||
format!("{}{}A", cur_str, prefix)
|
||||
format!("{cur_str}{prefix}A")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,53 +2,30 @@
|
||||
//!
|
||||
//! `explorer` is the module which provides an Helper in handling Directory status through
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// Mods
|
||||
pub(crate) mod builder;
|
||||
mod formatter;
|
||||
// Locals
|
||||
use formatter::Formatter;
|
||||
// Ext
|
||||
use remotefs::fs::File;
|
||||
use std::cmp::Reverse;
|
||||
use std::collections::VecDeque;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
|
||||
use formatter::Formatter;
|
||||
// Ext
|
||||
use remotefs::fs::File;
|
||||
|
||||
bitflags! {
|
||||
/// ## ExplorerOpts
|
||||
///
|
||||
/// ExplorerOpts are bit options which provides different behaviours to `FileExplorer`
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct ExplorerOpts: u32 {
|
||||
const SHOW_HIDDEN_FILES = 0b00000001;
|
||||
}
|
||||
}
|
||||
|
||||
/// FileSorting defines the criteria for sorting files
|
||||
#[derive(Copy, Clone, PartialEq, std::fmt::Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, std::fmt::Debug)]
|
||||
pub enum FileSorting {
|
||||
Name,
|
||||
ModifyTime,
|
||||
@@ -57,7 +34,7 @@ pub enum FileSorting {
|
||||
}
|
||||
|
||||
/// GroupDirs defines how directories should be grouped in sorting files
|
||||
#[derive(PartialEq, std::fmt::Debug)]
|
||||
#[derive(PartialEq, Eq, std::fmt::Debug)]
|
||||
pub enum GroupDirs {
|
||||
First,
|
||||
Last,
|
||||
@@ -313,13 +290,14 @@ impl FromStr for GroupDirs {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::utils::fmt::fmt_time;
|
||||
use std::thread::sleep;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use remotefs::fs::{File, FileType, Metadata, UnixPex};
|
||||
use std::thread::sleep;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use super::*;
|
||||
use crate::utils::fmt::fmt_time;
|
||||
|
||||
#[test]
|
||||
fn test_fs_explorer_new() {
|
||||
@@ -341,8 +319,8 @@ mod tests {
|
||||
explorer.stack_size = 2;
|
||||
explorer.dirstack = VecDeque::with_capacity(2);
|
||||
// Push dir
|
||||
explorer.pushd(&Path::new("/tmp"));
|
||||
explorer.pushd(&Path::new("/home/omar"));
|
||||
explorer.pushd(Path::new("/tmp"));
|
||||
explorer.pushd(Path::new("/home/omar"));
|
||||
// Pop
|
||||
assert_eq!(explorer.popd().unwrap(), PathBuf::from("/home/omar"));
|
||||
assert_eq!(explorer.dirstack.len(), 1);
|
||||
@@ -351,9 +329,9 @@ mod tests {
|
||||
// Dirstack is empty now
|
||||
assert!(explorer.popd().is_none());
|
||||
// Exceed limit
|
||||
explorer.pushd(&Path::new("/tmp"));
|
||||
explorer.pushd(&Path::new("/home/omar"));
|
||||
explorer.pushd(&Path::new("/dev"));
|
||||
explorer.pushd(Path::new("/tmp"));
|
||||
explorer.pushd(Path::new("/home/omar"));
|
||||
explorer.pushd(Path::new("/dev"));
|
||||
assert_eq!(explorer.dirstack.len(), 2);
|
||||
assert_eq!(*explorer.dirstack.get(1).unwrap(), PathBuf::from("/dev"));
|
||||
assert_eq!(
|
||||
@@ -535,7 +513,7 @@ mod tests {
|
||||
mode: Some(UnixPex::from(0o644)),
|
||||
},
|
||||
};
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
assert_eq!(
|
||||
explorer.fmt_file(&entry),
|
||||
format!(
|
||||
@@ -543,7 +521,7 @@ mod tests {
|
||||
fmt_time(t, "%b %d %Y %H:%M")
|
||||
)
|
||||
);
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(windows)]
|
||||
assert_eq!(
|
||||
explorer.fmt_file(&entry),
|
||||
format!(
|
||||
|
||||
@@ -2,39 +2,25 @@
|
||||
//!
|
||||
//! Remotefs client builder
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
use super::params::{AwsS3Params, GenericProtocolParams};
|
||||
use super::{FileTransferProtocol, ProtocolParams};
|
||||
use crate::system::config_client::ConfigClient;
|
||||
use crate::system::sshkey_storage::SshKeyStorage;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use remotefs::RemoteFs;
|
||||
use remotefs_aws_s3::AwsS3Fs;
|
||||
use remotefs_ftp::FtpFs;
|
||||
use remotefs_ssh::{ScpFs, SftpFs, SshOpts};
|
||||
use std::path::PathBuf;
|
||||
#[cfg(smb_unix)]
|
||||
use remotefs_smb::SmbOptions;
|
||||
#[cfg(smb)]
|
||||
use remotefs_smb::{SmbCredentials, SmbFs};
|
||||
use remotefs_ssh::{ScpFs, SftpFs, SshConfigParseRule, SshOpts};
|
||||
|
||||
#[cfg(not(smb))]
|
||||
use super::params::{AwsS3Params, GenericProtocolParams};
|
||||
#[cfg(smb)]
|
||||
use super::params::{AwsS3Params, GenericProtocolParams, SmbParams};
|
||||
use super::{FileTransferProtocol, ProtocolParams};
|
||||
use crate::system::config_client::ConfigClient;
|
||||
use crate::system::sshkey_storage::SshKeyStorage;
|
||||
use crate::utils::ssh as ssh_utils;
|
||||
|
||||
/// Remotefs builder
|
||||
pub struct Builder;
|
||||
@@ -61,12 +47,13 @@ impl Builder {
|
||||
(FileTransferProtocol::Sftp, ProtocolParams::Generic(params)) => {
|
||||
Box::new(Self::sftp_client(params, config_client))
|
||||
}
|
||||
#[cfg(smb)]
|
||||
(FileTransferProtocol::Smb, ProtocolParams::Smb(params)) => {
|
||||
Box::new(Self::smb_client(params))
|
||||
}
|
||||
(protocol, params) => {
|
||||
error!("Invalid params for protocol '{:?}'", protocol);
|
||||
panic!(
|
||||
"Invalid protocol '{:?}' with parameters of type {:?}",
|
||||
protocol, params
|
||||
)
|
||||
panic!("Invalid protocol '{protocol:?}' with parameters of type {params:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,19 +110,85 @@ impl Builder {
|
||||
Self::build_ssh_opts(params, config_client).into()
|
||||
}
|
||||
|
||||
#[cfg(smb_unix)]
|
||||
fn smb_client(params: SmbParams) -> SmbFs {
|
||||
let mut credentials = SmbCredentials::default()
|
||||
.server(format!("smb://{}:{}", params.address, params.port))
|
||||
.share(params.share);
|
||||
|
||||
if let Some(username) = params.username {
|
||||
credentials = credentials.username(username);
|
||||
}
|
||||
if let Some(password) = params.password {
|
||||
credentials = credentials.password(password);
|
||||
}
|
||||
if let Some(workgroup) = params.workgroup {
|
||||
credentials = credentials.workgroup(workgroup);
|
||||
}
|
||||
|
||||
match SmbFs::try_new(
|
||||
credentials,
|
||||
SmbOptions::default()
|
||||
.one_share_per_server(true)
|
||||
.case_sensitive(false),
|
||||
) {
|
||||
Ok(fs) => fs,
|
||||
Err(e) => {
|
||||
error!("Invalid params for protocol SMB: {e}");
|
||||
panic!("Invalid params for protocol SMB: {e}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn smb_client(params: SmbParams) -> SmbFs {
|
||||
let mut credentials = SmbCredentials::new(params.address, params.share);
|
||||
|
||||
if let Some(username) = params.username {
|
||||
credentials = credentials.username(username);
|
||||
}
|
||||
if let Some(password) = params.password {
|
||||
credentials = credentials.password(password);
|
||||
}
|
||||
|
||||
SmbFs::new(credentials)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
let mut opts = SshOpts::new(params.address.clone())
|
||||
.key_storage(Box::new(Self::make_ssh_storage(config_client)))
|
||||
.port(params.port);
|
||||
//* get username. Case 1 provided in params
|
||||
if let Some(username) = params.username {
|
||||
opts = opts.username(username);
|
||||
} else if let Some(ssh_config) = config_client.get_ssh_config().and_then(|x| {
|
||||
//* case 2: found in ssh2 config
|
||||
debug!("reading ssh config at {}", x);
|
||||
ssh_utils::parse_ssh2_config(x).ok()
|
||||
}) {
|
||||
debug!("no username was provided, checking whether a user is set for this host");
|
||||
if let Some(username) = ssh_config.query(¶ms.address).user {
|
||||
debug!("found username from config: {username}");
|
||||
opts = opts.username(username);
|
||||
} else {
|
||||
//* case 3: use system username; can't be None
|
||||
debug!("no username was provided, using current username");
|
||||
opts = opts.username(whoami::username());
|
||||
}
|
||||
} else {
|
||||
//* case 3: use system username; can't be None
|
||||
debug!("no username was provided, using current username");
|
||||
opts = opts.username(whoami::username());
|
||||
}
|
||||
if let Some(password) = params.password {
|
||||
opts = opts.password(password);
|
||||
}
|
||||
if let Some(config_path) = config_client.get_ssh_config() {
|
||||
opts = opts.config_file(PathBuf::from(config_path));
|
||||
opts = opts.config_file(
|
||||
PathBuf::from(config_path),
|
||||
SshConfigParseRule::ALLOW_UNKNOWN_FIELDS,
|
||||
);
|
||||
}
|
||||
opts
|
||||
}
|
||||
@@ -149,11 +202,12 @@ impl Builder {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use tempfile::TempDir;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_build_aws_s3_fs() {
|
||||
let params = ProtocolParams::AwsS3(
|
||||
@@ -208,6 +262,14 @@ mod test {
|
||||
let _ = Builder::build(FileTransferProtocol::Sftp, params, &config_client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(smb)]
|
||||
fn should_build_smb_fs() {
|
||||
let params = ProtocolParams::Smb(SmbParams::new("localhost", "share"));
|
||||
let config_client = get_config_client();
|
||||
let _ = Builder::build(FileTransferProtocol::Smb, params, &config_client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn should_not_build_fs() {
|
||||
|
||||
@@ -2,29 +2,6 @@
|
||||
//!
|
||||
//! `filetransfer` is the module which provides the file transfer protocols and remotefs builders
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
mod builder;
|
||||
pub mod params;
|
||||
|
||||
@@ -34,12 +11,13 @@ pub use params::{FileTransferParams, ProtocolParams};
|
||||
|
||||
/// This enum defines the different transfer protocol available in termscp
|
||||
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub enum FileTransferProtocol {
|
||||
Sftp,
|
||||
Scp,
|
||||
Ftp(bool), // Bool is for secure (true => ftps)
|
||||
AwsS3,
|
||||
Ftp(bool), // Bool is for secure (true => ftps)
|
||||
Scp,
|
||||
Sftp,
|
||||
Smb,
|
||||
}
|
||||
|
||||
// Traits
|
||||
@@ -47,13 +25,14 @@ pub enum FileTransferProtocol {
|
||||
impl std::string::ToString for FileTransferProtocol {
|
||||
fn to_string(&self) -> String {
|
||||
String::from(match self {
|
||||
FileTransferProtocol::AwsS3 => "S3",
|
||||
FileTransferProtocol::Ftp(secure) => match secure {
|
||||
true => "FTPS",
|
||||
false => "FTP",
|
||||
},
|
||||
FileTransferProtocol::Scp => "SCP",
|
||||
FileTransferProtocol::Sftp => "SFTP",
|
||||
FileTransferProtocol::AwsS3 => "S3",
|
||||
FileTransferProtocol::Smb => "SMB",
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -64,9 +43,10 @@ impl std::str::FromStr for FileTransferProtocol {
|
||||
match s.to_ascii_uppercase().as_str() {
|
||||
"FTP" => Ok(FileTransferProtocol::Ftp(false)),
|
||||
"FTPS" => Ok(FileTransferProtocol::Ftp(true)),
|
||||
"S3" => Ok(FileTransferProtocol::AwsS3),
|
||||
"SCP" => Ok(FileTransferProtocol::Scp),
|
||||
"SFTP" => Ok(FileTransferProtocol::Sftp),
|
||||
"S3" => Ok(FileTransferProtocol::AwsS3),
|
||||
"SMB" => Ok(FileTransferProtocol::Smb),
|
||||
_ => Err(s.to_string()),
|
||||
}
|
||||
}
|
||||
@@ -77,12 +57,13 @@ impl std::str::FromStr for FileTransferProtocol {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_filetransfer_mod_protocol() {
|
||||
assert_eq!(
|
||||
@@ -126,6 +107,14 @@ mod tests {
|
||||
FileTransferProtocol::from_str("scp").ok().unwrap(),
|
||||
FileTransferProtocol::Scp
|
||||
);
|
||||
assert_eq!(
|
||||
FileTransferProtocol::from_str("SMB").ok().unwrap(),
|
||||
FileTransferProtocol::Smb
|
||||
);
|
||||
assert_eq!(
|
||||
FileTransferProtocol::from_str("smb").ok().unwrap(),
|
||||
FileTransferProtocol::Smb
|
||||
);
|
||||
assert_eq!(
|
||||
FileTransferProtocol::from_str("S3").ok().unwrap(),
|
||||
FileTransferProtocol::AwsS3
|
||||
@@ -148,5 +137,6 @@ mod tests {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,41 +2,17 @@
|
||||
//!
|
||||
//! file transfer parameters
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
use super::FileTransferProtocol;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// ### FileTransferParams
|
||||
///
|
||||
use super::FileTransferProtocol;
|
||||
|
||||
/// Holds connection parameters for file transfers
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FileTransferParams {
|
||||
pub protocol: FileTransferProtocol,
|
||||
pub params: ProtocolParams,
|
||||
pub entry_directory: Option<PathBuf>,
|
||||
pub remote_path: Option<PathBuf>,
|
||||
pub local_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
/// Container for protocol params
|
||||
@@ -44,6 +20,7 @@ pub struct FileTransferParams {
|
||||
pub enum ProtocolParams {
|
||||
Generic(GenericProtocolParams),
|
||||
AwsS3(AwsS3Params),
|
||||
Smb(SmbParams),
|
||||
}
|
||||
|
||||
/// Protocol params used by most common protocols
|
||||
@@ -69,21 +46,60 @@ pub struct AwsS3Params {
|
||||
pub new_path_style: bool,
|
||||
}
|
||||
|
||||
/// Connection parameters for SMB protocol
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SmbParams {
|
||||
pub address: String,
|
||||
#[cfg(unix)]
|
||||
pub port: u16,
|
||||
pub share: String,
|
||||
pub username: Option<String>,
|
||||
pub password: Option<String>,
|
||||
#[cfg(unix)]
|
||||
pub workgroup: Option<String>,
|
||||
}
|
||||
|
||||
impl FileTransferParams {
|
||||
/// Instantiates a new `FileTransferParams`
|
||||
pub fn new(protocol: FileTransferProtocol, params: ProtocolParams) -> Self {
|
||||
Self {
|
||||
protocol,
|
||||
params,
|
||||
entry_directory: None,
|
||||
remote_path: None,
|
||||
local_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set entry directory
|
||||
pub fn entry_directory<P: AsRef<Path>>(mut self, dir: Option<P>) -> Self {
|
||||
self.entry_directory = dir.map(|x| x.as_ref().to_path_buf());
|
||||
/// Set remote directory
|
||||
pub fn remote_path<P: AsRef<Path>>(mut self, dir: Option<P>) -> Self {
|
||||
self.remote_path = dir.map(|x| x.as_ref().to_path_buf());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set local directory
|
||||
pub fn local_path<P: AsRef<Path>>(mut self, dir: Option<P>) -> Self {
|
||||
self.local_path = dir.map(|x| x.as_ref().to_path_buf());
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns whether a password is supposed to be required for this protocol params.
|
||||
/// The result true is returned ONLY if the supposed secret is MISSING!!!
|
||||
pub fn password_missing(&self) -> bool {
|
||||
match &self.params {
|
||||
ProtocolParams::AwsS3(params) => params.password_missing(),
|
||||
ProtocolParams::Generic(params) => params.password_missing(),
|
||||
ProtocolParams::Smb(params) => params.password_missing(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the secret to ft params for the default secret field for this protocol
|
||||
pub fn set_default_secret(&mut self, secret: String) {
|
||||
match &mut self.params {
|
||||
ProtocolParams::AwsS3(params) => params.set_default_secret(secret),
|
||||
ProtocolParams::Generic(params) => params.set_default_secret(secret),
|
||||
ProtocolParams::Smb(params) => params.set_default_secret(secret),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FileTransferParams {
|
||||
@@ -99,7 +115,6 @@ impl Default for ProtocolParams {
|
||||
}
|
||||
|
||||
impl ProtocolParams {
|
||||
#[cfg(test)]
|
||||
/// Retrieve generic parameters from protocol params if any
|
||||
pub fn generic_params(&self) -> Option<&GenericProtocolParams> {
|
||||
match self {
|
||||
@@ -108,6 +123,7 @@ impl ProtocolParams {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Get a mutable reference to the inner generic protocol params
|
||||
pub fn mut_generic_params(&mut self) -> Option<&mut GenericProtocolParams> {
|
||||
match self {
|
||||
@@ -124,6 +140,15 @@ impl ProtocolParams {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Retrieve SMB parameters if any
|
||||
pub fn smb_params(&self) -> Option<&SmbParams> {
|
||||
match self {
|
||||
ProtocolParams::Smb(params) => Some(params),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Generic protocol params
|
||||
@@ -163,6 +188,17 @@ impl GenericProtocolParams {
|
||||
self.password = password.map(|x| x.as_ref().to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns whether a password is supposed to be required for this protocol params.
|
||||
/// The result true is returned ONLY if the supposed secret is MISSING!!!
|
||||
pub fn password_missing(&self) -> bool {
|
||||
self.password.is_none()
|
||||
}
|
||||
|
||||
/// Set password
|
||||
pub fn set_default_secret(&mut self, secret: String) {
|
||||
self.password = Some(secret);
|
||||
}
|
||||
}
|
||||
|
||||
// -- S3 params
|
||||
@@ -218,28 +254,94 @@ impl AwsS3Params {
|
||||
self.new_path_style = new_path_style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns whether a password is supposed to be required for this protocol params.
|
||||
/// The result true is returned ONLY if the supposed secret is MISSING!!!
|
||||
pub fn password_missing(&self) -> bool {
|
||||
self.secret_access_key.is_none() && self.security_token.is_none()
|
||||
}
|
||||
|
||||
/// Set password
|
||||
pub fn set_default_secret(&mut self, secret: String) {
|
||||
self.secret_access_key = Some(secret);
|
||||
}
|
||||
}
|
||||
|
||||
// -- SMB params
|
||||
|
||||
impl SmbParams {
|
||||
/// Instantiates a new `AwsS3Params` struct
|
||||
pub fn new<S: AsRef<str>>(address: S, share: S) -> Self {
|
||||
Self {
|
||||
address: address.as_ref().to_string(),
|
||||
#[cfg(unix)]
|
||||
port: 445,
|
||||
share: share.as_ref().to_string(),
|
||||
username: None,
|
||||
password: None,
|
||||
#[cfg(unix)]
|
||||
workgroup: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn port(mut self, port: u16) -> Self {
|
||||
self.port = port;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn username(mut self, username: Option<impl ToString>) -> Self {
|
||||
self.username = username.map(|x| x.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn password(mut self, password: Option<impl ToString>) -> Self {
|
||||
self.password = password.map(|x| x.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn workgroup(mut self, workgroup: Option<impl ToString>) -> Self {
|
||||
self.workgroup = workgroup.map(|x| x.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns whether a password is supposed to be required for this protocol params.
|
||||
/// The result true is returned ONLY if the supposed secret is MISSING!!!
|
||||
pub fn password_missing(&self) -> bool {
|
||||
self.password.is_none()
|
||||
}
|
||||
|
||||
/// Set password
|
||||
#[cfg(unix)]
|
||||
pub fn set_default_secret(&mut self, secret: String) {
|
||||
self.password = Some(secret);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn set_default_secret(&mut self, _secret: String) {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_filetransfer_params() {
|
||||
let params: FileTransferParams =
|
||||
FileTransferParams::new(FileTransferProtocol::Scp, ProtocolParams::default())
|
||||
.entry_directory(Some(&Path::new("/tmp")));
|
||||
.remote_path(Some(&Path::new("/tmp")))
|
||||
.local_path(Some(&Path::new("/usr")));
|
||||
assert_eq!(
|
||||
params.params.generic_params().unwrap().address.as_str(),
|
||||
"localhost"
|
||||
);
|
||||
assert_eq!(params.protocol, FileTransferProtocol::Scp);
|
||||
assert_eq!(
|
||||
params.entry_directory.as_deref().unwrap(),
|
||||
Path::new("/tmp")
|
||||
);
|
||||
assert_eq!(params.remote_path.as_deref().unwrap(), Path::new("/tmp"));
|
||||
assert_eq!(params.local_path.as_deref().unwrap(), Path::new("/usr"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -288,6 +390,53 @@ mod test {
|
||||
assert_eq!(params.new_path_style, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_init_smb_params() {
|
||||
let params = SmbParams::new("localhost", "temp");
|
||||
assert_eq!(¶ms.address, "localhost");
|
||||
|
||||
#[cfg(unix)]
|
||||
assert_eq!(params.port, 445);
|
||||
assert_eq!(¶ms.share, "temp");
|
||||
|
||||
#[cfg(unix)]
|
||||
assert!(params.username.is_none());
|
||||
#[cfg(unix)]
|
||||
assert!(params.password.is_none());
|
||||
#[cfg(unix)]
|
||||
assert!(params.workgroup.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn should_init_smb_params_with_optionals() {
|
||||
let params = SmbParams::new("localhost", "temp")
|
||||
.port(3456)
|
||||
.username(Some("foo"))
|
||||
.password(Some("bar"))
|
||||
.workgroup(Some("baz"));
|
||||
|
||||
assert_eq!(¶ms.address, "localhost");
|
||||
assert_eq!(params.port, 3456);
|
||||
assert_eq!(¶ms.share, "temp");
|
||||
assert_eq!(params.username.as_deref().unwrap(), "foo");
|
||||
assert_eq!(params.password.as_deref().unwrap(), "bar");
|
||||
assert_eq!(params.workgroup.as_deref().unwrap(), "baz");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn should_init_smb_params_with_optionals() {
|
||||
let params = SmbParams::new("localhost", "temp")
|
||||
.username(Some("foo"))
|
||||
.password(Some("bar"));
|
||||
|
||||
assert_eq!(¶ms.address, "localhost");
|
||||
assert_eq!(¶ms.share, "temp");
|
||||
assert_eq!(params.username.as_deref().unwrap(), "foo");
|
||||
assert_eq!(params.password.as_deref().unwrap(), "bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references() {
|
||||
let mut params =
|
||||
@@ -300,4 +449,83 @@ mod test {
|
||||
assert!(params.generic_params().is_some());
|
||||
assert!(params.mut_generic_params().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn password_missing() {
|
||||
assert!(FileTransferParams::new(
|
||||
FileTransferProtocol::Scp,
|
||||
ProtocolParams::AwsS3(AwsS3Params::new("omar", Some("eu-west-1"), Some("test")))
|
||||
)
|
||||
.password_missing());
|
||||
assert_eq!(
|
||||
FileTransferParams::new(
|
||||
FileTransferProtocol::Scp,
|
||||
ProtocolParams::AwsS3(
|
||||
AwsS3Params::new("omar", Some("eu-west-1"), Some("test"))
|
||||
.secret_access_key(Some("test"))
|
||||
)
|
||||
)
|
||||
.password_missing(),
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
FileTransferParams::new(
|
||||
FileTransferProtocol::Scp,
|
||||
ProtocolParams::AwsS3(
|
||||
AwsS3Params::new("omar", Some("eu-west-1"), Some("test"))
|
||||
.security_token(Some("test"))
|
||||
)
|
||||
)
|
||||
.password_missing(),
|
||||
false
|
||||
);
|
||||
assert!(
|
||||
FileTransferParams::new(FileTransferProtocol::Scp, ProtocolParams::default())
|
||||
.password_missing()
|
||||
);
|
||||
assert_eq!(
|
||||
FileTransferParams::new(
|
||||
FileTransferProtocol::Scp,
|
||||
ProtocolParams::Generic(GenericProtocolParams::default().password(Some("Hello")))
|
||||
)
|
||||
.password_missing(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_default_secret_aws_s3() {
|
||||
let mut params = FileTransferParams::new(
|
||||
FileTransferProtocol::Scp,
|
||||
ProtocolParams::AwsS3(AwsS3Params::new("omar", Some("eu-west-1"), Some("test"))),
|
||||
);
|
||||
params.set_default_secret(String::from("secret"));
|
||||
assert_eq!(
|
||||
params
|
||||
.params
|
||||
.s3_params()
|
||||
.unwrap()
|
||||
.secret_access_key
|
||||
.as_deref()
|
||||
.unwrap(),
|
||||
"secret"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_default_secret_generic() {
|
||||
let mut params =
|
||||
FileTransferParams::new(FileTransferProtocol::Scp, ProtocolParams::default());
|
||||
params.set_default_secret(String::from("secret"));
|
||||
assert_eq!(
|
||||
params
|
||||
.params
|
||||
.generic_params()
|
||||
.unwrap()
|
||||
.password
|
||||
.as_deref()
|
||||
.unwrap(),
|
||||
"secret"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
171
src/host/mod.rs
@@ -2,42 +2,21 @@
|
||||
//!
|
||||
//! `host` is the module which provides functionalities to host file system
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// ext
|
||||
#[cfg(target_family = "unix")]
|
||||
// Metadata ext
|
||||
#[cfg(unix)]
|
||||
use std::fs::set_permissions;
|
||||
use std::fs::{self, File as StdFile, OpenOptions};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use filetime::{self, FileTime};
|
||||
#[cfg(unix)]
|
||||
use remotefs::fs::UnixPex;
|
||||
use remotefs::fs::{File, FileType, Metadata};
|
||||
use std::fs::{self, File as StdFile, OpenOptions};
|
||||
use std::path::{Path, PathBuf};
|
||||
use thiserror::Error;
|
||||
use wildmatch::WildMatch;
|
||||
// Metadata ext
|
||||
#[cfg(target_family = "unix")]
|
||||
use std::fs::set_permissions;
|
||||
#[cfg(target_family = "unix")]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
// Locals
|
||||
use crate::utils::path;
|
||||
@@ -63,8 +42,6 @@ pub enum HostErrorType {
|
||||
DeleteFailed,
|
||||
}
|
||||
|
||||
/// ### HostError
|
||||
///
|
||||
/// HostError is a wrapper for the error type and the exact io error
|
||||
#[derive(Debug)]
|
||||
pub struct HostError {
|
||||
@@ -429,6 +406,27 @@ impl Localhost {
|
||||
Ok(File { path, metadata })
|
||||
}
|
||||
|
||||
/// Set file stat
|
||||
pub fn setstat(&self, path: &Path, metadata: &Metadata) -> Result<(), HostError> {
|
||||
debug!("Setting stat for file at {}", path.display());
|
||||
if let Some(mtime) = metadata.modified {
|
||||
let mtime = FileTime::from_system_time(mtime);
|
||||
debug!("setting mtime {:?}", mtime);
|
||||
filetime::set_file_mtime(path, mtime)
|
||||
.map_err(|e| HostError::new(HostErrorType::FileNotAccessible, Some(e), path))?;
|
||||
}
|
||||
if let Some(atime) = metadata.accessed {
|
||||
let atime = FileTime::from_system_time(atime);
|
||||
filetime::set_file_atime(path, atime)
|
||||
.map_err(|e| HostError::new(HostErrorType::FileNotAccessible, Some(e), path))?;
|
||||
}
|
||||
#[cfg(unix)]
|
||||
if let Some(mode) = metadata.mode {
|
||||
self.chmod(path, mode)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Execute a command on localhost
|
||||
pub fn exec(&self, cmd: &str) -> Result<String, HostError> {
|
||||
// Make command
|
||||
@@ -456,7 +454,7 @@ impl Localhost {
|
||||
}
|
||||
|
||||
/// Change file mode to file, according to UNIX permissions
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
pub fn chmod(&self, path: &Path, pex: UnixPex) -> Result<(), HostError> {
|
||||
let path: PathBuf = self.to_path(path);
|
||||
// Get metadta
|
||||
@@ -588,7 +586,7 @@ impl Localhost {
|
||||
}
|
||||
|
||||
/// Create a symlink at path pointing at target
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
pub fn symlink(&self, path: &Path, target: &Path) -> Result<(), HostError> {
|
||||
let path = self.to_path(path);
|
||||
std::os::unix::fs::symlink(target, path.as_path()).map_err(|e| {
|
||||
@@ -646,19 +644,21 @@ impl Localhost {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
#[cfg(target_family = "unix")]
|
||||
use crate::utils::test_helpers::{create_sample_file, make_fsentry};
|
||||
use crate::utils::test_helpers::{make_dir_at, make_file_at};
|
||||
#[cfg(unix)]
|
||||
use std::fs::File as StdFile;
|
||||
#[cfg(unix)]
|
||||
use std::io::Write;
|
||||
use std::ops::AddAssign;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::{symlink, PermissionsExt};
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
#[cfg(target_family = "unix")]
|
||||
use std::fs::File as StdFile;
|
||||
#[cfg(target_family = "unix")]
|
||||
use std::io::Write;
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
use std::os::unix::fs::{symlink, PermissionsExt};
|
||||
use super::*;
|
||||
#[cfg(unix)]
|
||||
use crate::utils::test_helpers::make_fsentry;
|
||||
use crate::utils::test_helpers::{create_sample_file, make_dir_at, make_file_at};
|
||||
|
||||
#[test]
|
||||
fn test_host_error_new() {
|
||||
@@ -669,7 +669,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
fn test_host_localhost_new() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/dev")).ok().unwrap();
|
||||
assert_eq!(host.wrkdir, PathBuf::from("/dev"));
|
||||
@@ -677,13 +677,13 @@ mod tests {
|
||||
let entries = std::fs::read_dir(PathBuf::from("/dev").as_path()).unwrap();
|
||||
let mut counter: usize = 0;
|
||||
for _ in entries {
|
||||
counter = counter + 1;
|
||||
counter += 1;
|
||||
}
|
||||
assert_eq!(host.files.len(), counter);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(windows)]
|
||||
fn test_host_localhost_new() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("C:\\users")).ok().unwrap();
|
||||
assert_eq!(host.wrkdir, PathBuf::from("C:\\users"));
|
||||
@@ -705,43 +705,43 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
fn test_host_localhost_pwd() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/dev")).ok().unwrap();
|
||||
assert_eq!(host.pwd(), PathBuf::from("/dev"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
fn test_host_localhost_list_files() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/dev")).ok().unwrap();
|
||||
// Scan dir
|
||||
let entries = std::fs::read_dir(PathBuf::from("/dev").as_path()).unwrap();
|
||||
let mut counter: usize = 0;
|
||||
for _ in entries {
|
||||
counter = counter + 1;
|
||||
counter += 1;
|
||||
}
|
||||
assert_eq!(host.list_dir().len(), counter);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
fn test_host_localhost_change_dir() {
|
||||
let mut host: Localhost = Localhost::new(PathBuf::from("/dev")).ok().unwrap();
|
||||
let new_dir: PathBuf = PathBuf::from("/dev");
|
||||
assert!(host.change_wrkdir(new_dir.as_path()).is_ok());
|
||||
// Verify new files
|
||||
// Scan dir
|
||||
let entries = std::fs::read_dir(PathBuf::from(new_dir).as_path()).unwrap();
|
||||
let entries = std::fs::read_dir(new_dir.as_path()).unwrap();
|
||||
let mut counter: usize = 0;
|
||||
for _ in entries {
|
||||
counter = counter + 1;
|
||||
counter += 1;
|
||||
}
|
||||
assert_eq!(host.files.len(), counter);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
#[should_panic]
|
||||
fn test_host_localhost_change_dir_failed() {
|
||||
let mut host: Localhost = Localhost::new(PathBuf::from("/dev")).ok().unwrap();
|
||||
@@ -750,7 +750,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
fn test_host_localhost_open_read() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/dev")).ok().unwrap();
|
||||
// Create temp file
|
||||
@@ -759,7 +759,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
#[should_panic]
|
||||
fn test_host_localhost_open_read_err_no_such_file() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/dev")).ok().unwrap();
|
||||
@@ -780,7 +780,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
fn test_host_localhost_open_write() {
|
||||
let host: Localhost = Localhost::new(PathBuf::from("/dev")).ok().unwrap();
|
||||
// Create temp file
|
||||
@@ -799,7 +799,7 @@ mod tests {
|
||||
assert!(host.open_file_write(file.path()).is_err());
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_host_localhost_symlinks() {
|
||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
@@ -816,7 +816,7 @@ mod tests {
|
||||
let files: Vec<File> = host.list_dir();
|
||||
// Verify files
|
||||
let file_0: &File = files.get(0).unwrap();
|
||||
if file_0.name() == String::from("foo.txt") {
|
||||
if file_0.name() == *"foo.txt" {
|
||||
assert!(file_0.metadata.symlink.is_none());
|
||||
} else {
|
||||
assert_eq!(
|
||||
@@ -826,7 +826,7 @@ mod tests {
|
||||
}
|
||||
// Verify simlink
|
||||
let file_1: &File = files.get(1).unwrap();
|
||||
if file_1.name() == String::from("bar.txt") {
|
||||
if file_1.name() == *"bar.txt" {
|
||||
assert_eq!(
|
||||
file_1.metadata.symlink.as_ref().unwrap(),
|
||||
&PathBuf::from(format!("{}/foo.txt", tmpdir.path().display()))
|
||||
@@ -837,7 +837,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
fn test_host_localhost_mkdir() {
|
||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
let mut host: Localhost = Localhost::new(PathBuf::from(tmpdir.path())).ok().unwrap();
|
||||
@@ -862,7 +862,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
fn test_host_localhost_remove() {
|
||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
// Create sample file
|
||||
@@ -891,7 +891,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
fn test_host_localhost_rename() {
|
||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
// Create sample file
|
||||
@@ -919,7 +919,32 @@ mod tests {
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
#[test]
|
||||
fn should_setstat() {
|
||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
let file: tempfile::NamedTempFile = create_sample_file();
|
||||
let host: Localhost = Localhost::new(PathBuf::from(tmpdir.path())).ok().unwrap();
|
||||
// stat
|
||||
let mut filemeta = host.stat(file.path()).unwrap();
|
||||
|
||||
let mut new_atime = SystemTime::UNIX_EPOCH;
|
||||
new_atime.add_assign(Duration::from_secs(1612164210));
|
||||
|
||||
let mut new_mtime = SystemTime::UNIX_EPOCH;
|
||||
new_mtime.add_assign(Duration::from_secs(1613160210));
|
||||
|
||||
filemeta.metadata.accessed = Some(new_atime);
|
||||
filemeta.metadata.modified = Some(new_mtime);
|
||||
|
||||
// setstat
|
||||
assert!(host.setstat(filemeta.path(), filemeta.metadata()).is_ok());
|
||||
let new_metadata = host.stat(file.path()).unwrap();
|
||||
|
||||
assert_eq!(new_metadata.metadata().accessed, Some(new_atime));
|
||||
assert_eq!(new_metadata.metadata().modified, Some(new_mtime));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_host_chmod() {
|
||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
@@ -938,7 +963,7 @@ mod tests {
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_host_copy_file_absolute() {
|
||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
@@ -968,7 +993,7 @@ mod tests {
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_host_copy_file_relative() {
|
||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
@@ -990,7 +1015,7 @@ mod tests {
|
||||
assert_eq!(host.files.len(), 2);
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_host_copy_directory_absolute() {
|
||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
@@ -1021,7 +1046,7 @@ mod tests {
|
||||
assert!(host.stat(test_file_path.as_path()).is_ok());
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_host_copy_directory_relative() {
|
||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
@@ -1056,9 +1081,9 @@ mod tests {
|
||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
let host: Localhost = Localhost::new(PathBuf::from(tmpdir.path())).ok().unwrap();
|
||||
// Execute
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
assert_eq!(host.exec("echo 5").ok().unwrap().as_str(), "5\n");
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(windows)]
|
||||
assert_eq!(host.exec("echo 5").ok().unwrap().as_str(), "5\r\n");
|
||||
}
|
||||
|
||||
@@ -1095,7 +1120,7 @@ mod tests {
|
||||
assert_eq!(result[1].name(), "examples.csv");
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn should_create_symlink() {
|
||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
@@ -1122,7 +1147,7 @@ mod tests {
|
||||
Path::new("/tmp"),
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", err),
|
||||
format!("{err}"),
|
||||
String::from("Could not create file: address in use (/tmp)"),
|
||||
);
|
||||
assert_eq!(
|
||||
|
||||
300
src/main.rs
@@ -1,35 +1,12 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
const TERMSCP_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
const TERMSCP_AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
||||
|
||||
// Crates
|
||||
extern crate argh;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
#[macro_use]
|
||||
extern crate lazy_regex;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
@@ -37,13 +14,13 @@ extern crate log;
|
||||
extern crate magic_crypt;
|
||||
|
||||
// External libs
|
||||
use argh::FromArgs;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
// Include
|
||||
mod activity_manager;
|
||||
mod cli_opts;
|
||||
mod config;
|
||||
mod explorer;
|
||||
mod filetransfer;
|
||||
@@ -55,108 +32,33 @@ mod utils;
|
||||
|
||||
// namespaces
|
||||
use activity_manager::{ActivityManager, NextActivity};
|
||||
use cli_opts::{Args, BookmarkParams, HostParams, Remote, RunOpts, Task};
|
||||
use filetransfer::FileTransferParams;
|
||||
use system::logging::{self, LogLevel};
|
||||
|
||||
enum Task {
|
||||
Activity(NextActivity),
|
||||
ImportTheme(PathBuf),
|
||||
InstallUpdate,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(description = "
|
||||
where positional can be: [address] [local-wrkdir]
|
||||
|
||||
Address syntax can be:
|
||||
|
||||
- `protocol://user@address:port:wrkdir` for protocols such as Sftp, Scp, Ftp
|
||||
- `s3://bucket-name@region:profile:/wrkdir` for Aws S3 protocol
|
||||
|
||||
Please, report issues to <https://github.com/veeso/termscp>
|
||||
Please, consider supporting the author <https://ko-fi.com/veeso>")]
|
||||
struct Args {
|
||||
#[argh(switch, short = 'c', description = "open termscp configuration")]
|
||||
config: bool,
|
||||
#[argh(switch, short = 'D', description = "enable TRACE log level")]
|
||||
debug: bool,
|
||||
#[argh(option, short = 'P', description = "provide password from CLI")]
|
||||
password: Option<String>,
|
||||
#[argh(switch, short = 'q', description = "disable logging")]
|
||||
quiet: bool,
|
||||
#[argh(option, short = 't', description = "import specified theme")]
|
||||
theme: Option<String>,
|
||||
#[argh(
|
||||
switch,
|
||||
short = 'u',
|
||||
description = "update termscp to the latest version"
|
||||
)]
|
||||
update: bool,
|
||||
#[argh(
|
||||
option,
|
||||
short = 'T',
|
||||
default = "10",
|
||||
description = "set UI ticks; default 10ms"
|
||||
)]
|
||||
ticks: u64,
|
||||
#[argh(switch, short = 'v', description = "print version")]
|
||||
version: bool,
|
||||
// -- positional
|
||||
#[argh(
|
||||
positional,
|
||||
description = "protocol://user@address:port:wrkdir local-wrkdir"
|
||||
)]
|
||||
positional: Vec<String>,
|
||||
}
|
||||
|
||||
struct RunOpts {
|
||||
remote: Option<FileTransferParams>,
|
||||
ticks: Duration,
|
||||
log_level: LogLevel,
|
||||
task: Task,
|
||||
}
|
||||
|
||||
impl Default for RunOpts {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
remote: None,
|
||||
ticks: Duration::from_millis(10),
|
||||
log_level: LogLevel::Info,
|
||||
task: Task::Activity(NextActivity::Authentication),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Args = argh::from_env();
|
||||
// Parse args
|
||||
let mut run_opts: RunOpts = match parse_args(args) {
|
||||
let run_opts: RunOpts = match parse_args(args) {
|
||||
Ok(opts) => opts,
|
||||
Err(err) => {
|
||||
eprintln!("{}", err);
|
||||
eprintln!("{err}");
|
||||
std::process::exit(255);
|
||||
}
|
||||
};
|
||||
// Setup logging
|
||||
if let Err(err) = logging::init(run_opts.log_level) {
|
||||
eprintln!("Failed to initialize logging: {}", err);
|
||||
}
|
||||
// Read password from remote
|
||||
if let Err(err) = read_password(&mut run_opts) {
|
||||
eprintln!("{}", err);
|
||||
std::process::exit(255);
|
||||
eprintln!("Failed to initialize logging: {err}");
|
||||
}
|
||||
info!("termscp {} started!", TERMSCP_VERSION);
|
||||
// Run
|
||||
info!("Starting activity manager...");
|
||||
let rc: i32 = run(run_opts);
|
||||
info!("termscp terminated");
|
||||
let rc = run(run_opts);
|
||||
info!("termscp terminated with exitcode {}", rc);
|
||||
// Then return
|
||||
std::process::exit(rc);
|
||||
}
|
||||
|
||||
/// ### parse_args
|
||||
///
|
||||
/// Parse arguments
|
||||
/// In case of success returns `RunOpts`
|
||||
/// in case something is wrong returns the error message
|
||||
@@ -165,8 +67,7 @@ fn parse_args(args: Args) -> Result<RunOpts, String> {
|
||||
// Version
|
||||
if args.version {
|
||||
return Err(format!(
|
||||
"termscp - {} - Developed by {}",
|
||||
TERMSCP_VERSION, TERMSCP_AUTHORS,
|
||||
"termscp - {TERMSCP_VERSION} - Developed by {TERMSCP_AUTHORS}",
|
||||
));
|
||||
}
|
||||
// Setup activity?
|
||||
@@ -182,7 +83,7 @@ fn parse_args(args: Args) -> Result<RunOpts, String> {
|
||||
// Match ticks
|
||||
run_opts.ticks = Duration::from_millis(args.ticks);
|
||||
// @! extra modes
|
||||
if let Some(theme) = args.theme {
|
||||
if let Some(theme) = args.theme.as_deref() {
|
||||
run_opts.task = Task::ImportTheme(PathBuf::from(theme));
|
||||
}
|
||||
if args.update {
|
||||
@@ -190,116 +91,111 @@ fn parse_args(args: Args) -> Result<RunOpts, String> {
|
||||
}
|
||||
// @! Ordinary mode
|
||||
// Remote argument
|
||||
if let Some(remote) = args.positional.get(0) {
|
||||
// Parse address
|
||||
match utils::parser::parse_remote_opt(remote.as_str()) {
|
||||
Ok(mut remote) => {
|
||||
// If password is provided, set password
|
||||
if let Some(passwd) = args.password {
|
||||
if let Some(mut params) = remote.params.mut_generic_params() {
|
||||
params.password = Some(passwd);
|
||||
}
|
||||
}
|
||||
// Set params
|
||||
run_opts.remote = Some(remote);
|
||||
// In this case the first activity will be FileTransfer
|
||||
run_opts.task = Task::Activity(NextActivity::FileTransfer);
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(format!("Bad address option: {}", err));
|
||||
}
|
||||
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));
|
||||
return Err(format!("Bad working directory argument: {err}"));
|
||||
}
|
||||
}
|
||||
Ok(run_opts)
|
||||
}
|
||||
|
||||
/// ### read_password
|
||||
///
|
||||
/// Read password from tty if address is specified
|
||||
fn read_password(run_opts: &mut RunOpts) -> Result<(), String> {
|
||||
// Initialize client if necessary
|
||||
if let Some(remote) = run_opts.remote.as_mut() {
|
||||
// Ask password for generic params
|
||||
if let Some(mut params) = remote.params.mut_generic_params() {
|
||||
// Ask password only if generic protocol params
|
||||
if params.password.is_none() {
|
||||
// Ask password if unspecified
|
||||
params.password = match rpassword::read_password_from_tty(Some("Password: ")) {
|
||||
Ok(p) => {
|
||||
if p.is_empty() {
|
||||
None
|
||||
} else {
|
||||
debug!(
|
||||
"Read password from tty: {}",
|
||||
utils::fmt::shadow_password(p.as_str())
|
||||
);
|
||||
Some(p)
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
return Err("Could not read password from prompt".to_string());
|
||||
}
|
||||
};
|
||||
}
|
||||
/// Parse address argument from cli args
|
||||
fn parse_address_arg(args: &Args) -> Result<Remote, String> {
|
||||
if let Some(remote) = args.positional.get(0) {
|
||||
if args.address_as_bookmark {
|
||||
Ok(Remote::Bookmark(BookmarkParams::new(
|
||||
remote,
|
||||
args.password.as_ref(),
|
||||
)))
|
||||
} else {
|
||||
// Parse address
|
||||
parse_remote_address(remote.as_str())
|
||||
.map(|x| Remote::Host(HostParams::new(x, args.password.as_deref())))
|
||||
}
|
||||
} else {
|
||||
Ok(Remote::None)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// ### run
|
||||
///
|
||||
/// Parse remote address
|
||||
fn parse_remote_address(remote: &str) -> Result<FileTransferParams, String> {
|
||||
utils::parser::parse_remote_opt(remote).map_err(|e| format!("Bad address option: {e}"))
|
||||
}
|
||||
|
||||
/// Run task and return rc
|
||||
fn run(mut run_opts: RunOpts) -> i32 {
|
||||
fn run(run_opts: RunOpts) -> i32 {
|
||||
match run_opts.task {
|
||||
Task::ImportTheme(theme) => match support::import_theme(theme.as_path()) {
|
||||
Ok(_) => {
|
||||
println!("Theme has been successfully imported!");
|
||||
0
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("{}", err);
|
||||
1
|
||||
}
|
||||
},
|
||||
Task::InstallUpdate => match support::install_update() {
|
||||
Ok(msg) => {
|
||||
println!("{}", msg);
|
||||
0
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Could not install update: {}", err);
|
||||
1
|
||||
}
|
||||
},
|
||||
Task::Activity(activity) => {
|
||||
// Get working directory
|
||||
let wrkdir: PathBuf = match env::current_dir() {
|
||||
Ok(dir) => dir,
|
||||
Err(_) => PathBuf::from("/"),
|
||||
};
|
||||
// Create activity manager (and context too)
|
||||
let mut manager: ActivityManager =
|
||||
match ActivityManager::new(wrkdir.as_path(), run_opts.ticks) {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
eprintln!("Could not start activity manager: {}", err);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
// Set file transfer params if set
|
||||
if let Some(remote) = run_opts.remote.take() {
|
||||
manager.set_filetransfer_params(remote);
|
||||
}
|
||||
manager.run(activity);
|
||||
Task::ImportTheme(theme) => run_import_theme(&theme),
|
||||
Task::InstallUpdate => run_install_update(),
|
||||
Task::Activity(activity) => run_activity(activity, run_opts.ticks, run_opts.remote),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_import_theme(theme: &Path) -> i32 {
|
||||
match support::import_theme(theme) {
|
||||
Ok(_) => {
|
||||
println!("Theme has been successfully imported!");
|
||||
0
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("{err}");
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_install_update() -> i32 {
|
||||
match support::install_update() {
|
||||
Ok(msg) => {
|
||||
println!("{msg}");
|
||||
0
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Could not install update: {err}");
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_activity(activity: NextActivity, ticks: Duration, remote: Remote) -> i32 {
|
||||
// Create activity manager (and context too)
|
||||
let mut manager: ActivityManager = match ActivityManager::new(ticks) {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
eprintln!("Could not start activity manager: {err}");
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
// Set file transfer params if set
|
||||
match remote {
|
||||
Remote::Bookmark(BookmarkParams { name, password }) => {
|
||||
if let Err(err) = manager.resolve_bookmark_name(&name, password.as_deref()) {
|
||||
eprintln!("{err}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Remote::Host(HostParams { params, password }) => {
|
||||
if let Err(err) = manager.set_filetransfer_params(params, password.as_deref()) {
|
||||
eprintln!("{err}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Remote::None => {}
|
||||
}
|
||||
manager.run(activity);
|
||||
0
|
||||
}
|
||||
|
||||
@@ -2,42 +2,16 @@
|
||||
//!
|
||||
//! this module exposes some extra run modes for termscp, meant to be used for "support", such as installing themes
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// mod
|
||||
use crate::system::{
|
||||
auto_update::{Update, UpdateStatus},
|
||||
config_client::ConfigClient,
|
||||
environment,
|
||||
notifications::Notification,
|
||||
theme_provider::ThemeProvider,
|
||||
};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// ### import_theme
|
||||
///
|
||||
use crate::system::auto_update::{Update, UpdateStatus};
|
||||
use crate::system::config_client::ConfigClient;
|
||||
use crate::system::environment;
|
||||
use crate::system::notifications::Notification;
|
||||
use crate::system::theme_provider::ThemeProvider;
|
||||
|
||||
/// Import theme at provided path into termscp
|
||||
pub fn import_theme(p: &Path) -> Result<(), String> {
|
||||
if !p.exists() {
|
||||
@@ -46,7 +20,7 @@ pub fn import_theme(p: &Path) -> Result<(), String> {
|
||||
));
|
||||
}
|
||||
// Validate theme file
|
||||
ThemeProvider::new(p).map_err(|e| format!("Invalid theme error: {}", e))?;
|
||||
ThemeProvider::new(p).map_err(|e| format!("Invalid theme error: {e}"))?;
|
||||
// get config dir
|
||||
let cfg_dir: PathBuf = get_config_dir()?;
|
||||
// Get theme directory
|
||||
@@ -54,11 +28,9 @@ pub fn import_theme(p: &Path) -> Result<(), String> {
|
||||
// Copy theme to theme_dir
|
||||
fs::copy(p, theme_file.as_path())
|
||||
.map(|_| ())
|
||||
.map_err(|e| format!("Could not import theme: {}", e))
|
||||
.map_err(|e| format!("Could not import theme: {e}"))
|
||||
}
|
||||
|
||||
/// ### install_update
|
||||
///
|
||||
/// Install latest version of termscp if an update is available
|
||||
pub fn install_update() -> Result<String, String> {
|
||||
match Update::default()
|
||||
@@ -74,7 +46,7 @@ pub fn install_update() -> Result<String, String> {
|
||||
{
|
||||
Notification::update_installed(v.as_str());
|
||||
}
|
||||
Ok(format!("termscp has been updated to version {}", v))
|
||||
Ok(format!("termscp has been updated to version {v}"))
|
||||
}
|
||||
Err(err) => {
|
||||
if get_config_client()
|
||||
@@ -88,8 +60,6 @@ pub fn install_update() -> Result<String, String> {
|
||||
}
|
||||
}
|
||||
|
||||
/// ### get_config_dir
|
||||
///
|
||||
/// Get configuration directory
|
||||
fn get_config_dir() -> Result<PathBuf, String> {
|
||||
match environment::init_config_dir() {
|
||||
@@ -98,14 +68,11 @@ fn get_config_dir() -> Result<PathBuf, String> {
|
||||
"Your system doesn't provide a configuration directory",
|
||||
)),
|
||||
Err(err) => Err(format!(
|
||||
"Could not initialize configuration directory: {}",
|
||||
err
|
||||
"Could not initialize configuration directory: {err}"
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// ### get_config_client
|
||||
///
|
||||
/// Get configuration client
|
||||
fn get_config_client() -> Option<ConfigClient> {
|
||||
match get_config_dir() {
|
||||
|
||||
@@ -2,39 +2,13 @@
|
||||
//!
|
||||
//! Automatic update module. This module is used to upgrade the current version of termscp to the latest available on Github
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
use self_update::backends::github::Update as GithubUpdater;
|
||||
pub use self_update::errors::Error as UpdateError;
|
||||
use self_update::update::Release as UpdRelease;
|
||||
use self_update::{cargo_crate_version, Status};
|
||||
|
||||
use crate::utils::parser::parse_semver;
|
||||
|
||||
pub use self_update::errors::Error as UpdateError;
|
||||
use self_update::{
|
||||
backends::github::Update as GithubUpdater, cargo_crate_version, update::Release as UpdRelease,
|
||||
Status,
|
||||
};
|
||||
|
||||
/// ### UpdateStatus
|
||||
///
|
||||
/// The status of the update in case of success
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum UpdateStatus {
|
||||
@@ -119,7 +93,7 @@ impl Update {
|
||||
new_version,
|
||||
cargo_crate_version!()
|
||||
);
|
||||
if new_version.as_str() > cargo_crate_version!() {
|
||||
if Self::is_new_version_higher(new_version.as_str(), cargo_crate_version!()) {
|
||||
Some(r) // New version is available
|
||||
} else {
|
||||
None // No new version
|
||||
@@ -128,6 +102,12 @@ impl Update {
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check wether new version is higher than new version
|
||||
fn is_new_version_higher(new_version: &str, current_version: &str) -> bool {
|
||||
version_compare::compare(new_version, current_version).unwrap_or(version_compare::Cmp::Lt)
|
||||
== version_compare::Cmp::Gt
|
||||
}
|
||||
}
|
||||
impl From<Status> for UpdateStatus {
|
||||
fn from(s: Status) -> Self {
|
||||
@@ -150,10 +130,10 @@ impl From<UpdRelease> for Release {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn auto_update_default() {
|
||||
let upd: Update = Update::default();
|
||||
@@ -216,4 +196,13 @@ mod test {
|
||||
assert_eq!(release.body.as_str(), "fixed everything");
|
||||
assert_eq!(release.version.as_str(), "0.7.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_tell_that_version_is_higher() {
|
||||
assert!(Update::is_new_version_higher("0.10.0", "0.9.0"));
|
||||
assert!(Update::is_new_version_higher("0.20.0", "0.19.0"));
|
||||
assert!(!Update::is_new_version_higher("0.9.0", "0.10.0"));
|
||||
assert!(!Update::is_new_version_higher("0.9.9", "0.10.1"));
|
||||
assert!(!Update::is_new_version_higher("0.10.9", "0.11.0"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,33 +2,17 @@
|
||||
//!
|
||||
//! `bookmarks_client` is the module which provides an API between the Bookmarks module and the system
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// Crate
|
||||
// Ext
|
||||
use std::fs::OpenOptions;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::ToString;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use super::keys::filestorage::FileStorage;
|
||||
#[cfg(feature = "with-keyring")]
|
||||
use super::keys::keyringstorage::KeyringStorage;
|
||||
use super::keys::{filestorage::FileStorage, KeyStorage, KeyStorageError};
|
||||
use super::keys::{KeyStorage, KeyStorageError};
|
||||
// Local
|
||||
use crate::config::{
|
||||
bookmarks::{Bookmark, UserHosts},
|
||||
@@ -38,11 +22,6 @@ use crate::filetransfer::FileTransferParams;
|
||||
use crate::utils::crypto;
|
||||
use crate::utils::fmt::fmt_time;
|
||||
use crate::utils::random::random_alphanumeric_with_len;
|
||||
// Ext
|
||||
use std::fs::OpenOptions;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::ToString;
|
||||
use std::time::SystemTime;
|
||||
|
||||
/// BookmarksClient provides a layer between the host system and the bookmarks module
|
||||
pub struct BookmarksClient {
|
||||
@@ -111,7 +90,7 @@ impl BookmarksClient {
|
||||
error!("Failed to set new key into storage: {}", e);
|
||||
return Err(SerializerError::new_ex(
|
||||
SerializerErrorKind::Io,
|
||||
format!("Could not write key to storage: {}", e),
|
||||
format!("Could not write key to storage: {e}"),
|
||||
));
|
||||
}
|
||||
// Return key
|
||||
@@ -121,7 +100,7 @@ impl BookmarksClient {
|
||||
error!("Failed to get key from storage: {}", e);
|
||||
return Err(SerializerError::new_ex(
|
||||
SerializerErrorKind::Io,
|
||||
format!("Could not get key from storage: {}", e),
|
||||
format!("Could not get key from storage: {e}"),
|
||||
));
|
||||
}
|
||||
},
|
||||
@@ -393,15 +372,16 @@ impl BookmarksClient {
|
||||
#[cfg(not(target_os = "macos"))] // CI/CD blocks
|
||||
mod tests {
|
||||
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use super::*;
|
||||
use crate::filetransfer::params::{AwsS3Params, GenericProtocolParams};
|
||||
use crate::filetransfer::{FileTransferProtocol, ProtocolParams};
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[test]
|
||||
|
||||
fn test_system_bookmarks_new() {
|
||||
@@ -418,28 +398,6 @@ mod tests {
|
||||
assert_eq!(client.recents_size, 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
fn test_system_bookmarks_new_err() {
|
||||
assert!(BookmarksClient::new(
|
||||
Path::new("/tmp/oifoif/omar"),
|
||||
Path::new("/tmp/efnnu/omar"),
|
||||
16
|
||||
)
|
||||
.is_err());
|
||||
|
||||
let tmp_dir: tempfile::TempDir = TempDir::new().ok().unwrap();
|
||||
let (cfg_path, _): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
||||
assert!(
|
||||
BookmarksClient::new(cfg_path.as_path(), Path::new("/tmp/efnnu/omar"), 16).is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
fn test_system_bookmarks_new_from_existing() {
|
||||
@@ -753,7 +711,7 @@ mod tests {
|
||||
// Limit is 2
|
||||
assert_eq!(client.iter_recents().count(), 2);
|
||||
// Check that 192.168.1.1 has been removed
|
||||
let key: String = client.iter_recents().nth(0).unwrap().to_string();
|
||||
let key: String = client.iter_recents().next().unwrap().to_string();
|
||||
assert!(matches!(
|
||||
client
|
||||
.hosts
|
||||
|
||||
@@ -2,36 +2,7 @@
|
||||
//!
|
||||
//! `config_client` is the module which provides an API between the Config module and the system
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// Locals
|
||||
use crate::config::{
|
||||
params::{UserConfig, DEFAULT_NOTIFICATION_TRANSFER_THRESHOLD},
|
||||
serialization::{deserialize, serialize, SerializerError, SerializerErrorKind},
|
||||
};
|
||||
use crate::explorer::GroupDirs;
|
||||
use crate::filetransfer::FileTransferProtocol;
|
||||
// Ext
|
||||
use std::fs::{create_dir, remove_file, File, OpenOptions};
|
||||
use std::io::Write;
|
||||
@@ -39,6 +10,11 @@ use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
|
||||
use crate::config::params::{UserConfig, DEFAULT_NOTIFICATION_TRANSFER_THRESHOLD};
|
||||
use crate::config::serialization::{deserialize, serialize, SerializerError, SerializerErrorKind};
|
||||
use crate::explorer::GroupDirs;
|
||||
use crate::filetransfer::FileTransferProtocol;
|
||||
|
||||
// Types
|
||||
pub type SshHost = (String, String, PathBuf); // 0: host, 1: username, 2: RSA key path
|
||||
|
||||
@@ -272,7 +248,7 @@ impl ConfigClient {
|
||||
// Get key path
|
||||
let ssh_key_path: PathBuf = {
|
||||
let mut p: PathBuf = self.ssh_key_dir.clone();
|
||||
p.push(format!("{}.key", host_name));
|
||||
p.push(format!("{host_name}.key"));
|
||||
p
|
||||
};
|
||||
info!(
|
||||
@@ -413,7 +389,7 @@ impl ConfigClient {
|
||||
/// Hosts are saved as `username@host` into configuration.
|
||||
/// This method creates the key name, starting from host and username
|
||||
fn make_ssh_host_key(host: &str, username: &str) -> String {
|
||||
format!("{}@{}", username, host)
|
||||
format!("{username}@{host}")
|
||||
}
|
||||
|
||||
/// Get ssh tokens starting from ssh host key
|
||||
@@ -421,7 +397,7 @@ impl ConfigClient {
|
||||
/// Returns: (host, username)
|
||||
fn get_ssh_tokens(host_key: &str) -> (String, String) {
|
||||
let tokens: Vec<&str> = host_key.split('@').collect();
|
||||
assert_eq!(tokens.len(), 2);
|
||||
assert!(tokens.len() >= 2);
|
||||
(String::from(tokens[1]), String::from(tokens[0]))
|
||||
}
|
||||
|
||||
@@ -437,14 +413,15 @@ impl ConfigClient {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::io::Read;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use super::*;
|
||||
use crate::config::UserConfig;
|
||||
use crate::utils::random::random_alphanumeric_with_len;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::io::Read;
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[test]
|
||||
fn test_system_config_new() {
|
||||
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
||||
@@ -514,7 +491,7 @@ mod tests {
|
||||
// Verify client has updated parameters
|
||||
assert_eq!(client.get_default_protocol(), FileTransferProtocol::Scp);
|
||||
assert_eq!(client.get_text_editor(), PathBuf::from("/usr/bin/vim"));
|
||||
let mut expected_key_path: PathBuf = key_path.clone();
|
||||
let mut expected_key_path: PathBuf = key_path;
|
||||
expected_key_path.push("pi@192.168.1.31.key");
|
||||
assert_eq!(
|
||||
client.get_ssh_key("pi@192.168.1.31").unwrap().unwrap(),
|
||||
@@ -668,13 +645,24 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_config_remote_ssh_config() {
|
||||
fn should_get_and_set_ssh_config_dir() {
|
||||
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
||||
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
||||
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
||||
.ok()
|
||||
.unwrap();
|
||||
assert_eq!(client.get_ssh_config(), None); // Null ?
|
||||
|
||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("/root"));
|
||||
let mut ssh_config_path = "~/.ssh/config".to_string();
|
||||
ssh_config_path = ssh_config_path.replacen('~', &home_dir.to_string_lossy(), 1);
|
||||
|
||||
let ssh_config_path = if PathBuf::from(&ssh_config_path).exists() {
|
||||
Some(ssh_config_path)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
assert_eq!(client.get_ssh_config(), ssh_config_path.as_deref()); // Null ?
|
||||
client.set_ssh_config(Some(String::from("/tmp/ssh_config")));
|
||||
assert_eq!(client.get_ssh_config(), Some("/tmp/ssh_config"));
|
||||
client.set_ssh_config(None);
|
||||
|
||||
@@ -2,36 +2,11 @@
|
||||
//!
|
||||
//! `environment` is the module which provides Path and values for the system environment
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// Ext
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// ### get_config_dir
|
||||
///
|
||||
/// Get termscp configuration directory path.
|
||||
/// Returns None, if it's not possible to get it
|
||||
/// Get termscp config directory path and initialize it.
|
||||
/// Returns None if it's not possible to initialize it
|
||||
pub fn init_config_dir() -> Result<Option<PathBuf>, String> {
|
||||
// Get file
|
||||
#[cfg(not(test))]
|
||||
@@ -42,26 +17,51 @@ pub fn init_config_dir() -> Result<Option<PathBuf>, String> {
|
||||
lazy_static! {
|
||||
static ref CONF_DIR: Option<PathBuf> = Some(std::env::temp_dir());
|
||||
}
|
||||
if CONF_DIR.is_some() {
|
||||
// Get path of bookmarks
|
||||
let mut p: PathBuf = CONF_DIR.as_ref().unwrap().clone();
|
||||
// Append termscp dir
|
||||
p.push("termscp/");
|
||||
// If directory doesn't exist, create it
|
||||
match p.exists() {
|
||||
true => Ok(Some(p)),
|
||||
false => match std::fs::create_dir(p.as_path()) {
|
||||
Ok(_) => Ok(Some(p)),
|
||||
Err(err) => Err(err.to_string()),
|
||||
},
|
||||
}
|
||||
|
||||
if let Some(dir) = CONF_DIR.as_deref() {
|
||||
init_dir(dir).map(Option::Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// ### get_bookmarks_paths
|
||||
///
|
||||
/// Get termscp cache directory path and initialize it.
|
||||
/// Returns None if it's not possible to initialize it
|
||||
pub fn init_cache_dir() -> Result<Option<PathBuf>, String> {
|
||||
// Get file
|
||||
#[cfg(not(test))]
|
||||
lazy_static! {
|
||||
static ref CACHE_DIR: Option<PathBuf> = dirs::cache_dir();
|
||||
}
|
||||
#[cfg(test)]
|
||||
lazy_static! {
|
||||
static ref CACHE_DIR: Option<PathBuf> = Some(std::env::temp_dir());
|
||||
}
|
||||
|
||||
if let Some(dir) = CACHE_DIR.as_deref() {
|
||||
init_dir(dir).map(Option::Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Init a termscp env dir
|
||||
fn init_dir(p: &Path) -> Result<PathBuf, String> {
|
||||
// Get path of bookmarks
|
||||
let mut p: PathBuf = p.to_path_buf();
|
||||
// Append termscp dir
|
||||
p.push("termscp/");
|
||||
// If directory doesn't exist, create it
|
||||
if p.exists() {
|
||||
return Ok(p);
|
||||
}
|
||||
// directory doesn't exist; create dir recursively
|
||||
match std::fs::create_dir_all(p.as_path()) {
|
||||
Ok(_) => Ok(p),
|
||||
Err(err) => Err(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get paths for bookmarks client
|
||||
/// Returns: path of bookmarks.toml
|
||||
pub fn get_bookmarks_paths(config_dir: &Path) -> PathBuf {
|
||||
@@ -71,8 +71,6 @@ pub fn get_bookmarks_paths(config_dir: &Path) -> PathBuf {
|
||||
bookmarks_file
|
||||
}
|
||||
|
||||
/// ### get_config_paths
|
||||
///
|
||||
/// Returns paths for config client
|
||||
/// Returns: path of config.toml and path for ssh keys
|
||||
pub fn get_config_paths(config_dir: &Path) -> (PathBuf, PathBuf) {
|
||||
@@ -84,17 +82,13 @@ pub fn get_config_paths(config_dir: &Path) -> (PathBuf, PathBuf) {
|
||||
(bookmarks_file, keys_dir)
|
||||
}
|
||||
|
||||
/// ### get_log_paths
|
||||
///
|
||||
/// Returns the path for the supposed log file
|
||||
pub fn get_log_paths(config_dir: &Path) -> PathBuf {
|
||||
let mut log_file: PathBuf = PathBuf::from(config_dir);
|
||||
pub fn get_log_paths(cache_dir: &Path) -> PathBuf {
|
||||
let mut log_file: PathBuf = PathBuf::from(cache_dir);
|
||||
log_file.push("termscp.log");
|
||||
log_file
|
||||
}
|
||||
|
||||
/// ### get_theme_path
|
||||
///
|
||||
/// Get paths for theme provider
|
||||
/// Returns: path of theme.toml
|
||||
pub fn get_theme_path(config_dir: &Path) -> PathBuf {
|
||||
@@ -107,12 +101,13 @@ pub fn get_theme_path(config_dir: &Path) -> PathBuf {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Write;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use serial_test::serial;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Write;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
@@ -123,6 +118,15 @@ mod tests {
|
||||
assert!(std::fs::remove_dir_all(conf_dir.as_path()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn should_get_cache_dir() {
|
||||
// Create and get cache_dir
|
||||
let cache_dir: PathBuf = init_cache_dir().ok().unwrap().unwrap();
|
||||
// Remove dir
|
||||
assert!(std::fs::remove_dir_all(cache_dir.as_path()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_system_environment_get_config_dir_err() {
|
||||
@@ -149,7 +153,7 @@ mod tests {
|
||||
#[serial]
|
||||
fn test_system_environment_get_bookmarks_paths() {
|
||||
assert_eq!(
|
||||
get_bookmarks_paths(&Path::new("/home/omar/.config/termscp/")),
|
||||
get_bookmarks_paths(Path::new("/home/omar/.config/termscp/")),
|
||||
PathBuf::from("/home/omar/.config/termscp/bookmarks.toml"),
|
||||
);
|
||||
}
|
||||
@@ -158,7 +162,7 @@ mod tests {
|
||||
#[serial]
|
||||
fn test_system_environment_get_config_paths() {
|
||||
assert_eq!(
|
||||
get_config_paths(&Path::new("/home/omar/.config/termscp/")),
|
||||
get_config_paths(Path::new("/home/omar/.config/termscp/")),
|
||||
(
|
||||
PathBuf::from("/home/omar/.config/termscp/config.toml"),
|
||||
PathBuf::from("/home/omar/.config/termscp/.ssh/")
|
||||
@@ -170,8 +174,8 @@ mod tests {
|
||||
#[serial]
|
||||
fn test_system_environment_get_log_paths() {
|
||||
assert_eq!(
|
||||
get_log_paths(&Path::new("/home/omar/.config/termscp/")),
|
||||
PathBuf::from("/home/omar/.config/termscp/termscp.log"),
|
||||
get_log_paths(Path::new("/home/omar/.cache/termscp/")),
|
||||
PathBuf::from("/home/omar/.cache/termscp/termscp.log"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -179,7 +183,7 @@ mod tests {
|
||||
#[serial]
|
||||
fn test_system_environment_get_theme_path() {
|
||||
assert_eq!(
|
||||
get_theme_path(&Path::new("/home/omar/.config/termscp/")),
|
||||
get_theme_path(Path::new("/home/omar/.config/termscp/")),
|
||||
PathBuf::from("/home/omar/.config/termscp/theme.toml"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,36 +2,14 @@
|
||||
//!
|
||||
//! `filestorage` provides an implementation of the `KeyStorage` trait using a file
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// Local
|
||||
use super::{KeyStorage, KeyStorageError};
|
||||
// Ext
|
||||
use std::fs::{OpenOptions, Permissions};
|
||||
use std::io::{Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use super::{KeyStorage, KeyStorageError};
|
||||
|
||||
/// File storage is an implementation o the `KeyStorage` which uses a file to store the key
|
||||
pub struct FileStorage {
|
||||
dir_path: PathBuf,
|
||||
@@ -48,7 +26,7 @@ impl FileStorage {
|
||||
/// Make file path for key file from `dir_path` and the application id
|
||||
fn make_file_path(&self, storage_id: &str) -> PathBuf {
|
||||
let mut p: PathBuf = self.dir_path.clone();
|
||||
let file_name = format!(".{}.key", storage_id);
|
||||
let file_name = format!(".{storage_id}.key");
|
||||
p.push(file_name);
|
||||
p
|
||||
}
|
||||
@@ -113,13 +91,13 @@ impl KeyStorage for FileStorage {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_system_keys_filestorage_make_dir() {
|
||||
let storage: FileStorage = FileStorage::new(&Path::new("/tmp/"));
|
||||
let storage: FileStorage = FileStorage::new(Path::new("/tmp/"));
|
||||
assert_eq!(
|
||||
storage.make_file_path("bookmarks").as_path(),
|
||||
Path::new("/tmp/.bookmarks.key")
|
||||
@@ -136,10 +114,7 @@ mod tests {
|
||||
let app_name: &str = "termscp";
|
||||
let secret: &str = "Th15-15/My-Супер-Секрет";
|
||||
// Secret should not exist
|
||||
assert_eq!(
|
||||
storage.get_key(app_name).err().unwrap(),
|
||||
KeyStorageError::NoSuchKey
|
||||
);
|
||||
assert!(storage.get_key(app_name).is_err());
|
||||
// Write secret
|
||||
assert!(storage.set_key(app_name, secret).is_ok());
|
||||
// Get secret
|
||||
|
||||
@@ -2,34 +2,12 @@
|
||||
//!
|
||||
//! `keyringstorage` provides an implementation of the `KeyStorage` trait using the OS keyring
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// Local
|
||||
use super::{KeyStorage, KeyStorageError};
|
||||
// Ext
|
||||
use keyring::{Entry as Keyring, Error as KeyringError};
|
||||
|
||||
use super::{KeyStorage, KeyStorageError};
|
||||
|
||||
/// provides a `KeyStorage` implementation using the keyring crate
|
||||
pub struct KeyringStorage {
|
||||
username: String,
|
||||
@@ -49,24 +27,26 @@ impl KeyStorage for KeyringStorage {
|
||||
/// The key might be acccess through an identifier, which identifies
|
||||
/// the key in the storage
|
||||
fn get_key(&self, storage_id: &str) -> Result<String, KeyStorageError> {
|
||||
let storage: Keyring = Keyring::new(storage_id, self.username.as_str());
|
||||
let storage: Keyring = Keyring::new(storage_id, self.username.as_str())?;
|
||||
match storage.get_password() {
|
||||
Ok(s) => Ok(s),
|
||||
Err(e) => match e {
|
||||
KeyringError::NoEntry => Err(KeyStorageError::NoSuchKey),
|
||||
KeyringError::PlatformFailure(_)
|
||||
| KeyringError::NoStorageAccess(_)
|
||||
| KeyringError::WrongCredentialPlatform => Err(KeyStorageError::ProviderError),
|
||||
| KeyringError::Invalid(_, _)
|
||||
| KeyringError::Ambiguous(_) => Err(KeyStorageError::ProviderError),
|
||||
KeyringError::BadEncoding(_) | KeyringError::TooLong(_, _) => {
|
||||
Err(KeyStorageError::BadSytax)
|
||||
}
|
||||
_ => Err(KeyStorageError::ProviderError),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the key into the key storage
|
||||
fn set_key(&self, storage_id: &str, key: &str) -> Result<(), KeyStorageError> {
|
||||
let storage: Keyring = Keyring::new(storage_id, self.username.as_str());
|
||||
let storage: Keyring = Keyring::new(storage_id, self.username.as_str())?;
|
||||
match storage.set_password(key) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(KeyStorageError::ProviderError),
|
||||
@@ -78,7 +58,13 @@ impl KeyStorage for KeyringStorage {
|
||||
/// Returns whether the key storage is supported on the host system
|
||||
fn is_supported(&self) -> bool {
|
||||
let dummy: String = String::from("dummy-service");
|
||||
let storage: Keyring = Keyring::new(dummy.as_str(), self.username.as_str());
|
||||
let storage: Keyring = match Keyring::new(dummy.as_str(), self.username.as_str()) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
error!("could not instantiate keyring {e}");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// Check what kind of error is returned
|
||||
match storage.get_password() {
|
||||
Ok(_) => true,
|
||||
@@ -90,11 +76,11 @@ impl KeyStorage for KeyringStorage {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use whoami::username;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_system_keys_keyringstorage() {
|
||||
let username: String = username();
|
||||
@@ -102,21 +88,18 @@ mod tests {
|
||||
assert!(storage.is_supported());
|
||||
let app_name: &str = "termscp-test2";
|
||||
let secret: &str = "Th15-15/My-Супер-Секрет";
|
||||
let kring: Keyring = Keyring::new(app_name, username.as_str());
|
||||
let kring: Keyring = Keyring::new(app_name, username.as_str()).unwrap();
|
||||
let _ = kring.delete_password();
|
||||
drop(kring);
|
||||
// Secret should not exist
|
||||
assert_eq!(
|
||||
storage.get_key(app_name).err().unwrap(),
|
||||
KeyStorageError::NoSuchKey
|
||||
);
|
||||
assert!(storage.get_key(app_name).is_err());
|
||||
// Write secret
|
||||
assert!(storage.set_key(app_name, secret).is_ok());
|
||||
// Get secret
|
||||
assert_eq!(storage.get_key(app_name).ok().unwrap().as_str(), secret);
|
||||
|
||||
// Delete the key manually...
|
||||
let kring: Keyring = Keyring::new(app_name, username.as_str());
|
||||
let kring: Keyring = Keyring::new(app_name, username.as_str()).unwrap();
|
||||
assert!(kring.delete_password().is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,38 +2,17 @@
|
||||
//!
|
||||
//! `keystorage` provides the trait to manipulate to a KeyStorage
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// Storages
|
||||
pub mod filestorage;
|
||||
#[cfg(feature = "with-keyring")]
|
||||
pub mod keyringstorage;
|
||||
// ext
|
||||
#[cfg(feature = "with-keyring")]
|
||||
use keyring::Error as KeyringError;
|
||||
use thiserror::Error;
|
||||
|
||||
/// defines the error type for the `KeyStorage`
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum KeyStorageError {
|
||||
#[cfg(feature = "with-keyring")]
|
||||
#[error("Key has a bad syntax")]
|
||||
@@ -42,6 +21,16 @@ pub enum KeyStorageError {
|
||||
ProviderError,
|
||||
#[error("No such key")]
|
||||
NoSuchKey,
|
||||
#[cfg(feature = "with-keyring")]
|
||||
#[error("keyring error: {0}")]
|
||||
KeyringError(KeyringError),
|
||||
}
|
||||
|
||||
#[cfg(feature = "with-keyring")]
|
||||
impl From<KeyringError> for KeyStorageError {
|
||||
fn from(e: KeyringError) -> Self {
|
||||
Self::KeyringError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// this traits provides the methods to communicate and interact with the key storage.
|
||||
@@ -63,10 +52,10 @@ pub trait KeyStorage {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_system_keys_mod_errors() {
|
||||
#[cfg(feature = "with-keyring")]
|
||||
|
||||
@@ -2,63 +2,32 @@
|
||||
//!
|
||||
//! `logging` is the module which initializes the logging system for termscp
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// locals
|
||||
use crate::system::environment::{get_log_paths, init_config_dir};
|
||||
use crate::utils::file::open_file;
|
||||
// ext
|
||||
pub use simplelog::LevelFilter as LogLevel;
|
||||
use simplelog::{ConfigBuilder, WriteLogger};
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// ### init
|
||||
///
|
||||
use super::environment::{get_log_paths, init_cache_dir};
|
||||
use crate::utils::file::open_file;
|
||||
|
||||
/// Initialize logger
|
||||
pub fn init(level: LogLevel) -> Result<(), String> {
|
||||
// Init config dir
|
||||
let config_dir: PathBuf = match init_config_dir() {
|
||||
// Init cache dir
|
||||
let cache_dir = match init_cache_dir() {
|
||||
Ok(Some(p)) => p,
|
||||
Ok(None) => {
|
||||
return Err(String::from(
|
||||
"This system doesn't seem to support CONFIG_DIR",
|
||||
"This system doesn't seem to support CACHE_DIR",
|
||||
))
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let log_file_path: PathBuf = get_log_paths(config_dir.as_path());
|
||||
let log_file_path = get_log_paths(cache_dir.as_path());
|
||||
// Open log file
|
||||
let file: File = open_file(log_file_path.as_path(), true, true, false)
|
||||
let file = open_file(log_file_path.as_path(), true, true, false)
|
||||
.map_err(|e| format!("Failed to open file {}: {}", log_file_path.display(), e))?;
|
||||
// Prepare log config
|
||||
let config = ConfigBuilder::new()
|
||||
.set_time_format_str("%Y-%m-%dT%H:%M:%S%z")
|
||||
.build();
|
||||
let config = ConfigBuilder::new().set_time_format_rfc3339().build();
|
||||
// Make logger
|
||||
WriteLogger::init(level, config, file)
|
||||
.map_err(|e| format!("Failed to initialize logger: {}", e))
|
||||
WriteLogger::init(level, config, file).map_err(|e| format!("Failed to initialize logger: {e}"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -2,29 +2,6 @@
|
||||
//!
|
||||
//! `system` is the module which contains functions and data types related to current system
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// modules
|
||||
pub mod auto_update;
|
||||
pub mod bookmarks_client;
|
||||
@@ -35,3 +12,4 @@ pub mod logging;
|
||||
pub mod notifications;
|
||||
pub mod sshkey_storage;
|
||||
pub mod theme_provider;
|
||||
pub mod watcher;
|
||||
|
||||
@@ -2,52 +2,29 @@
|
||||
//!
|
||||
//! `SshKeyStorage` is the module which behaves a storage for ssh keys
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// Locals
|
||||
use super::config_client::ConfigClient;
|
||||
// Ext
|
||||
use remotefs_ssh::SshKeyStorage as SshKeyStorageT;
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
// Ext
|
||||
use remotefs_ssh::SshKeyStorage as SshKeyStorageTrait;
|
||||
use ssh2_config::SshConfig;
|
||||
|
||||
use super::config_client::ConfigClient;
|
||||
use crate::utils::ssh as ssh_utils;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SshKeyStorage {
|
||||
hosts: HashMap<String, PathBuf>, // Association between {user}@{host} and RSA key path
|
||||
/// Association between {user}@{host} and RSA key path
|
||||
hosts: HashMap<String, PathBuf>,
|
||||
/// Ssh2 configuration
|
||||
ssh_config: Option<SshConfig>,
|
||||
}
|
||||
|
||||
impl SshKeyStorage {
|
||||
/// Create an empty ssh key storage; used in case `ConfigClient` is not available
|
||||
#[cfg(test)]
|
||||
pub fn empty() -> Self {
|
||||
SshKeyStorage {
|
||||
hosts: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Make mapkey from host and username
|
||||
fn make_mapkey(host: &str, username: &str) -> String {
|
||||
format!("{}@{}", username, host)
|
||||
format!("{username}@{host}")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -57,21 +34,55 @@ impl SshKeyStorage {
|
||||
let key: String = Self::make_mapkey(host, username);
|
||||
self.hosts.insert(key, p);
|
||||
}
|
||||
}
|
||||
|
||||
impl SshKeyStorageT for SshKeyStorage {
|
||||
fn resolve(&self, host: &str, username: &str) -> Option<&Path> {
|
||||
/// Resolve host via termscp ssh keys storage
|
||||
fn resolve_host_in_termscp_storage(&self, host: &str, username: &str) -> Option<&Path> {
|
||||
let key: String = Self::make_mapkey(host, username);
|
||||
self.hosts.get(&key).map(|x| x.as_path())
|
||||
}
|
||||
|
||||
/// Resolve host via ssh2 configuration
|
||||
fn resolve_host_in_ssh2_configuration(&self, host: &str) -> Option<PathBuf> {
|
||||
self.ssh_config.as_ref().and_then(|x| {
|
||||
let key = x
|
||||
.query(host)
|
||||
.identity_file
|
||||
.as_ref()
|
||||
.and_then(|x| x.get(0).cloned());
|
||||
|
||||
key
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SshKeyStorageTrait for SshKeyStorage {
|
||||
fn resolve(&self, host: &str, username: &str) -> Option<PathBuf> {
|
||||
// search in termscp keys
|
||||
if let Some(path) = self.resolve_host_in_termscp_storage(host, username) {
|
||||
return Some(path.to_path_buf());
|
||||
}
|
||||
debug!(
|
||||
"couldn't find any ssh key associated to {} at {}. Trying with ssh2 config",
|
||||
username, host
|
||||
);
|
||||
// otherwise search in configuration
|
||||
let key = self.resolve_host_in_ssh2_configuration(host)?;
|
||||
debug!("Found key in SSH config for {host}: {}", key.display());
|
||||
Some(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ConfigClient> for SshKeyStorage {
|
||||
fn from(cfg_client: &ConfigClient) -> Self {
|
||||
// read ssh2 config
|
||||
let ssh_config = cfg_client.get_ssh_config().and_then(|x| {
|
||||
debug!("reading ssh config at {}", x);
|
||||
ssh_utils::parse_ssh2_config(x).ok()
|
||||
});
|
||||
let mut hosts: HashMap<String, PathBuf> =
|
||||
HashMap::with_capacity(cfg_client.iter_ssh_keys().count());
|
||||
debug!("Setting up SSH key storage");
|
||||
// Iterate over keys
|
||||
// Iterate over keys in storage
|
||||
for key in cfg_client.iter_ssh_keys() {
|
||||
match cfg_client.get_ssh_key(key) {
|
||||
Ok(host) => match host {
|
||||
@@ -89,18 +100,20 @@ impl From<&ConfigClient> for SshKeyStorage {
|
||||
info!("Got SSH key for {}", key);
|
||||
}
|
||||
// Return storage
|
||||
SshKeyStorage { hosts }
|
||||
SshKeyStorage { hosts, ssh_config }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::system::config_client::ConfigClient;
|
||||
use std::path::Path;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::path::Path;
|
||||
|
||||
use super::*;
|
||||
use crate::system::config_client::ConfigClient;
|
||||
use crate::utils::test_helpers;
|
||||
|
||||
#[test]
|
||||
fn test_system_sshkey_storage_new() {
|
||||
@@ -116,7 +129,7 @@ mod tests {
|
||||
// Create ssh key storage
|
||||
let storage: SshKeyStorage = SshKeyStorage::from(&client);
|
||||
// Verify key exists
|
||||
let mut exp_key_path: PathBuf = key_path.clone();
|
||||
let mut exp_key_path: PathBuf = key_path;
|
||||
exp_key_path.push("pi@192.168.1.31.key");
|
||||
assert_eq!(
|
||||
*storage.resolve("192.168.1.31", "pi").unwrap(),
|
||||
@@ -126,15 +139,44 @@ mod tests {
|
||||
assert!(storage.resolve("deskichup", "veeso").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sould_resolve_key_from_ssh2_config() {
|
||||
let rsa_key = test_helpers::create_sample_file_with_content("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDErJhQxEI0+VvhlXVUyh+vMCm7aXfCA/g633AG8ezD/5EylwchtAr2JCoBWnxn4zV8nI9dMqOgm0jO4IsXpKOjQojv+0VOH7I+cDlBg0tk4hFlvyyS6YviDAfDDln3jYUM+5QNDfQLaZlH2WvcJ3mkDxLVlI9MBX1BAeSmChLxwAvxALp2ncImNQLzDO9eHcig3dtMrEKkzXQowRW5Y7eUzg2+vvVq4H2DOjWwUndvB5sJkhEfTUVE7ID8ZdGJo60kUb/02dZYj+IbkAnMCsqktk0cg/4XFX82hEfRYFeb1arkysFisPU1DOb6QielL/axeTebVplaouYcXY0pFdJt root@8c50fd4c345a");
|
||||
let ssh_config_file = test_helpers::create_sample_file_with_content(format!(
|
||||
r#"
|
||||
Host test
|
||||
HostName 127.0.0.1
|
||||
Port 2222
|
||||
User test
|
||||
IdentityFile {}
|
||||
StrictHostKeyChecking no
|
||||
UserKnownHostsFile /dev/null
|
||||
"#,
|
||||
rsa_key.path().display()
|
||||
));
|
||||
// make storage
|
||||
let tmp_dir: tempfile::TempDir = tempfile::TempDir::new().ok().unwrap();
|
||||
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
||||
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
||||
.ok()
|
||||
.unwrap();
|
||||
client.set_ssh_config(Some(ssh_config_file.path().to_string_lossy().to_string()));
|
||||
let storage: SshKeyStorage = SshKeyStorage::from(&client);
|
||||
assert_eq!(
|
||||
storage.resolve("test", "pi").unwrap().as_path(),
|
||||
rsa_key.path()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_sshkey_storage_empty() {
|
||||
let storage: SshKeyStorage = SshKeyStorage::empty();
|
||||
let storage: SshKeyStorage = SshKeyStorage::default();
|
||||
assert_eq!(storage.hosts.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_sshkey_storage_add() {
|
||||
let mut storage: SshKeyStorage = SshKeyStorage::empty();
|
||||
let mut storage: SshKeyStorage = SshKeyStorage::default();
|
||||
storage.add_key("deskichup", "veeso", PathBuf::from("/tmp/omar"));
|
||||
assert_eq!(
|
||||
*storage.resolve("deskichup", "veeso").unwrap(),
|
||||
|
||||
@@ -2,39 +2,15 @@
|
||||
//!
|
||||
//! `theme_provider` is the module which provides an API between the theme configuration and the system
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// Locals
|
||||
use crate::config::{
|
||||
serialization::{deserialize, serialize, SerializerError, SerializerErrorKind},
|
||||
themes::Theme,
|
||||
};
|
||||
// Ext
|
||||
use std::fs::OpenOptions;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::ToString;
|
||||
|
||||
use crate::config::serialization::{deserialize, serialize, SerializerError, SerializerErrorKind};
|
||||
use crate::config::themes::Theme;
|
||||
|
||||
/// ThemeProvider provides a high level API to communicate with the termscp theme
|
||||
pub struct ThemeProvider {
|
||||
theme: Theme, // Theme loaded
|
||||
@@ -165,12 +141,12 @@ impl ThemeProvider {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use tempfile::TempDir;
|
||||
use tuirealm::tui::style::Color;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_system_theme_provider_new() {
|
||||
let tmp_dir: tempfile::TempDir = TempDir::new().ok().unwrap();
|
||||
|
||||