Compare commits

...

646 commits

Author SHA1 Message Date
timvisee
238f1f54d7
Bump MSRV to 1.63.0 2023-08-20 17:48:39 +02:00
timvisee
5df8463b74
Bump dependencies 2023-08-20 17:37:36 +02:00
timvisee
8d8ad7fdb3
Revert "Upgrade to libssl3/openssl3."
This reverts commit 5fd2eac6eb.
2023-05-18 23:12:06 +02:00
timvisee
f06b639542
Merge branch 'sarrchri-linux-libssl3' into master
See https://github.com/timvisee/ffsend/pull/156
2023-05-18 23:00:57 +02:00
Christian Sarre
5fd2eac6eb Upgrade to libssl3/openssl3. 2023-04-12 13:59:56 +03:00
timvisee
e0821c751d
Merge branch 'alichtman-master' into master
See https://github.com/timvisee/ffsend/pull/154
2023-04-07 08:25:20 +02:00
Aaron Lichtman
3c1c2dc28c
Fix segfault
close  #153
2023-04-06 23:14:09 -07:00
timvisee
13f9edd2ea
Bump MSRV to 1.60 2023-02-20 11:16:30 +01:00
timvisee
fd5b38f9ab
Update dependencies 2023-02-14 14:00:41 +01:00
timvisee
6661a58770
Revert "Use shorter passphrase words, make them less than 32-characters long"
This reverts commit fdf8ae9201.

This is reverted because the password length limit is now set much
higher, see: https://github.com/timvisee/send/pull/147
2023-02-14 13:59:44 +01:00
Tim Visée
b2b287e34f Merge branch 'prettytable-rs-features' into 'master'
Disable unused features in prettytable-rs crate

See merge request timvisee/ffsend!41
2022-11-14 17:49:35 +00:00
Jakub Jirutka
9b8dee12ea Disable unused features in prettytable-rs crate
ffsend doesn't use csv.
2022-10-09 21:57:19 +02:00
timvisee
a98c9d1ce4
Merge branch 'kianmeng-fix-typos' into master
See https://github.com/timvisee/ffsend/pull/144
2022-10-05 16:07:03 +02:00
Kian-Meng Ang
f77f9f116a Fix typos
Found via `codespell -S *.svg -L crate,ser`
2022-10-05 21:52:50 +08:00
timvisee
afb004680b
Update dependencies 2022-08-18 16:51:01 +02:00
timvisee
6edc0b1eac
Merge branch 'master' of gitlab.com:timvisee/ffsend 2022-07-04 18:38:44 +02:00
timvisee
7c94f265e2
Update Send link in README, point to repository instead 2022-07-04 18:38:35 +02:00
Tim Visée
d33c03cd2c Merge branch 'nautilus-script' into 'master'
Add script for Nautilus

See merge request timvisee/ffsend!39
2022-06-27 13:22:36 +00:00
getzze
ecc6bcf736 add script for Nautilus 2022-06-27 13:46:50 +01:00
timvisee
3e9a4d67f2
Bump version to 0.2.76 2022-06-20 10:09:01 +02:00
timvisee
ea87a778b3
Update dependencies 2022-06-20 10:08:45 +02:00
timvisee
91cf01ec75
Fix Snapcraft CI release, use new authentication method 2022-06-20 10:04:37 +02:00
timvisee
7598063e36
Bump version to 0.2.75 2022-06-19 02:01:33 +02:00
timvisee
6672891490
Update dependencies 2022-06-19 02:01:12 +02:00
timvisee
8139e3be01
Bump MSRV to 1.56.1 2022-06-19 01:42:32 +02:00
timvisee
ac9f56945b
Update dependencies 2022-06-19 01:41:03 +02:00
timvisee
b42225be76
Update dependencies 2022-06-07 10:29:32 +02:00
dependabot[bot]
1a5ec2de79
Bump regex from 1.5.4 to 1.5.5
Bumps [regex](https://github.com/rust-lang/regex) from 1.5.4 to 1.5.5.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.5.4...1.5.5)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-06 21:18:06 +00:00
timvisee
216772361a
Remove deprecated badges from Cargo.toml 2021-10-15 12:36:05 +02:00
timvisee
55c243a007
Update dependencies 2021-10-14 22:52:02 +02:00
timvisee
ccd489ce2e
Bump version to 0.2.74 2021-10-07 13:32:06 +02:00
timvisee
8026a79478
Update dependencies 2021-10-07 13:31:30 +02:00
Tim Visée
92ab75f837 Merge branch '113-update-static-openssl' into 'master'
Resolve "Update static OpenSSL"

Closes #113

See merge request timvisee/ffsend!38
2021-10-07 11:29:13 +00:00
timvisee
011d97f843
Compile OpenSSL with no-async 2021-10-07 13:09:39 +02:00
timvisee
3782996376
Update static OpenSSL version to 1.1.1k 2021-10-07 13:09:38 +02:00
timvisee
8f909b805b
Revert "Use latest ca-certificates for ffsend binary test against public instance"
This reverts commit 4c55145a07.
2021-10-07 13:09:24 +02:00
timvisee
178b73f210
Update dependencies 2021-10-06 13:51:38 +02:00
timvisee
4c55145a07
Use latest ca-certificates for ffsend binary test against public instance 2021-10-04 12:45:11 +02:00
timvisee
faa1141a2b
Further improve Arch AUR packages
Based on comments at:
10977f056d

Thanks @alerque
2021-08-31 16:03:38 +02:00
timvisee
10977f056d
Update Arch AUR packages to latest standards
Suggested by https://aur.archlinux.org/packages/ffsend/#comment-824438
2021-08-31 15:32:58 +02:00
timvisee
e2f9b75844
Bump version to 0.2.73 2021-08-30 14:19:44 +02:00
timvisee
e5fd4c237b
Update dependencies 2021-08-30 14:18:58 +02:00
timvisee
4593ece50f
Update dependencies 2021-08-26 20:37:43 +02:00
timvisee
cf8c13afa2
Merge branch 'a1346054-fixes' into master
See https://github.com/timvisee/ffsend/pull/129
2021-08-26 16:33:48 +02:00
a1346054
7e5c750340 Trim excess whitespace 2021-08-26 14:24:23 +00:00
a1346054
e2c20840e4 Fix spelling 2021-08-26 13:41:29 +00:00
a1346054
c7c53d8649 Fix issues in scripts identified using shellcheck 2021-08-25 22:31:38 +00:00
a1346054
1dcecbc281 Invoke bash using /usr/bin/env instead of hardcoded path 2021-08-25 22:30:17 +00:00
a1346054
485e5cd088 Use https links 2021-08-25 22:29:23 +00:00
a1346054
42dffc864d Use correct license file
The current file was missing the "How to Apply These Terms to Your New
Programs" section, which is an integral part of the GPL and is not
supposed to be removed:

https://www.gnu.org/licenses/gpl-faq.html#GPLOmitPreamble

Correct file was downloaded from:
https://www.gnu.org/licenses/gpl-3.0.txt
2021-08-25 22:23:37 +00:00
a1346054
abe52c831d Don't parse output of ls 2021-08-25 21:54:57 +00:00
timvisee
2322096927
Update README, link to issue explaining default host, remove old info 2021-06-24 16:40:35 +02:00
timvisee
22c53316ab
Replace use of deprecated URL to string function
See https://gitlab.com/timvisee/ffsend/-/issues/110
2021-05-09 17:37:20 +02:00
timvisee
e440f4b68a
Bump version to 0.2.72 2021-05-08 00:12:02 +02:00
timvisee
e96d2a7a0b
Update dependencies 2021-05-08 00:11:31 +02:00
timvisee
e03dec6d91
Add FFSEND_DOWNLOAD_LIMIT environment variable 2021-05-08 00:06:59 +02:00
timvisee
4ced2ea4d0
Change Firefox Send branding to Send 2021-05-05 22:33:41 +02:00
timvisee
924cfbeb95
Replace Mozilla's Send links with @timvisee's Send links in README
For more details see: https://gitlab.com/timvisee/ffsend/-/issues/101
2021-05-05 22:28:28 +02:00
timvisee
8266fcb633
Change CI AUR build image from archlinux/base to archlinux 2021-05-02 17:54:08 +02:00
timvisee
796098093b
Update dependencies 2021-04-21 19:13:07 +02:00
timvisee
8bd3efb1c4
Shell completion generation status should print to stderr, not stdout 2021-04-21 19:11:54 +02:00
timvisee
463130cdf8
Merge branch 'herbygillot-patch-1' into 'master' 2021-04-09 18:54:01 +02:00
Herby Gillot
2d7fd864b3
README: add MacPorts install info 2021-04-09 12:41:21 -04:00
timvisee
68a57b5ccc
Bump version to 0.2.71 2021-04-09 16:21:17 +02:00
timvisee
63faf51685
Fix compilation error due to missing imports 2021-04-09 16:20:42 +02:00
timvisee
37cdba8ddd
Add support to upload file from stdin
Fixes https://github.com/timvisee/ffsend/issues/39
2021-04-09 15:53:06 +02:00
timvisee
9908084745
Bump version to 0.2.70 2021-04-09 14:48:51 +02:00
timvisee
1070f1f297
Bump MSRV to 1.46
See https://github.com/rust-lang/rust/issues/49146
2021-04-09 14:48:06 +02:00
timvisee
f418bc47ab
Bump version to 0.2.69 2021-04-09 14:41:29 +02:00
timvisee
29069012f7
Update dependencies 2021-04-09 14:36:06 +02:00
timvisee
ce5d31e546
Add environment variable to set default upload expiry time
See https://github.com/timvisee/ffsend/issues/124
2021-04-09 14:32:13 +02:00
timvisee
d75e69841e
Bump minimum Rust version to 1.43.0 2021-01-04 12:51:35 +01:00
timvisee
dfb773a96e
Fix compilation error due to rand API change 2021-01-04 12:33:09 +01:00
timvisee
ba865715d7
Update dependencies 2021-01-04 11:44:33 +01:00
timvisee
549657df21
Remove redundant extern crate imports 2020-10-26 12:32:54 +01:00
timvisee
65306a30e8
Run non dependent jobs concurrently across stages in GitLab CI 2020-10-26 12:28:48 +01:00
timvisee
01e9524e7d
Remove instructions from LICENSE file 2020-10-21 23:28:06 +02:00
timvisee
1664c48d7d
Bump version to 0.2.68 2020-10-16 14:36:51 +02:00
timvisee
8524993e89
Update dependencies 2020-10-16 14:36:34 +02:00
timvisee
87816a58e2
Update ffsend-api to 0.6.3 for increased limits 2020-10-16 14:33:46 +02:00
timvisee
ed34588184
Bump version to 0.2.67 2020-09-27 15:33:47 +02:00
timvisee
cb98358fb9
Update dependencies 2020-09-27 15:33:09 +02:00
Tim Visée
fa0ecf61bc Merge branch '104-fix-share-url-printing-on-windows' into 'master'
Resolve "Fix share URL printing on Windows"

Closes #104

See merge request timvisee/ffsend!37
2020-09-27 13:31:25 +00:00
timvisee
b52b2256b8
Print additional newline on Windows before share URL 2020-09-26 20:47:28 +02:00
timvisee
a20deeb759
Print upload complete notice below progress bar, attempt to fix URL 2020-09-24 22:43:01 +02:00
timvisee
3a90b0b0b1
Bump version to 0.2.66 2020-09-21 12:24:16 +02:00
timvisee
70710c14ed
Update dependencies 2020-09-21 12:23:45 +02:00
timvisee
3fd00cfdc3
Update host info in README 2020-09-21 12:16:06 +02:00
timvisee
3d60493573
Update command line about notice, add donate link 2020-09-21 12:09:48 +02:00
timvisee
56ab026d22
Update custom host info notice in README, add donation link 2020-08-25 13:19:03 +02:00
timvisee
ed78f29ccf
Merge branch 'master' of gitlab.com:timvisee/ffsend 2020-08-19 11:37:09 +02:00
timvisee
e1e1343b31
Update Docker image maintainer label 2020-08-19 11:36:54 +02:00
timvisee
d779188a73
Update ffsend-api dependency 2020-07-30 19:06:19 +02:00
timvisee
42bbfac6bd
Update dependencies 2020-07-30 18:23:52 +02:00
timvisee
ff91585e1b
Make notice on top of README less verbose 2020-07-09 12:53:34 +02:00
timvisee
4de467a86f
Bump version to 0.2.65 2020-07-09 12:50:56 +02:00
timvisee
d0a01ca34e
Add notice about default host switch to top of README 2020-07-09 12:48:59 +02:00
timvisee
8658a68b23
Update dependencies 2020-07-09 12:46:27 +02:00
timvisee
522bf187f5
Use send.vis.ee as default host until Mozilla's Firefox Send is up again
See https://gitlab.com/timvisee/ffsend/-/issues/101
2020-07-09 12:45:57 +02:00
timvisee
62133be103
Make temporarily unavailable notice bold 2020-07-09 09:50:21 +02:00
timvisee
290a057b7e
Add temporarily unavailable notice to README 2020-07-09 09:49:52 +02:00
timvisee
0032555348
Merge branch 'vlcinsky-master'
See https://github.com/timvisee/ffsend/pull/117
2020-06-21 22:01:49 +02:00
Jan Vlcinsky
7c6d2ac6a3 README admits, only annonymous client is supported now. 2020-06-21 21:42:45 +02:00
Jan Vlcinsky
36ebce88cf Help text --basic-auth explains it is for protected proxy, not FxA 2020-06-21 21:37:46 +02:00
timvisee
24304200a8
Bump minimum Rust version to 1.40 2020-05-30 16:19:12 +02:00
timvisee
48c2804885
Update dependencies 2020-05-30 16:17:38 +02:00
timvisee
3aab4bcd5f
Update Cargo.lock 2020-05-30 16:16:45 +02:00
timvisee
8e12e2788a
Update Cargo.lock 2020-05-25 19:17:23 +02:00
timvisee
1772ec6b8d
Merge branch 'ignatenkobrain-patch-2'
See https://github.com/timvisee/ffsend/pull/113
2020-05-17 19:17:42 +02:00
Igor Raits
9a0c0eb8d4
README: Improve Fedora description 2020-05-17 19:16:15 +02:00
timvisee
6a94ea759d
Bump version to 0.2.64 2020-05-17 18:14:06 +02:00
timvisee
fd8b4dd050
Fix formatting issue in AUR packages 2020-05-17 18:13:26 +02:00
timvisee
fe0ba4d77b
Bump version to 0.2.63
- OpenSSL is still required on Linux/FreeBSD, update packages
2020-05-17 17:20:04 +02:00
timvisee
727c885f38
Still require OpenSSL on Linux, update README and package definitions
See https://gitlab.com/timvisee/ffsend-api/-/issues/8
2020-05-17 17:19:02 +02:00
timvisee
c670947375
Bump version to 0.2.62
- Implement ring as new crypto backend, used by default
- Disable Send v2 support by default
- Added various new compiler feature flags
- Update dependencies
2020-05-17 15:27:29 +02:00
timvisee
385d9e9224
Update Cargo.lock 2020-05-17 15:24:04 +02:00
timvisee
49f6089f37
Merge branch 'master' of gitlab.com:timvisee/ffsend 2020-05-17 15:22:26 +02:00
timvisee
93f38c5537
Fix build example with no default features missing required feature 2020-05-17 15:21:40 +02:00
Tim Visée
cb7f37efaf Merge branch 'crypto-ring' into 'master'
Implement ring as new crypto backend

See merge request timvisee/ffsend!36
2020-05-17 12:17:58 +00:00
timvisee
b430920157
Revert last change for GitLab CI, do install OpenSSL there 2020-05-17 13:49:06 +02:00
timvisee
3fee8854b7
Do not install OpenSSL on CI 2020-05-17 13:47:40 +02:00
timvisee
eea76722a7
Update compiler feature flag combinations tested in GitLab CI 2020-05-17 13:45:09 +02:00
timvisee
adc20d1357
Update package manifests 2020-05-17 13:42:33 +02:00
timvisee
b18265ab74
Show crypto backend and new compiler features in debug command 2020-05-17 13:42:02 +02:00
timvisee
da21dd894b
Update ffsend to 0.6.0, set up new compiler features 2020-05-17 13:41:04 +02:00
timvisee
60e91abba9
Update compiler flags and OpenSSL notes in README, ring is now default 2020-05-17 13:15:44 +02:00
timvisee
7b610c83d5
Update Cargo.lock 2020-05-16 17:28:47 +02:00
timvisee
3690319a59
Update scoop package manifest URL 2020-05-15 12:40:06 +02:00
timvisee
1ca7720d07
Bump version to 0.2.61 2020-05-11 22:18:01 +02:00
timvisee
bfe4cda42d
Fix github-release binary URL, not in latest release anymore 2020-05-11 22:16:56 +02:00
timvisee
3410340c2d
Bump version to 0.2.60 2020-05-11 21:04:30 +02:00
timvisee
e1b1be6754
Update dependencies 2020-05-11 21:03:00 +02:00
timvisee
20cf8e9ec2
Update Cargo.lock 2020-05-11 20:56:24 +02:00
timvisee
4e1291b3d0
Update Cargo.lock 2020-05-05 01:46:01 +02:00
timvisee
daef5b731c
Update Cargo.lock 2020-04-19 18:11:14 +02:00
timvisee
fba2cad1b6
Update qr2term to v0.2.0 2020-04-19 14:42:34 +02:00
timvisee
7daffe51ec
Revert back to OpenSSL 1.0.2o, but with updated source link 2020-04-03 02:09:28 +02:00
timvisee
62501e4135
Fix OpenSSL download failure on CI, use latest 1.0.2 version 2020-04-03 01:42:07 +02:00
timvisee
6ba3c34362
Bump version to 0.2.59 2020-03-31 02:13:37 +02:00
timvisee
e97b3922f4
Update Cargo.lock 2020-03-31 02:12:54 +02:00
timvisee
a565453949
Generate random name when uploading unnamed archives
Fixes https://github.com/timvisee/ffsend/issues/109
2020-03-31 02:12:13 +02:00
timvisee
5e2b5eb1c2
Merge branch 'master' of gitlab.com:timvisee/ffsend 2020-03-18 19:48:30 +01:00
timvisee
2b34398fbe
Only build docs.rs documentation for GNU Linux 2020-03-18 19:48:13 +01:00
Tim Visée
800fecbb90 Merge branch 'urlshortener-3' into 'master'
Update to urlshortener 3

See merge request timvisee/ffsend!35
2020-03-08 12:59:09 +00:00
Josh Stone
37963f94e7 Update to urlshortener 3 2020-03-06 19:16:29 -08:00
timvisee
5f21b87937
Revert "Remove our complex clipboard logic, use clipboard-ext crate instead"
This reverts commit 8bfe173a18.
2020-02-14 00:24:14 +01:00
timvisee
5b088610dd
Revert "Install python3 dependency in CI"
This reverts commit 0d8bcb08e0.
2020-02-14 00:24:13 +01:00
timvisee
d199a3b62b
Revert "Update minimum Rust version to 1.40"
This reverts commit 0cafab842f.
2020-02-14 00:24:12 +01:00
timvisee
c891f389dc
Revert "Install new test requirements for XCB on CI"
This reverts commit aac53caef4.
2020-02-14 00:24:10 +01:00
timvisee
27854d15bc
Revert "Install XCB dependencies for all jobs globally in CI"
This reverts commit 5afdeccf98.
2020-02-14 00:24:06 +01:00
timvisee
5afdeccf98
Install XCB dependencies for all jobs globally in CI 2020-02-13 22:32:48 +01:00
timvisee
aac53caef4
Install new test requirements for XCB on CI 2020-02-13 21:56:53 +01:00
timvisee
0cafab842f
Update minimum Rust version to 1.40 2020-02-13 21:36:57 +01:00
timvisee
0d8bcb08e0
Install python3 dependency in CI 2020-02-13 21:25:00 +01:00
timvisee
8bfe173a18
Remove our complex clipboard logic, use clipboard-ext crate instead 2020-02-13 20:21:07 +01:00
timvisee
5bea3feffa
Use string slice when setting clipboard using xclip or xsel 2020-02-13 17:26:33 +01:00
timvisee
b780236fa2
Fix duplicate entry in GitLab CI configuration, minor other improvements 2020-02-11 18:24:54 +01:00
timvisee
e73f5d08b4
Minor README tweaks 2020-01-27 14:00:15 +01:00
timvisee
9299ec04eb
Update Cargo.toml version number constraints and Cargo.lock 2020-01-20 18:06:58 +01:00
timvisee
a261afe749
Update ffsend-api dependency to 0.5.0, change min Rust version to 1.39 2020-01-20 18:00:52 +01:00
timvisee
60a130ef2a
Update shell completion scripts 2020-01-10 17:13:36 +01:00
timvisee
a6656c06b2
Fix error in last commit due to incorrect conditional compilation 2020-01-10 16:40:19 +01:00
timvisee
0337632175
Add --delete flag to delete local file after upload 2020-01-10 16:31:09 +01:00
timvisee
fa5560eb9f
Update dependencies 2020-01-10 16:14:16 +01:00
timvisee
638b69325e
Bump version to 0.2.58 2019-12-29 16:12:51 +01:00
timvisee
4c16e38d0d
Update dependencies, add urlshortener compiler feature, fix compilation 2019-12-29 16:11:52 +01:00
timvisee
940093ba13
Merge branch 'kslr-patch-1'
See https://github.com/timvisee/ffsend/pull/103
2019-12-15 14:57:58 +01:00
Kslr
18a578d7d8
Update README.md 2019-12-15 15:55:31 +08:00
timvisee
43eba88b42
Merge branch 'master' of gitlab.com:timvisee/ffsend 2019-12-10 15:32:40 +01:00
timvisee
62f1bce228
Update shell completion scripts 2019-12-10 15:32:24 +01:00
Tim Visée
85b226ca3e Merge branch 'staging' into 'master'
Few small updates and fixes in Cargo.{toml,lock}

See merge request timvisee/ffsend!34
2019-12-06 14:39:33 +00:00
Igor Gnatenko
4100bc69d8
chore: Update ffsend-api in the Cargo.lock
So that dependencies get updated as well.

Signed-off-by: Igor Gnatenko <i.gnatenko.brain@gmail.com>
2019-12-05 22:27:25 +01:00
Igor Gnatenko
092d47176a
chore: Update chbs to 0.0.9
Signed-off-by: Igor Gnatenko <i.gnatenko.brain@gmail.com>
2019-12-05 22:25:16 +01:00
Igor Gnatenko
b93e457a52
Revert "chore: Include contrib folder into the crates.io tarball"
I am very sorry, but I realized that those completions can be easily
generated by produced ffsend binary so after all we don't need to ship
those files.

This reverts commit c740ddc676.

Signed-off-by: Igor Gnatenko <i.gnatenko.brain@gmail.com>
2019-12-05 22:23:31 +01:00
timvisee
55e165f315
Bump version to 0.2.57 2019-12-05 14:38:33 +01:00
timvisee
31c5fff9c7
Merge branch 'ignatenkobrain-patch-1'
See https://github.com/timvisee/ffsend/pull/102
2019-12-05 14:36:41 +01:00
Igor Gnatenko
c740ddc676
chore: Include contrib folder into the crates.io tarball
Otherwise distributions can't make completions available.
2019-12-05 14:28:52 +01:00
timvisee
86d8d5e329
Remove redundant xclip -f flag because we pipe stdout to null now
See https://gitlab.com/timvisee/ffsend/issues/93
2019-12-04 14:00:56 +01:00
Tim Visée
10f5448c0d Merge branch 'remove_aurget_reference' into 'master'
Remove reference to Aurget

See merge request timvisee/ffsend!33
2019-12-04 12:52:01 +00:00
Bram Crielaard
43c6da8cda
Keep recommending ffsend-bin 2019-12-03 19:59:10 +01:00
Bram Crielaard
d2427d78a9
Remove reference to Aurget
Aurget has been deprecated: https://github.com/pbrisbin/aurget/issues/66
As such I would recommend removing it from the install instructions.
2019-12-03 19:59:10 +01:00
timvisee
34b829fca1
Bump version to 0.2.56 2019-12-03 13:48:19 +01:00
timvisee
892e959945
Update dependencies 2019-12-03 13:47:44 +01:00
timvisee
ff657683cc
Pipe clipboard process output to null on Linux 2019-12-03 13:47:25 +01:00
Tim Visée
e9f7dde6fa Merge branch 'fix_xclip_hanging' into 'master'
Add -f flag to xclip, fixing hanging

Closes #93

See merge request timvisee/ffsend!31
2019-12-03 12:35:47 +00:00
Bram Crielaard
69dfaa5970
Add -f flag to xclip, fixing hanging
Fixes #93
2019-12-03 13:34:07 +01:00
timvisee
1082df1d2c
Add Android Termux installation instructions 2019-12-01 12:16:18 +01:00
timvisee
801b946abd
Update dependencies 2019-11-29 16:13:23 +01:00
timvisee
bf36925786
Update minimum Rust requirement to 1.37, fixing CI build 2019-11-06 17:33:46 +01:00
timvisee
624be49503
Update dependencies 2019-11-06 16:52:26 +01:00
timvisee
53649ebbb3
Use duration syntax for CLI timeout options as well 2019-11-04 17:43:51 +01:00
timvisee
e9a2b715d1
Do not snap upload expiry time to fixed list, now allows any duration 2019-11-03 23:07:14 +01:00
timvisee
a4305e19da
Bump version to 0.2.55, fix history for uploads with custom expiry 2019-10-30 21:51:18 +01:00
timvisee
4fd67baa71
Update ffsend-api to 0.4.1, fix history for uploads with custom expiry 2019-10-30 21:50:32 +01:00
timvisee
43c1480233
Fix typo introduced in last commit 2019-10-28 12:38:07 +01:00
timvisee
cc88186b0e
Mention that newer OpenSSL versions can be installed on Windows as well 2019-10-28 12:36:55 +01:00
timvisee
7729ee258d
Bump version to 0.2.54 2019-10-25 15:05:02 +02:00
Tim Visée
4a89cc3f82 Merge branch '85-make-upload-expiry-time-configurable' into 'master'
Resolve "Make upload expiry time configurable"

Closes #85

See merge request timvisee/ffsend!30
2019-10-25 13:03:48 +00:00
timvisee
5f21f49aec
Bump version to 0.2.53 2019-10-25 14:56:49 +02:00
timvisee
6326a88d42
Use hosted ffsend-api, update dependencies 2019-10-25 14:52:57 +02:00
timvisee
9a633d4941
Update README to reflect latest changes 2019-10-24 17:29:08 +02:00
timvisee
98150f7f44
Use human readable format to specify expiry time 2019-10-24 17:27:32 +02:00
timvisee
e39add8ffd
Make file expiry time configurable, update ffsend-api to v0.4.0-git 2019-10-24 16:42:50 +02:00
timvisee
9076744ead
Add notice in README for snap package, it might be troublesome 2019-10-16 22:22:37 +02:00
timvisee
34ee42cf28
Update GitHub sponsor configuration 2019-10-12 14:55:27 +02:00
timvisee
a3a7458478
Update GitHub sponsor configuration 2019-10-07 00:53:43 +02:00
timvisee
b4ed399250
Update Cargo.lock 2019-10-07 00:53:34 +02:00
timvisee
fce3996f31
Attempt to fix snap release, update Windows SSL dependency to fix release 2019-09-30 00:07:56 +02:00
timvisee
76a1328a77
Bump version to 0.2.52, update dependencies to fix nightly compilation 2019-09-29 22:24:18 +02:00
timvisee
5af629860a
Update ffsend-api dependency, update Cargo.lock 2019-09-29 22:16:26 +02:00
timvisee
40d2f351a5
Add Alpine package APKBUILD snippet, proudly stolen from Alpine pkg index 2019-09-24 22:04:55 +02:00
timvisee
29c5c8c975
Add contributed Alpine package to README, link some existing packages 2019-09-24 22:00:44 +02:00
timvisee
17c7f62787
Remove crate download count badge 2019-09-08 17:41:38 +02:00
timvisee
e98ddafda7
Bump version to 0.2.51, install shell completions and update dependencies 2019-08-29 22:07:02 +02:00
timvisee
b69933f200
Fix shell completion installation, add ZSH file and LICENSE in AUR 2019-08-28 19:59:04 +02:00
timvisee
4c185539a4
Update shell completion files 2019-08-28 19:39:22 +02:00
timvisee
0c9f0a01c3
Bump version to 0.2.50, update dependencies 2019-08-25 15:34:49 +02:00
timvisee
f2b98373d9
Update dependencies and Cargo.lock to latest versions, fix some warnings 2019-08-25 15:14:33 +02:00
timvisee
8c554ac43f
Update urlshortener dependency to v2.0 2019-08-25 15:05:25 +02:00
timvisee
e369b581ec
Fix GitLab CI configuration for latest DAG change 2019-08-23 17:33:14 +02:00
timvisee
777ce46fb1
Use DAG in pipeline to speed up overall process 2019-08-23 17:32:15 +02:00
timvisee
bfe30e1be3
Change minimum Rust version to 1.34 2019-08-04 23:17:12 +02:00
timvisee
091fd35410
Update dependencies 2019-08-04 22:49:07 +02:00
timvisee
20c69011ab
Make -P conflict with -p 2019-08-04 22:39:51 +02:00
timvisee
c6066d6ca2
Exclude some more files from packaging, update Cargo.lock 2019-07-17 14:59:29 +02:00
timvisee
c8f3275ed7
Bump version to 0.2.49, update dependencies 2019-06-29 16:14:12 +02:00
timvisee
98fe01c218
Add --clear/--rm flag/option to clear history/remove specific item 2019-06-29 15:57:07 +02:00
timvisee
0394579a2d
Update upload limit in README for Firefox Send service 2019-06-22 22:32:05 +02:00
timvisee
d81b7951ad
Bump version to 0.2.48 2019-06-21 00:20:37 +02:00
timvisee
2babe1af63
Update used OpenSSL version for Windows release 2019-06-21 00:19:53 +02:00
timvisee
ea2286151b
Fix Docker image release job, do not run command with non-zero exit code 2019-06-21 00:18:34 +02:00
timvisee
7e27d7928a
Bump version to 0.2.47, update dependencies 2019-06-20 22:40:41 +02:00
Tim Visée
02559bc6ce Merge branch '30-allow-multi-file-uploads' into 'master'
Support multiple files for uploading through archive

See merge request timvisee/ffsend!29
2019-06-20 20:36:50 +00:00
timvisee
58d44f09d9
Find shared directory for multiple files, use as archive base directory 2019-06-20 22:35:16 +02:00
timvisee
dee5289597
Require user to set file name when archiving multiple files 2019-06-20 21:29:32 +02:00
timvisee
a7582ae765
Show error when archiving files outside working directory 2019-06-20 21:22:03 +02:00
timvisee
ce39eada4c
Allow uploading multiple files by archiving, rework archiving 2019-06-20 21:16:45 +02:00
timvisee
12503d0d21
Update Cargo.lock 2019-06-05 01:31:13 +02:00
timvisee
b228e41753
Update README to match changes in latest commit by @Gahr 2019-06-03 00:47:36 +02:00
Tim Visée
1edf1f8485 Merge branch 'only-output-link' into 'master'
Only output link to stdout

See merge request timvisee/ffsend!28
2019-06-02 20:12:23 +00:00
Wesley Gahr
9cd1f77717
Only output link to stdout when calling upload (without other text) 2019-06-02 21:51:56 +02:00
timvisee
de5a9a470c
Return non-zero exit code when running ffsend with no arguments 2019-06-01 21:39:00 +02:00
timvisee
0adf815e62
Update dependencies 2019-05-30 14:17:56 +02:00
timvisee
4c0629d178
Update Cargo.lock 2019-05-23 22:17:38 +02:00
timvisee
533f1d9444
Add Patreon funding URL 2019-05-23 21:30:40 +02:00
timvisee
e5aa72b971
Add GitHub Sponsors funding links 2019-05-23 21:15:38 +02:00
timvisee
9e504f71ea
Add basic security policy based on GitHub template 2019-05-23 21:05:10 +02:00
timvisee
15a7285ae1
Remove frozen flag from Cargo check due to error 2019-05-10 16:02:58 +02:00
timvisee
757d672df6
Add simple check for up-to-date Cargo.lock in CI, update dependencies 2019-05-10 15:51:35 +02:00
timvisee
7751898851
Bump version to 0.2.46, update dependencies 2019-05-04 14:44:32 +02:00
Tim Visée
d4efd984eb Merge branch 'fix-freebsd-clipboard' into 'master'
Use alternate clipboard method for FreeBSD and other BSD derivatives

See merge request timvisee/ffsend!26
2019-05-04 12:34:22 +00:00
timvisee
0f4e841779
Merge remote-tracking branch 'origin/master' into fix-freebsd-clipboard 2019-05-04 14:33:30 +02:00
timvisee
7ca71ce163
Merge branch '0mp/ffsend-master' into 'master'
See merge request !27
2019-05-04 14:31:19 +02:00
timvisee
4e9603702b
Indent README with spaces 2019-05-04 14:29:36 +02:00
timvisee
cce6218b11
Move FreeBSD items around, move behind Windows 2019-05-04 14:27:54 +02:00
Mateusz Piotrowski
3ae80cbe7b Fix Gentoo indentation 2019-05-02 18:31:12 +02:00
Mateusz Piotrowski
dda9f2c8a1 Add FreeBSD installation and building instructions 2019-05-02 18:30:44 +02:00
timvisee
307284e684
Update README for clipboard support changes 2019-05-02 17:27:55 +02:00
timvisee
d46a40c4a4
Clean up conditional compilation for clipboard support with sub features 2019-05-02 17:23:38 +02:00
timvisee
9982049633
Fix invalid change in last commit causing compilation error 2019-05-02 17:01:49 +02:00
timvisee
ab55390ba8
Use alternate clipboard method for FreeBSD and other BSD derivatives 2019-05-02 16:37:45 +02:00
timvisee
d5bc5a4289
Change OpenSSL download to self hosted mirror due to host being down 2019-05-02 12:35:27 +02:00
timvisee
5951ce5ccc
Update dependencies 2019-04-21 15:40:11 +02:00
timvisee
0a08718b2c
Bump version to 0.2.45, update dependencies 2019-04-18 21:17:22 +02:00
timvisee
59c7ec627f
Show error when attempting to upload file of 0 bytes
Fixes timvisee/ffsend#86
2019-04-18 21:12:53 +02:00
timvisee
22bba025cb
Update dependencies 2019-04-11 17:28:21 +02:00
timvisee
7bfff9a87f
Update dependencies 2019-04-09 18:12:15 +02:00
timvisee
1e86e9d6b3
Update version-compare to 0.0.7 2019-04-09 18:11:35 +02:00
timvisee
0e3f36090c
Fix checked downloads limit not being used for parameters action 2019-04-05 17:13:25 +02:00
timvisee
37579b8f65
Fix force flag not working with invalid download limits 2019-04-05 17:06:16 +02:00
timvisee
1754bda00f
Bump version to 0.2.44 2019-04-05 17:02:42 +02:00
timvisee
e6e84cdcf2
Optimize imports causing a warning 2019-04-05 17:02:13 +02:00
timvisee
888b10afd6
Respect download limits enforced by remote server, update ffsend-api
See issue timvisee/ffsend#17
2019-04-05 16:54:34 +02:00
timvisee
b59650f25d
Switch from quale to which, properly optionally import clipboard crates 2019-04-05 15:15:05 +02:00
timvisee
6feea9b463
Fix AUR binary package setting invalid binary URL in CI on release 2019-04-04 12:12:04 +02:00
timvisee
469b554244
Update dependencies 2019-04-04 10:50:29 +02:00
timvisee
3a59911965
Fix sed commands for AUR packages, escape variables 2019-04-04 10:48:48 +02:00
timvisee
864e94ede3
Comment-out faulty AppVeyor CI statement for GitHub release 2019-04-02 17:13:30 +02:00
timvisee
5a7472cee0
Bump version to 0.2.43, update dependencies 2019-04-02 16:33:02 +02:00
timvisee
d092d7527a
Merge branch 'axionl-master' into 'master'
See pull request timvisee/ffsend#35
2019-04-02 16:31:32 +02:00
timvisee
ed75ed8227
Set proper completion file URLs for AUR 2019-04-02 16:21:26 +02:00
timvisee
156d5db765
Use and cache versioned files for AUR, fix inconsistencies [ci skip] 2019-04-02 16:09:27 +02:00
axionl
e68e0feedd
Add auto completions for bash and fish shell. 2019-04-02 22:40:10 +09:00
timvisee
e52b357e86
Update dependencies 2019-04-01 15:12:56 +02:00
timvisee
2804e42a06
Merge branch 'axionl-master' into 'master'
See pull request timvisee/ffsend#32
2019-03-30 18:29:51 +01:00
axionl
7084bfa8c8
Modify the name to avoid using the locally cached file 2019-03-31 01:04:06 +09:00
timvisee
fbbc84db25
List ffsend-bin AUR package in README again 2019-03-29 18:09:00 +01:00
timvisee
8df3f5ecc7
Remove ffsend-bin AUR package from README for now, mention ffsend package 2019-03-27 19:17:13 +01:00
timvisee
11577a2038
Fix package base for AUR git package, use double quotes to infer vars 2019-03-27 18:13:27 +01:00
timvisee
24629eebdf
Explicitly set AUR binary permission, rename AUR SHA check variables 2019-03-27 16:38:12 +01:00
timvisee
3075b8e8fc
Disable automatic homebrew package releases for now to throttle
See pull request https://github.com/Homebrew/homebrew-core/pull/38274
2019-03-26 14:11:12 +01:00
Tim Visée
29a54d7724 Merge branch 'aur-fix' into 'master'
Fix AUR package names, add git master package, improve automatic releases

Closes #64

See merge request timvisee/ffsend!25
2019-03-26 13:08:52 +00:00
timvisee
a5f3086dcf
Only update AUR git package on release if PKGBUILD has changed 2019-03-26 14:06:26 +01:00
timvisee
e6118122d5
Define ffsend-git AUR package as well, release it through CI 2019-03-26 13:49:16 +01:00
timvisee
100f955071
Fix AUR installer name, should be AUR helper as noted by @murlakatamenka
See merge request timvisee/ffsend!24

Co-authored-by: murlakatamenka <murlakatamenka@disroot.org>
2019-03-26 13:40:57 +01:00
timvisee
4f7e1f5c92
Merge remote-tracking branch 'origin/master' into aur-fix 2019-03-26 13:33:59 +01:00
Tim Visée
f8584df22b Merge branch 'master' into 'master'
Fix AUR package URL; remove yaourt example

See merge request timvisee/ffsend!24
2019-03-26 12:31:28 +00:00
murlakatamenka
88fa345a33 Replace ffsend with ffsend-bin in README 2019-03-26 15:27:56 +03:00
timvisee
e224033286
Merge remote-tracking branch 'origin/master' into aur-fix 2019-03-26 13:08:45 +01:00
timvisee
9ea0d3ba89
Fix some issues as noted by @lilyball
Co-authored-by: Lily Ballard <lily@sb.org>
2019-03-26 13:05:55 +01:00
timvisee
46750793bf
Merge branch 'wilmardo-fix-windows-ci-build'
See pull request timvisee/ffsend#31
2019-03-26 12:59:46 +01:00
timvisee
a4f9a23975
Mention PKGBUILD source locations in a comment 2019-03-26 12:47:21 +01:00
wilmardo
74ca880f11 Fixes the powershell hack for Windows Github release creation 2019-03-26 12:41:14 +01:00
timvisee
8872f375ca
Update AUR package release pipeline: proper names, proper URLs, sha sums 2019-03-26 12:36:16 +01:00
murlakatamenka
387b018182 Restore trimmed trailing whitespaces in README 2019-03-26 11:30:13 +03:00
murlakatamenka
01f8b50d03 Fix AUR package URL; remove yaourt example 2019-03-26 10:27:50 +03:00
timvisee
8f0fdc9751
Force brew package release pull requests, disable errorous AppVeyor command again 2019-03-25 21:47:42 +01:00
timvisee
4e7cdde829
Bump version to 0.2.42 2019-03-25 21:01:52 +01:00
timvisee
dbbf8ac8af
Fix missing import on Windows and macOS 2019-03-25 21:01:05 +01:00
timvisee
7a5a2379e8
Bump version to 0.2.41 2019-03-25 20:35:38 +01:00
timvisee
f0cac5767f
Fix Windows and macOS builds, only use quale dependency on Linux 2019-03-25 20:34:54 +01:00
timvisee
df04b7ae29
Bump version to 0.2.40, update dependencies 2019-03-25 20:22:32 +01:00
timvisee
cc5ad4c9b6
Attemp to fix GitHub release creation on AppVeyor once again 2019-03-25 20:21:31 +01:00
timvisee
8d7bbce4d0
Mention Fedora package in README 2019-03-25 20:12:35 +01:00
timvisee
53e5843fed
Rebuild if XCLIP_PATH or XSEL_PATH environment variable is changed 2019-03-25 19:16:50 +01:00
timvisee
5702367b84
Update clipboard error messages, to reflect support for xclip and xsel 2019-03-25 19:09:43 +01:00
timvisee
982027f33c
Add support for xsel clipboard as well, dynamically select at runtime
Fixes timvisee/ffsend#76
2019-03-25 18:55:34 +01:00
timvisee
c2dfd3e174
Show clipboard information on Linux in debug command output
Fixes gitlab.com/timvisee/ffsend#80
2019-03-25 17:55:53 +01:00
timvisee
32e0e1c48f
Do not use empty binary names 2019-03-25 17:40:53 +01:00
timvisee
f6bf067ff3
Attempt to fix brew push issue, explicitly save Git credentials on host 2019-03-24 18:28:23 +01:00
timvisee
de7945645a
Remove completer property from snapcraft configuration, not supported 2019-03-24 18:22:35 +01:00
timvisee
f51bd673ff
Add nix package installation instructions in README 2019-03-23 19:08:07 +01:00
timvisee
e9d5bec4f5
Bump version to 0.2.39, update dependencies 2019-03-23 18:58:15 +01:00
timvisee
13edd30ff4
Add bash completions file to snap package 2019-03-23 18:56:59 +01:00
timvisee
9d98220083
Move contributing guidelines file to a path GitHub recognizes 2019-03-23 18:54:56 +01:00
timvisee
6158cf6d14
Some contributing guideline improvements 2019-03-23 18:50:32 +01:00
timvisee
a29f380dbe
Add contributing guideline stubs 2019-03-23 18:47:10 +01:00
timvisee
40504e0fd3
Fix homebrew package installation command 2019-03-23 18:00:29 +01:00
timvisee
dc62db19d9
Mention macOS homebrew installation instructions in README 2019-03-23 17:54:35 +01:00
timvisee
d1571ac57f
Automate homebrew package updates using Travis CI 2019-03-23 17:48:27 +01:00
timvisee
fdbdec0722
Update rpassword dependency to 3.0.0 2019-03-21 09:23:14 +01:00
timvisee
c8a854d492
Fix unused import on non-Linux systems
See issue github.com/timvisee/ffsend#29
2019-03-20 00:26:47 +01:00
timvisee
14dbfdfd79
Improve README 2019-03-20 00:23:15 +01:00
timvisee
1376218d30
Describe scriptability in README 2019-03-20 00:20:06 +01:00
timvisee
c2920b507b
Show crate version in debug output 2019-03-19 23:32:20 +01:00
timvisee
8744e05372
Mention previous releases in README for not yet available artifacts 2019-03-19 23:30:51 +01:00
timvisee
8a7703a2ac
Test more compiler feature flag combinations on CI 2019-03-19 23:28:38 +01:00
timvisee
c04ad07b05
Bump version to 0.2.38, update dependencies 2019-03-19 23:19:34 +01:00
Tim Visée
12e6606164 Merge branch 'configurable-client' into 'master'
Add basic HTTP authentication support, update ffsend API dependency

See merge request timvisee/ffsend!23
2019-03-19 22:18:25 +00:00
timvisee
4890b59bd7
Update ffsend-api to 0.3.0 for basic HTTP authentication support 2019-03-19 23:14:20 +01:00
timvisee
556eed4a71
Implement basic HTTP authentication argument and environment variable 2019-03-19 22:09:42 +01:00
timvisee
0052a60503
Implement configurable ffsend API client for new ffsend API update 2019-03-19 21:53:56 +01:00
Tim Visée
2f10b2f057 Merge branch 'xclip_path' into 'master'
Support XCLIP_PATH at compilation to control the path to xclip

Closes #71

See merge request timvisee/ffsend!22
2019-03-19 06:59:15 +00:00
Lily Ballard
8bcf0e4a0e Support XCLIP_PATH at compilation to control the path to xclip
If the `XCLIP_PATH` env var is set during compilation, it will be used
to determine what binary to invoke for copying to the clipboard on
Linux. Otherwise `xclip` will be looked up in the `PATH`.

Fixes #71.
2019-03-18 21:23:57 -07:00
timvisee
f9dcdb0531
Bump version to 0.2.37, update dependencies 2019-03-18 22:42:44 +01:00
timvisee
a86b416640
Merge branch 'nagyf-history_file_permissions' into 'master'
See pull request timvisee/ffsend#26
2019-03-18 22:37:19 +01:00
Ferenc Nagy
50f27189de Set history file permissions on UNIX based systems 2019-03-18 22:29:39 +01:00
timvisee
9b1cf9dab4
Minor README improvements [ci skip] 2019-03-18 21:29:12 +01:00
timvisee
47db4dc7e9
Mention Docker alias in README 2019-03-18 17:09:03 +01:00
timvisee
0f96e3fd2e
Mark generated completion files as vendored for Linguist 2019-03-18 16:54:51 +01:00
timvisee
fba888c5aa
Add common shell completions, fix invalid quiet check 2019-03-18 16:35:02 +01:00
timvisee
2fe8e24fb1
Add command to generate completions 2019-03-18 16:31:43 +01:00
timvisee
d55b3e8ef4
Move zsh completions file 2019-03-18 16:29:40 +01:00
timvisee
1a64e40aa2
Bump version to 0.2.36 2019-03-17 19:46:33 +01:00
timvisee
c6752231be
Remove license property from snapcraft configuration again 2019-03-17 19:45:50 +01:00
timvisee
0c3ac720a5
Bump version to 0.2.35 2019-03-17 18:00:08 +01:00
timvisee
7240ef30ab
Improve README for ffput and ffget 2019-03-17 17:56:18 +01:00
timvisee
a6deb9155b
Implement subcommand inferring based on calling name with feature flag
See issue timvisee/ffsend#24
2019-03-17 17:48:01 +01:00
timvisee
1fc189d4d9
Fix bin_name function giving incorrect results when symlinked 2019-03-17 16:43:20 +01:00
timvisee
d27b48aad0
Add license property to snapcraft configuration 2019-03-16 20:41:38 +01:00
timvisee
e717b1f1a9
Bump version to 0.2.34 2019-03-16 15:52:01 +01:00
timvisee
a694ded0fa
Add desktop interface/plug to snap package, to allow opening in browser
See issue timvisee/ffsend#66
2019-03-16 15:50:25 +01:00
timvisee
55d1934868
Update toml dependency 2019-03-16 15:43:23 +01:00
timvisee
ed049472b9
Merge branch 'nathfreder-patch-1' into 'master'
See pull request timvisee/ffsend#23
2019-03-16 01:40:41 +01:00
timvisee
e162fdb585
Merge branch 'patch-1' of git://github.com/nathfreder/ffsend into nathfreder-patch-1 2019-03-16 01:40:27 +01:00
Nathaniel Fredericks
413328392d
chore(README.md): Fix macOS bin directory 2019-03-15 16:27:22 -03:00
timvisee
085035bf88
List all compiler features in debug output 2019-03-15 13:51:55 +01:00
Tim Visée
32e078e1e1 Merge branch 'master' into 'master'
chore: Update urlshortener to 0.10

See merge request timvisee/ffsend!21
2019-03-15 11:55:42 +00:00
Igor Gnatenko
d142b6183e
chore: Update urlshortener to 0.10
Signed-off-by: Igor Gnatenko <i.gnatenko.brain@gmail.com>
2019-03-15 09:39:00 +01:00
timvisee
c3c5a6b70d
Bump version to 0.2.33 2019-03-15 01:46:37 +01:00
timvisee
a4a5dd156e
Disable the GitHub release creation task for AppVeyor, until it's fixed 2019-03-15 01:45:49 +01:00
timvisee
f9833dac55
Update README [ci skip] 2019-03-15 01:16:41 +01:00
timvisee
da49dc5345
Fix incorrect conditional compilation feature 2019-03-15 01:12:28 +01:00
timvisee
ddf0e191a3
Test new compiler feature flag combinations in CI 2019-03-15 01:05:02 +01:00
timvisee
c15ef067fe
Bump version to 0.2.32 2019-03-15 00:58:52 +01:00
Tim Visée
866579c80b Merge branch 'shorten-urls' into 'master'
Implement URL shortener support

Closes #68

See merge request timvisee/ffsend!20
2019-03-14 23:57:17 +00:00
timvisee
dd3a4c561f
Follow URL redirects before downloading, this passes URL shorteners 2019-03-15 00:54:56 +01:00
timvisee
dfd9a39633
Attempt to fix AppVeyor release job 2019-03-15 00:24:13 +01:00
timvisee
06ec14c2ee
Do not test ffsend Docker container in interactive mode 2019-03-15 00:22:56 +01:00
timvisee
e2ebbc91a9
Prompt the user to confirm shortening the URL, may be security risk 2019-03-15 00:18:37 +01:00
timvisee
6de33446c7
Implement proper error handling for URL shortening 2019-03-15 00:10:19 +01:00
timvisee
cca5889e39
Implement first URL shortening logic using is.gd 2019-03-14 23:50:38 +01:00
timvisee
417bf803e7
Bump version to 0.2.31, update dependencies 2019-03-14 23:24:51 +01:00
timvisee
2a95eccd0e
Attempt to ignore AppVeyor GitHub release errors 2019-03-14 23:24:08 +01:00
timvisee
01cba1fe35
Update scoop manifest, specify dependency, update related READMEs 2019-03-14 22:30:02 +01:00
Tim Visée
2fe025e88a Merge branch 'master' into 'master'
Created scoop app manifest for ffsend 0.2.30

See merge request timvisee/ffsend!18
2019-03-14 21:25:29 +00:00
Tim Visée
6d58e5efe3 Merge branch 'qrcode' into 'master'
Add support for showing a QR code for a share URL after uploading

See merge request timvisee/ffsend!19
2019-03-14 21:13:06 +00:00
timvisee
176775d9cf
Add support for showing a QR code for a share URL after uploading 2019-03-14 22:12:21 +01:00
timvisee
c068e4c143
Add scoop package instructions 2019-03-14 10:11:58 +01:00
Maxime Blais
41db446812 Created scoop app manifest for ffsend 0.2.30 2019-03-13 17:49:16 -04:00
timvisee
2b2b592155
Merge branch 'master' of gitlab.com:timvisee/ffsend 2019-03-13 20:52:16 +01:00
timvisee
601034e87f
Use proper image, and enable Docker in Docker for Dockerfile job on CI 2019-03-13 20:51:54 +01:00
Tim Visée
e001bfa0db Merge branch 'brew_correction' into 'master'
Updated macOS install information in README.md

See merge request timvisee/ffsend!16
2019-03-13 18:14:21 +00:00
timvisee
09c6ef5764
Merge branch 'ogarcia-zsh-completion' into 'master'
See pull request timvisee/ffsend#16
2019-03-13 19:08:57 +01:00
timvisee
391fe019f6
Merge branch 'zsh-completion' of git://github.com/ogarcia/ffsend into ogarcia-zsh-completion 2019-03-13 19:08:45 +01:00
timvisee
9a6c45b8f3
Bump version to 0.2.30, update dependencies 2019-03-13 18:59:25 +01:00
Tim Visée
a6ce935ea4 Merge branch 'pkg-docker' into 'master'
Create Docker image for easily running ffsend

See merge request timvisee/ffsend!17
2019-03-13 17:57:45 +00:00
timvisee
fad390f584
Describe Docker image usage in README 2019-03-13 18:56:04 +01:00
Óscar García Amor
0e34e2b7aa Added zsh completion
Signed-off-by: Óscar García Amor <ogarcia@connectical.com>
2019-03-13 18:52:36 +01:00
timvisee
5de3f89de7
Add simple Docker image and Docker release job on CI 2019-03-13 18:41:36 +01:00
timvisee
2921f6463d
Merge branch '0xflotus-patch-1'
See pull request timvisee/ffsend#15
2019-03-13 14:57:12 +01:00
0xflotus
af98b77f74
Update README.md 2019-03-13 14:56:24 +01:00
0xflotus
44ea30cbe2
fixed Upcoming 2019-03-13 14:54:43 +01:00
Fabian Rodriguez
47879a7d1a Update README, some typos and Linux mentions
Co-authored-by: Tim Visée <timvisee@gmail.com>

See pull request timvisee/ffsend#13
2019-03-13 13:09:04 +01:00
timvisee
77db03a494
Fix WSL link in README. Fixes timvisee/ffsend#14 2019-03-13 12:27:12 +01:00
timvisee
f07a30b859
Mention the snap package is isolated to the home directory in README 2019-03-13 12:14:00 +01:00
timvisee
bd034a40d9
Install specific OpenSSL version on TravisCI for macOS releases as well
See pull request timvisee/ffsend#11
2019-03-13 02:46:38 +01:00
timvisee
626d830d83
Merge branch 'sent-hil-master' into 'master'
Specify which version of OpenSSL to install in README for macOS

See pull request timvisee/ffsend#11
2019-03-13 02:25:19 +01:00
Brian A. Carter
b1f27fc174 1. Corrected name of homebrew command, 2) Added specific required OpenSSL. 2019-03-12 20:53:44 -04:00
Senthil Arivudainambi
af614f55c3 specify which version of openssl to install in readme 2019-03-12 17:04:21 -07:00
timvisee
a2efbe9ce1
Bump version to 0.2.29 2019-03-12 21:45:27 +01:00
timvisee
fdf8ae9201
Use shorter passphrase words, make them less than 32-characters long 2019-03-12 21:39:21 +01:00
timvisee
9a27be259d
Fix public Send test failure due to incorrect silent flag 2019-03-12 19:09:20 +01:00
Tim Visée
8ac8f73ca7 Merge branch 'improve-readme-install' into 'master'
Update installation instructions in README

See merge request timvisee/ffsend!15
2019-03-12 17:08:09 +00:00
timvisee
90de83e142
Update installation instructions in README 2019-03-12 18:07:36 +01:00
timvisee
bbe2224f18
Mention upcoming Firefox Account support in README [ci skip] 2019-03-12 16:47:39 +01:00
timvisee
10905384af
Set public Send test download count to 1 to fix non authenticated issue 2019-03-12 16:46:03 +01:00
timvisee
37f2ef3f8b
Allow public CI test failures for now 2019-03-12 16:04:28 +01:00
timvisee
27050f7d44
Bump version to 0.2.28, update ffsend-api to 0.2.3 2019-03-12 15:32:01 +01:00
timvisee
0c93617b63
Fix CI configuration parsing issue 2019-03-12 14:23:45 +01:00
timvisee
eaa097e3c8
Assume Firefox Send v3 as default for now 2019-03-12 14:17:13 +01:00
timvisee
b1bf3554b4
Remove old public Send test from CI 2019-03-12 14:14:54 +01:00
timvisee
d6dbc66941
Only install CA certificates for public Send test 2019-03-11 12:12:26 +01:00
timvisee
941ee52ab5
Fix public Send test on CI, install missing OpenSSL dependency 2019-03-10 23:51:27 +01:00
timvisee
8cb9f3b6b1
Improve public Send test, smaller file, no password, proper cleanup 2019-03-10 22:39:19 +01:00
timvisee
72bf4a403c
Disable first public upload test for now 2019-03-10 21:32:18 +01:00
timvisee
7ef5685ec1
Bump version to 0.2.27 2019-03-10 20:29:08 +01:00
timvisee
9d6217a484
Disable Chocolatey package publishes for now 2019-03-10 20:28:37 +01:00
timvisee
9809d2d373
Bump version to 0.2.26, update dependencies 2019-03-10 18:53:52 +01:00
timvisee
f0a8eee7f2
Attempt to fix GitHub publish of nupkg package, properly expand wildcard 2019-03-10 18:52:08 +01:00
timvisee
69cbdd07ba
Bump version to 0.2.25 2019-03-10 00:55:44 +01:00
timvisee
9a4e79e02b
Publish Chocolatey package as last step 2019-03-10 00:54:58 +01:00
timvisee
743fdf38ef
Bump version to 0.2.24 2019-03-08 17:15:42 +01:00
Tim Visée
04b267cb5b Merge branch 'pkg-choco' into 'master'
Build Chocolatey packages for releases

See merge request timvisee/ffsend!14
2019-03-08 16:14:45 +00:00
timvisee
8f6354f15a
Prepare AppVeyor configuration for next release with Chocolatey package 2019-03-08 17:13:49 +01:00
timvisee
1eee2b1db1
Attempt to fix setting nuspec version on CI 2019-03-08 16:54:03 +01:00
timvisee
4723059274
Attempt setting package version with PowerShell, remove obsolete install script 2019-03-08 16:21:28 +01:00
timvisee
7b388ce03b
Attempt to fix shell script for versioning not running 2019-03-07 21:45:48 +01:00
timvisee
ac43a6e774
Explicitly install OpenSSL, attempt to push chocolatey package 2019-03-07 21:26:15 +01:00
timvisee
644581caf0
Only build static binary while testing 2019-03-07 21:16:03 +01:00
timvisee
f7d69c8ab3
Install specific OpenSSL Light version 2019-03-07 21:14:44 +01:00
timvisee
c47f81df14
Merge remote-tracking branch 'origin/master' into pkg-choco 2019-03-07 21:12:56 +01:00
timvisee
4433d09ae5
Experiment with building a chocolatey package for ffsend on AppVeyor 2019-03-07 21:12:46 +01:00
timvisee
f099f0c553
Bump version to 0.2.23 2019-03-07 10:50:05 +01:00
timvisee
103b6453b8
Remove invalid license property from snapcraft config [ci skip] 2019-03-07 10:48:11 +01:00
timvisee
ed0798cb8a
Merge remote-tracking branch 'origin/master' into pkg-choco 2019-03-07 09:48:35 +01:00
timvisee
e5ebc80a87
Bump version to 0.2.22 2019-03-07 09:47:56 +01:00
timvisee
983193a906
Add x11 plug to snapcraft configuration for clipboard support [ci skip] 2019-03-07 09:47:22 +01:00
timvisee
52d7ed6e16
Update openssl.light version constraint, set stubs in AppVeyor config [ci skip] 2019-03-07 00:53:39 +01:00
timvisee
91530e0465
Merge remote-tracking branch 'origin/master' into pkg-choco 2019-03-07 00:37:24 +01:00
timvisee
a2c28f827a
Bump version to 0.2.21 2019-03-07 00:37:07 +01:00
timvisee
cbdb15fcc3
Enable network plug for snapcraft package [ci skip] 2019-03-07 00:36:31 +01:00
timvisee
196376e7b6
Add Chocolatey package definition [WIP] [ci skip] 2019-03-07 00:28:39 +01:00
timvisee
38b1da71f0
Add application icon [ci skip] 2019-03-06 23:47:09 +01:00
timvisee
63cb9b455c
Bump version to 0.2.20 2019-03-06 23:25:46 +01:00
timvisee
4dab2a3705
Use strict confinement for snapcraft package [ci skip] 2019-03-06 23:24:54 +01:00
timvisee
0f2abac0c9
Explicitly install/update OpenSSL dependencies for macOS builds [ci skip] 2019-03-06 23:06:18 +01:00
timvisee
ac56dc77c7
Mention OpenSSL requirement for macOS in README [ci skip] 2019-03-06 22:31:02 +01:00
timvisee
b4c5532f7d
Bump version to 0.2.19 2019-03-06 22:22:12 +01:00
timvisee
0ac37fb8fd
Remove extern crate imports not needed anymore in Rust 2018 [ci skip] 2019-03-06 22:21:15 +01:00
timvisee
b3ffbc3b35
Prepare Travis CI configuration for release [ci skip] 2019-03-06 22:20:36 +01:00
timvisee
9ba8c90816
Install specific github-release version instead 2019-03-06 22:15:15 +01:00
timvisee
be08ccfd13
Install curl through addons on Travis CI 2019-03-06 22:12:45 +01:00
timvisee
c00d7d16a6
Use proper snapcraft grade for publishing 2019-03-06 22:05:01 +01:00
timvisee
a8a6b234b9
Install curl again for Travis CI macOS builds [ci skip] 2019-03-06 22:03:45 +01:00
timvisee
afca101a75
Do not instal curl on Travis CI macOS builds 2019-03-06 21:45:35 +01:00
timvisee
23cfbc7a5f
Add Travis CI configuration for macOS builds 2019-03-06 21:25:34 +01:00
timvisee
c3c978c111
Bump version to 0.2.18 2019-03-06 20:59:23 +01:00
timvisee
db28443816
Fix snapcraft login on CI, cleanup GitLab CI configuration 2019-03-06 20:58:47 +01:00
timvisee
e75202eb1a
Bump version to 0.2.17 2019-03-06 18:43:35 +01:00
timvisee
67343bae00
Use proper file name for snapcraft configuration on CI 2019-03-06 18:43:02 +01:00
timvisee
30d91ba4b7
Bump version to 0.2.16 2019-03-06 16:50:01 +01:00
Tim Visée
68410e9614 Merge branch 'gitlab-ci-snap2' into 'master'
Enable and fix snap package release job, mention snap package in README

See merge request timvisee/ffsend!13
2019-03-06 15:48:49 +00:00
timvisee
13a2f0082d
Fix GitLab CI configuration error 2019-03-06 16:48:23 +01:00
timvisee
7e5cfe9e3b
Enable and fix snap package release job, mention snap package in README 2019-03-06 16:43:21 +01:00
timvisee
d0a301b300
Improve clipboard string building 2019-03-06 16:09:21 +01:00
timvisee
1bcfa47b9f
Bump version to 0.2.15 2019-03-06 15:45:56 +01:00
timvisee
b697bc3231
Add option to copy ffsend download command instead of share URL using -C 2019-03-06 15:45:01 +01:00
timvisee
e1d0ddee0d
Support optional passwords as well for info command 2019-03-06 15:19:52 +01:00
timvisee
ff93b13b22
Make owner token for info action optional 2019-03-06 14:54:53 +01:00
timvisee
e0a8147ba5
Minor README improvements, alias --non-interactive flag as well 2019-03-06 14:17:38 +01:00
timvisee
199001ebe9
Bump version to 0.2.14, update Cargo.lock 2019-03-05 10:00:36 +01:00
timvisee
1720bc5cb4
Fix release on AppVeyor, use Rust stable on AppVeyor 2019-03-05 09:59:36 +01:00
timvisee
61b0d77497
Use proper version tag in AppVeyor builds 2019-03-05 08:20:16 +01:00
timvisee
df2ed5f542
Bump version to 0.2.13 2019-03-05 08:00:42 +01:00
timvisee
f4702b7920
Revive AppVeyor release builds, do not build through trigger 2019-03-05 07:59:54 +01:00
timvisee
ae7e285879
Bump version to 0.2.12, update dependencies 2019-03-04 20:48:04 +01:00
timvisee
3138fe716b
Update README to reflect changes for Windows binaries 2019-03-04 20:47:30 +01:00
Tim Visée
a36325b84a Merge branch 'appveyor-ci' into 'master'
Release Windows binaries through AppVeyor CI

See merge request timvisee/ffsend!12
2019-03-04 19:41:57 +00:00
timvisee
0b1d67e686
Fix AppVeyor trigger in GitLab CI 2019-03-04 20:40:52 +01:00
timvisee
ac1e1f8081
Trigger AppVeyor release builds only on GitLab CI release using trigger 2019-03-04 20:38:27 +01:00
timvisee
2584a6a643
Add .exe extension to Windows binaries 2019-03-04 18:50:52 +01:00
timvisee
4937feb92c
Build both dynamic and static Windows binaries, upload as GitHub release 2019-03-04 18:49:38 +01:00
timvisee
dfe3b103b1
Explicitly statically link on AppVeyor CI 2019-03-04 18:18:38 +01:00
timvisee
505cbeed5e
Bump version to 0.2.11 2019-03-04 18:01:23 +01:00
timvisee
6e6e2aee3a
Fix cargo dependency version constraint for ffsend-git AUR package 2019-03-04 18:00:52 +01:00
timvisee
ae2e64a181
Test static Windows build on AppVeyor 2019-03-04 17:37:54 +01:00
timvisee
6a87a815d5
Do not install other targets, remove notifications from AppVeyor CI 2019-03-04 17:11:28 +01:00
timvisee
4d16c721fe
Explicitly install OpenSSL on AppVeyor CI 2019-03-04 17:07:40 +01:00
timvisee
e4a7a2e5e9
Add AppVeyor CI configuration 2019-03-04 16:53:35 +01:00
timvisee
4cb9e4813f
Mention new AUR package in README 2019-03-04 16:33:46 +01:00
timvisee
3b45feca28
Bump version to 0.2.10, update Cargo.lock 2019-03-04 16:28:56 +01:00
timvisee
8ec647eac1
Specify minimum version for cargo and rust dependencies in AUR package 2019-03-04 16:23:38 +01:00
timvisee
9bd72bf7f4
Fix invalid provides property for ffsend-git AUR package 2019-03-04 16:12:58 +01:00
timvisee
9ba6939f72
Bump version to 0.2.9 2019-03-04 14:33:51 +01:00
timvisee
f70734eeec
Attempt to fix Arch AUR packaging on CI, again 2019-03-04 14:33:18 +01:00
timvisee
1db0833a13
Bump version to 0.2.8 2019-03-04 13:11:56 +01:00
timvisee
1b6dc0d77d
Fix incorrect package name in Arch AUR package builds 2019-03-04 13:11:34 +01:00
timvisee
e41f18c55d
Bump version to 0.2.7 2019-03-04 00:56:43 +01:00
Tim Visée
f503c11ba3 Merge branch 'fix-github-release' into 'master'
Fix GitHub release on CI

See merge request timvisee/ffsend!11
2019-03-03 23:56:22 +00:00
timvisee
1b0d255ad1
Install netbase required by github-release 2019-03-04 00:53:03 +01:00
timvisee
26e39ac580
Re-enable temporarily disabled CI jobs for testing [ci skip] 2019-03-04 00:21:15 +01:00
timvisee
279ed08051
Fix GitHub releases on CI, explicitly create release first 2019-03-04 00:20:12 +01:00
timvisee
75ff0db32f
Use explicit relative file path for GitHub release uploads 2019-03-03 23:59:26 +01:00
timvisee
c5aca77062
Attempt to fix GitHub release job on CI 2019-03-03 23:32:18 +01:00
timvisee
a6104eef30
Use proper Rust target for test job on CI 2019-03-03 21:47:56 +01:00
timvisee
27e71e9dae
Bump version to 0.2.6 2019-03-03 21:46:44 +01:00
timvisee
996a12b0ec
Do not explicitly remove github-release archive on CI 2019-03-03 21:46:14 +01:00
timvisee
dffd71e10b
Bump version to 0.2.5 2019-03-03 20:33:14 +01:00
timvisee
cdd05c513d
Fix various GitLab CI configuration issues in release 2019-03-03 20:32:10 +01:00
timvisee
4e25472b32
Bump version to 0.2.4 2019-03-03 18:52:07 +01:00
Tim Visée
c244762f50 Merge branch 'pkg-aur-bin' into 'master'
Release both binary and git AUR packages

See merge request timvisee/ffsend!10
2019-03-03 17:51:26 +00:00
timvisee
095e7d60ef
Merge remote-tracking branch 'origin/master' into pkg-aur-bin 2019-03-03 18:51:16 +01:00
timvisee
a227f9fe22
Install dependencies for GitLab release job on CI 2019-03-03 18:49:37 +01:00
timvisee
eb47d6f4ab
Release both binary and git AUR packages 2019-03-03 18:37:53 +01:00
timvisee
a2e0a363de
Bump version to 0.2.3 2019-03-03 17:44:52 +01:00
Tim Visée
f4221e1737 Merge branch 'gitlab-ci' into 'master'
Improve CI configuration, strip binaries, publish binaries on GitHub on release

See merge request timvisee/ffsend!9
2019-03-03 16:44:17 +00:00
timvisee
e0a8af6c00
Merge remote-tracking branch 'origin/master' into gitlab-ci 2019-03-03 17:40:49 +01:00
timvisee
4b641cb28d
Use GitLab CI status badge for Cargo 2019-03-03 17:39:43 +01:00
timvisee
c47c36c692
Strip ffsend binaries on CI from debug symbols 2019-03-03 16:57:24 +01:00
timvisee
77baf54d84
Switch to Rust 1.32 as minimum version 2019-03-03 16:52:17 +01:00
timvisee
1148d8141a
Upload binaries as GitHub releases 2019-03-03 16:24:00 +01:00
timvisee
0d55bee532
Fix invalid old Rust version 2019-03-03 16:20:51 +01:00
timvisee
b302128499
Delete uploaded test files after download, to fix history glitch 2019-03-03 15:51:19 +01:00
timvisee
3cbbc56ce8
Configure and use Rust platform target on CI, better name static bin 2019-03-03 15:47:55 +01:00
timvisee
2e194086d6
Also build against oldest supported Rust compiler on CI 2019-03-03 15:24:07 +01:00
timvisee
680e41268f
Bump version to 0.2.2 2019-03-03 15:12:20 +01:00
timvisee
614659f74b
Update ffsend-api dependency to version 0.2.0 2019-03-03 15:11:02 +01:00
timvisee
661d1031ed
Remove Travis CI configuration 2019-03-01 16:24:55 +01:00
timvisee
a61501a029
Change CI badge to GitLab CI 2019-03-01 12:28:33 +01:00
timvisee
d9b02a24ab
Bump version to 0.2.1 2019-02-28 21:00:39 +01:00
timvisee
b8e3ad1300
Change to newer archlinux Docker image for AUR package build 2019-02-28 21:00:03 +01:00
timvisee
077a8a0a9d
Bump version to 0.2.0 2019-02-28 19:42:00 +01:00
Tim Visée
0a5b4dc7e8 Merge branch 'send3' into 'master'
Implement support for Firefox Send v3

Closes #53

See merge request timvisee/ffsend!8
2019-02-28 18:40:11 +00:00
timvisee
1138b5b25e
Disable snapcraft CI job 2019-02-28 19:38:18 +01:00
timvisee
3a1556c5c6
Upgrade ffsend-api dependency to 0.2.0 from crates.io 2019-02-28 12:26:43 +01:00
timvisee
6979a901f3
Allow public test to fail for now 2019-02-28 10:42:52 +01:00
timvisee
0f123e4866
Make test file on CI just 2MBs, also test Firefox Send v3 host 2019-02-28 09:56:50 +01:00
timvisee
b8116c69f9
Test other compiler feature configurations on CI 2019-02-28 00:56:08 +01:00
timvisee
5f7c6eb15c
Allow any ffsend-api dependency version from Git repository 2019-02-28 00:54:40 +01:00
timvisee
8a46cfd968
Report supported API versions in debug output 2019-02-28 00:12:28 +01:00
timvisee
00618fe9db
Fix compiler error when compiling with support for multiple Send versions 2019-02-28 00:08:39 +01:00
timvisee
2236801f64
Mark version specific API logic with compiler feature attributes 2019-02-28 00:07:43 +01:00
timvisee
4cfc9b16b6
Update README for recent changes 2019-02-28 00:04:55 +01:00
timvisee
ea41cbdbc6
Improve API argument parsing and help information 2019-02-27 23:30:00 +01:00
timvisee
af98e133dc
Report proper error for invalid API version argument 2019-02-27 23:23:02 +01:00
timvisee
3bb0dc28dc
Remove unused imports, add Send version features to debug output 2019-02-27 23:17:03 +01:00
timvisee
ca122cad66
Make API version selection possible, update for newer ffsend-api version 2019-02-27 19:42:14 +01:00
timvisee
f53f11a9f1
Add version subcommand to determine remote server API version 2019-02-26 15:28:13 +01:00
timvisee
7c1429e112
Mention Send v2 and Send v3 support in README features 2019-02-26 14:58:46 +01:00
timvisee
802c96df3f
Update Cargo.lock, apply changes for ffsend-api send2 branch 2019-02-18 14:06:53 +01:00
timvisee
dd8a7fe645
Replace deprecated function for Send v2 2019-01-20 18:41:08 +01:00
timvisee
48d0b4f48d
Merge remote-tracking branch 'origin/master' into gitlab-ci-snap2 2019-01-20 17:37:52 +01:00
timvisee
c1345706ef
Test the snapcraft edge image for building 2019-01-20 17:36:53 +01:00
timvisee
e16139e1e5
Update dependencies 2019-01-20 16:01:23 +01:00
timvisee
d7145c54ec
Installation and build requirements updates 2019-01-16 20:55:36 +01:00
timvisee
f18c7f34d1
Update build requirements to use Rust with support for 2018 edition 2019-01-12 23:39:31 +01:00
timvisee
8daff05841
Bump version to 0.1.2 2019-01-12 23:36:58 +01:00
timvisee
e40c334f38
Update dependencies 2019-01-12 23:35:53 +01:00
timvisee
3ceff500cf
Update to Rust edition 2018 2019-01-12 23:34:38 +01:00
Tim Visée
26bdcebe00 Merge branch 'pkg-aur' into 'master'
Build and release Arch AUR package

See merge request timvisee/ffsend!7
2019-01-12 22:19:11 +00:00
Tim Visée
ab4ba3e0c4 Build and release Arch AUR package 2019-01-12 22:19:11 +00:00
timvisee
7f0857a884
Attempt to use beta Docker image for snapcraft on GitLab CI 2019-01-11 18:23:40 +01:00
timvisee
74675e9218
Update dependencies 2019-01-11 18:15:27 +01:00
timvisee
151609983f
Build snapcraft package on GitLab CI 2019-01-03 14:08:50 +01:00
timvisee
573c4862ca
Merge remote-tracking branch 'origin/master' into gitlab-ci-snap2 2019-01-03 14:07:06 +01:00
timvisee
60742bbf4e
Fix webbrowser spelling 2019-01-03 13:16:24 +01:00
timvisee
0f7733f798
Add .gitignore for snapcraft files 2019-01-03 13:14:42 +01:00
timvisee
b4145db623
Add snapcraft build configuration 2019-01-03 13:14:34 +01:00
Tim Visée
3291b8dcad Merge branch 'gitlab-ci-static' into 'master'
Improve GitLab CI static build

See merge request timvisee/ffsend!6
2019-01-03 11:30:41 +00:00
Tim Visée
5a00ea4dcc Improve GitLab CI static build 2019-01-03 11:30:41 +00:00
timvisee
19322306f1
Update help section in README 2018-11-29 09:39:34 +01:00
timvisee
c4a90498ad
Bump version to 0.1.1 2018-11-21 22:54:38 +01:00
timvisee
44cf283f97
Reformat using rustfmt 2018-11-21 22:53:47 +01:00
timvisee
5b8120e968
Update Cargo.lock 2018-11-21 22:53:34 +01:00
timvisee
c72e106de8
Update ffsend-api dependency, hide progress bars in quiet mode 2018-11-21 22:53:12 +01:00
timvisee
059a0d85c5
Simplify GitLab CI configuration, attempt to fix upload/download test 2018-11-21 22:44:22 +01:00
timvisee
d32333616b
Attempt to fix GitLab configuration [2/2] 2018-11-21 22:13:15 +01:00
timvisee
8b31aef887
Attempt to fix GitLab CI configuration 2018-11-21 22:12:22 +01:00
timvisee
a91e82407e
Update CI configuration, test if upload/download produces same file 2018-11-21 22:10:52 +01:00
timvisee
5fbcb2cd02
Be properly quiet when calling binary with no arguments 2018-11-21 22:03:38 +01:00
timvisee
7bbd74002b
Mention quiet mode in debug output 2018-11-21 21:57:12 +01:00
timvisee
5ac3cd4b15
Implement quiet CLI flag, only report URLs for history with quiet 2018-11-21 21:55:19 +01:00
timvisee
3ea774e227
Only show owner token in history view in verbose mode 2018-11-21 21:43:26 +01:00
timvisee
1e1d8bb697
Do not cache untracked files 2018-11-20 16:34:24 +01:00
timvisee
30f0a67cda
Re-enable caching on CI, attempt to improve hits and performance 2018-11-20 16:23:32 +01:00
timvisee
a7898d8e76
Disable caching on CI 2018-11-20 16:06:14 +01:00
timvisee
925bb0bd6b
Update ffsend-api dependency to fix download error 2018-11-20 15:03:25 +01:00
timvisee
a58b15af7b
Fix build errors in some feature configurations 2018-11-20 13:27:05 +01:00
timvisee
7ee2391cbc
Bump version to 0.1.0 2018-11-20 13:06:55 +01:00
timvisee
0768028089
Show timeout values in debug output 2018-11-20 13:05:59 +01:00
timvisee
f693ff42d3
Make timeout configurable 2018-11-20 12:42:27 +01:00
timvisee
0054daadff
Update ffsend-api dependency 2018-11-20 12:02:43 +01:00
timvisee
a6fdde6d0f
Set explicit client timeouts, fix timeout for big transfers 2018-11-20 12:01:09 +01:00
timvisee
1ec5b98961
Update dependencies 2018-11-20 11:04:27 +01:00
timvisee
e53c5bab67
Update Cargo.lock 2018-11-20 11:03:08 +01:00
timvisee
6c35585980
Update prettytable dependency 2018-10-28 18:21:53 +01:00
timvisee
44b22720c4
Update dependencies 2018-10-28 18:17:29 +01:00
timvisee
7a5ca17522
Update Cargo.lock 2018-10-17 23:30:00 +02:00
timvisee
269b5dcc14
Allow dirty crate publishes 2018-10-17 20:26:25 +02:00
timvisee
f4441367ec
Bump version to 0.0.9 2018-10-17 17:52:31 +02:00
Tim Visée
a7f7972c27 Merge branch 'openssl-1.0' into 'master'
Support OpenSSL 1.0

See merge request timvisee/ffsend!5
2018-10-17 15:50:44 +00:00
timvisee
3e84385d71
Upgrade to ffsend-api 0.0.4, drop custom crate patches 2018-10-17 17:49:48 +02:00
timvisee
aa7a6ae596
Merge remote-tracking branch 'origin/master' into openssl-1.0 2018-10-17 17:49:00 +02:00
timvisee
3f1f5b3e16
Disable crate release job on Travis, add it to GitLab CI configuration 2018-10-17 17:48:37 +02:00
timvisee
da605ac669
Merge remote-tracking branch 'origin/master' into openssl-1.0 2018-10-15 23:15:03 +02:00
timvisee
b3a4b95855
Merge remote-tracking branch 'origin-old/master' 2018-10-15 23:13:08 +02:00
timvisee
0413eb6fdf
Merge remote-tracking branch 'origin/master' into openssl-1.0 2018-10-15 23:07:36 +02:00
timvisee
560b462f21
Reformat using rustfmt 2018-10-15 23:07:22 +02:00
timvisee
3a8535e408
Switch to temporary ffsend-api/hkdf for OpenSSL 1.0 support 2018-10-15 23:02:56 +02:00
timvisee
4d5c0391cb
Minor README requirements improvement 2018-10-15 22:57:09 +02:00
timvisee
fa234d7410
Update Cargo.lock 2018-09-19 17:49:47 +02:00
timvisee
16d6a6a4ae
Reformat using rustfmt 2018-09-18 22:01:22 +02:00
timvisee
855da2c53b
Update Cargo.lock 2018-09-18 21:52:53 +02:00
timvisee
a50e4e2300
Update chbs dependency to v0.0.8 2018-09-04 23:50:38 +02:00
timvisee
0a6696d912
Merge branch 'master' of gitlab.com:timvisee/ffsend 2018-09-04 19:47:57 +02:00
timvisee
d388a48e44
Update derive_builder dependency to v0.6 2018-09-04 19:47:40 +02:00
Tim Visée
29849b7ff9
Merge pull request #7 from rrooij/fix_typo
README: fix typo in URL
2018-08-31 10:54:48 +02:00
rdrooij
a25b1e568a README: fix typo in URL
Mozzilla is changed to Mozilla.
2018-08-31 10:48:24 +02:00
timvisee
49fdf54f49
Bump version to 0.0.8 2018-08-02 02:57:43 +02:00
Tim Visée
743b84af67 Merge branch 'password-diceware' into 'master'
Add feature to generate easily rememberable passphrase

See merge request timvisee/ffsend!4
2018-08-02 00:43:36 +00:00
timvisee
cda587455b
Add feature to generate easily rememberable passphrase 2018-08-02 02:39:20 +02:00
Tim Visée
cd5588e9b6 Merge branch 'gitlab-ci-artifacts' into 'master'
Static compilation in GitLab CI

See merge request timvisee/ffsend!3
2018-06-23 15:02:02 +00:00
Tim Visée
ca3e704d4e Static compilation in GitLab CI 2018-06-23 15:02:02 +00:00
timvisee
c3edcfe704
Update Cargo.lock 2018-06-19 17:10:22 +02:00
timvisee
249b8bf6c9
Disable broken release builds until the work in the gitlab-ci branch 2018-06-18 21:36:27 +02:00
timvisee
c7e3739193
Update the APK package index first 2018-06-18 20:34:57 +02:00
timvisee
f4144d085b
Use apk on Alpine images 2018-06-18 20:32:58 +02:00
timvisee
0e8f643210
Minor rustup argument invocation fix 2018-06-18 19:59:02 +02:00
timvisee
0b58682b1b
Test release builds with a DinD capable image we install Rust in on CI 2018-06-18 19:53:45 +02:00
timvisee
e0f293a19f
Experiment with release jobs on GitLab CI (WIP) 2018-06-18 19:20:55 +02:00
timvisee
7f67eb766e
Revert cargo debugging in CI 2018-06-18 19:00:45 +02:00
timvisee
dbc325cc4e
Minor GitLab CI stage renames, cargo environment debugging (test) 2018-06-18 18:54:14 +02:00
timvisee
f9c0144af4
Remove obsolete ROADMAP file, using GitLab issues/milestones instead 2018-06-05 17:24:47 +02:00
timvisee
e0326b1716
Lowercase all errors properly 2018-06-05 16:15:02 +02:00
timvisee
b98c2b3f28
Update Cargo.lock 2018-06-05 01:14:28 +02:00
104 changed files with 13666 additions and 2436 deletions

3
.gitattributes vendored Normal file
View file

@ -0,0 +1,3 @@
# Mark generated completion files as vendored
contrib/completions/* linguist-vendored
contrib/completions/gen_completions linguist-vendored=false

7
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,7 @@
# Funding links
github:
- timvisee
custom:
- "https://timvisee.com/donate"
patreon: timvisee
ko_fi: timvisee

View file

@ -1,40 +1,61 @@
# GitLab CI configuration for ffsend builds, tests and releases
#
# To add a new release:
# - configure a new 'build-*' job with the proper target
# - export a build artifact from the new job
# - manually upload artifact to GitHub in the 'github-release' job
image: "rust:slim"
stages:
- check
- build
- test
- integration
- release
- package
# Variable defaults
variables:
RUST_VERSION: stable
RUST_TARGET: x86_64-unknown-linux-gnu
# Cargo artifacts caching per Rust version
# Cache rust/cargo/build artifacts
cache:
key: "$RUST_VERSION"
key: "$CI_PIPELINE_ID-$RUST_VERSION"
paths:
- /usr/local/rustup/
- /usr/local/cargo/
- /usr/local/cargo/registry/
- /usr/local/rustup/toolchains/
- /usr/local/rustup/update-hashes/
- target/
# Install compiler and OpenSSL dependencies
before_script:
- apt-get update -y
- apt-get update
- apt-get install -y --no-install-recommends build-essential pkg-config libssl-dev
- rustup install $RUST_VERSION && rustup default $RUST_VERSION
- rustc --version && cargo --version
- |
rustup install $RUST_VERSION
rustup default $RUST_VERSION
- |
rustc --version
cargo --version
# Check on stable, beta and nightly
# Check on stable, beta and nightly
.check-base: &check-base
stage: check
script:
- cargo check --all --verbose
- cargo check --no-default-features --all --verbose
- cargo check --features no-color --all --verbose
- cargo check --verbose
- cargo check --no-default-features --features send3,crypto-ring --verbose
- cargo check --no-default-features --features send2,crypto-openssl --verbose
- cargo check --no-default-features --features send3,crypto-openssl --verbose
- cargo check --no-default-features --features send2,send3,crypto-openssl --verbose
- cargo check --no-default-features --features send3,crypto-ring,archive --verbose
- cargo check --no-default-features --features send3,crypto-ring,history --verbose
- cargo check --no-default-features --features send3,crypto-ring,qrcode --verbose
- cargo check --no-default-features --features send3,crypto-ring,urlshorten --verbose
- cargo check --no-default-features --features send3,crypto-ring,infer-command --verbose
- cargo check --features no-color --verbose
check-stable:
<<: *check-base
variables:
RUST_VERSION: stable
check-beta:
<<: *check-base
variables:
@ -43,18 +64,328 @@ check-nightly:
<<: *check-base
variables:
RUST_VERSION: nightly
check-msrv:
<<: *check-base
variables:
RUST_VERSION: "1.63.0"
# Run unit tests
test-unit:
stage: test
# Build using Rust stable
build-x86_64-linux-gnu:
stage: build
needs: []
script:
- cargo test --all --verbose
- cargo build --target=$RUST_TARGET --release --verbose
- mv target/$RUST_TARGET/release/ffsend ./ffsend-$RUST_TARGET
- strip -g ./ffsend-$RUST_TARGET
artifacts:
name: ffsend-x86_64-linux-gnu
paths:
- ffsend-$RUST_TARGET
expire_in: 1 month
# Build a static version
build-x86_64-linux-musl:
stage: build
needs: []
variables:
RUST_TARGET: x86_64-unknown-linux-musl
script:
# Install the static target
- rustup target add $RUST_TARGET
# Build OpenSSL statically
- apt-get install -y build-essential wget musl-tools
- wget https://www.openssl.org/source/old/1.1.1/openssl-1.1.1k.tar.gz
- tar xzvf openssl-1.1.1k.tar.gz
- cd openssl-1.1.1k
- ./config no-async -fPIC --openssldir=/usr/local/ssl --prefix=/usr/local
- make
- make install
- cd ..
# Statically build ffsend
- export OPENSSL_STATIC=1
- export OPENSSL_LIB_DIR=/usr/local/lib
- export OPENSSL_INCLUDE_DIR=/usr/local/include
- cargo build --target=$RUST_TARGET --release --verbose
# Prepare the release artifact, strip it
- find . -name ffsend -exec ls -lah {} \;
- mv target/$RUST_TARGET/release/ffsend ./ffsend-$RUST_TARGET
- strip -g ./ffsend-$RUST_TARGET
artifacts:
name: ffsend-x86_64-linux-musl
paths:
- ffsend-$RUST_TARGET
expire_in: 1 month
# Run the unit tests through Cargo
test-cargo:
stage: test
needs: []
dependencies: []
script:
- cargo test --verbose
# Run integration test with the public Send service
integration-send:
stage: integration
test-public:
image: alpine:latest
stage: test
dependencies:
- build-x86_64-linux-musl
variables:
GIT_STRATEGY: none
RUST_TARGET: x86_64-unknown-linux-musl
before_script: []
script:
- cargo build
- head -c 1M </dev/urandom >testfile
- cargo run -- upload testfile -n testfile.bin -a -d 10 -p secret -I
# TODO: download this file, compare checksums
# Prepare ffsend binary, create random file
- mv ./ffsend-$RUST_TARGET ./ffsend
- chmod a+x ./ffsend
- head -c1m </dev/urandom >test.txt
# Generate random file, upload/download and assert equality
- ./ffsend upload test.txt -I
- ./ffsend download $(./ffsend history -q) -I -o=download.txt
- "cmp -s ./test.txt ./download.txt || (echo ERROR: Downloaded file is different than original; exit 1)"
- rm ./download.txt
# Cargo crate release
release-crate:
stage: release
dependencies: []
only:
- /^v(\d+\.)*\d+$/
script:
- echo "Creating release crate to publish on crates.io..."
- echo $CARGO_TOKEN | cargo login
- echo "Publishing crate to crates.io..."
- cargo publish --verbose --allow-dirty
# Snap release
release-snap:
image: snapcore/snapcraft:stable
stage: release
dependencies: []
only:
- /^v(\d+\.)*\d+$/
before_script: []
script:
# Prepare the environment
- apt-get update -y
- apt-get install python3 -yqq
- cd pkg/snap
# Update version number in snapcraft.yaml
- VERSION=$(echo $CI_COMMIT_REF_NAME | cut -c 2-)
- echo "Determined binary version number 'v$VERSION', updating snapcraft.yaml..."
- 'sed "s/^version:.*\$/version: $VERSION/" -i snapcraft.yaml'
- 'sed "s/^pkgver=.*\$/pkgver=$VERSION/" -i snapcraft.yaml'
# Build the package
- echo "Building snap package..."
- snapcraft
# Publish snap package
- echo "Publishing snap package..."
- echo "$SNAPCRAFT_LOGIN" | base64 -d > snapcraft.login
- snapcraft whoami
- snapcraft push --release=stable ffsend_*_amd64.snap
artifacts:
name: ffsend-snap-x86_64
paths:
- pkg/snap/ffsend_*_amd64.snap
expire_in: 1 month
# Publish release binaries to as GitHub release
release-github:
stage: release
only:
- /^v(\d+\.)*\d+$/
dependencies:
- build-x86_64-linux-gnu
- build-x86_64-linux-musl
before_script: []
script:
# Install dependencies
- apt-get update
- apt-get install -y curl wget gzip netbase
# Download github-release binary
- wget https://github.com/tfausak/github-release/releases/download/1.2.5/github-release-linux.gz -O github-release.gz
- gunzip github-release.gz
- chmod a+x ./github-release
# Create the release, upload binaries
- ./github-release release --token "$GITHUB_TOKEN" --owner timvisee --repo ffsend --tag "$CI_COMMIT_REF_NAME" --title "ffsend $CI_COMMIT_REF_NAME"
- ./github-release upload --token "$GITHUB_TOKEN" --owner timvisee --repo ffsend --tag "$CI_COMMIT_REF_NAME" --file ./ffsend-x86_64-unknown-linux-gnu --name ffsend-$CI_COMMIT_REF_NAME-linux-x64
- ./github-release upload --token "$GITHUB_TOKEN" --owner timvisee --repo ffsend --tag "$CI_COMMIT_REF_NAME" --file ./ffsend-x86_64-unknown-linux-musl --name ffsend-$CI_COMMIT_REF_NAME-linux-x64-static
# Publish a Docker image
release-docker:
image: docker:git
stage: release
only:
- /^v(\d+\.)*\d+$/
dependencies:
- build-x86_64-linux-musl
services:
- docker:dind
variables:
RUST_TARGET: x86_64-unknown-linux-musl
DOCKER_HOST: tcp://docker:2375
# DOCKER_DRIVER: overlay2
before_script: []
script:
# Place binary in Docker directory, change to it
- mv ./ffsend-$RUST_TARGET ./pkg/docker/ffsend
- cd ./pkg/docker
# Build the Docker image, run it once to test
- docker build -t timvisee/ffsend:latest ./
- docker run --rm timvisee/ffsend:latest -V
# Retag version
- VERSION=$(echo $CI_COMMIT_REF_NAME | cut -c 2-)
- echo "Determined Docker image version number 'v$VERSION', retagging image..."
- docker tag timvisee/ffsend:latest timvisee/ffsend:$VERSION
# Authenticate and push the Docker images
- echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USER" --password-stdin
- docker push timvisee/ffsend:$VERSION
- docker push timvisee/ffsend:latest
# AUR packages release
package-aur:
image: archlinux
stage: package
needs:
- release-github
dependencies: []
only:
- /^v(\d+\.)*\d+$/
before_script: []
script:
- cd ./pkg/aur
# Determine the version number we're releasing for
- VERSION=$(echo $CI_COMMIT_REF_NAME | cut -c 2-)
- echo "Determined binary version number 'v$VERSION'"
# Determine remote URLs and SHA checksums
- echo "Determining SHA checksums for remote files..."
- URL_BIN=https://github.com/timvisee/ffsend/releases/download/v$VERSION/ffsend-v$VERSION-linux-x64-static
- URL_SOURCE=https://gitlab.com/timvisee/ffsend/-/archive/v$VERSION/ffsend-v$VERSION.tar.gz
- URL_BASH_COMPLETION=https://gitlab.com/timvisee/ffsend/raw/v$VERSION/contrib/completions/ffsend.bash
- URL_ZSH_COMPLETION=https://gitlab.com/timvisee/ffsend/raw/v$VERSION/contrib/completions/_ffsend
- URL_FISH_COMPLETION=https://gitlab.com/timvisee/ffsend/raw/v$VERSION/contrib/completions/ffsend.fish
- URL_LICENSE=https://gitlab.com/timvisee/ffsend/raw/v$VERSION/LICENSE
- 'echo "Selected binary URL: $URL_BIN"'
- 'echo "Selected source URL: $URL_SOURCE"'
- echo "Determining sha256sum for remote binary..."
- 'SHA_BIN=$(curl -sSL "$URL_BIN" | sha256sum | cut -d" " -f1)'
- 'echo "Got sha256sum: $SHA_BIN"'
- 'SHA_BASH_COMPLETION=$(curl -sSL "$URL_BASH_COMPLETION" | sha256sum | cut -d" " -f1)'
- 'echo "Got sha256sums of bash completion: $SHA_BASH_COMPLETION"'
- 'SHA_ZSH_COMPLETION=$(curl -sSL "$URL_ZSH_COMPLETION" | sha256sum | cut -d" " -f1)'
- 'echo "Got sha256sums of ZSH completion: $SHA_ZSH_COMPLETION"'
- 'SHA_FISH_COMPLETION=$(curl -sSL "$URL_FISH_COMPLETION" | sha256sum | cut -d" " -f1)'
- 'echo "Got sha256sums of fish completion: $SHA_FISH_COMPLETION"'
- 'SHA_LICENSE=$(curl -sSL "$URL_LICENSE" | sha256sum | cut -d" " -f1)'
- 'echo "Got sha256sums of LICENSE: $SHA_LICENSE"'
- echo "Determining sha256sum for remote source..."
- 'SHA_SOURCE=$(curl -sSL "$URL_SOURCE" | sha256sum | cut -d" " -f1)'
- 'echo "Got sha256sum: $SHA_SOURCE"'
# Update PKGBUILD parameters: version, source URL and SHA sum
- echo "Updating PKGBUILDS with release information..."
- sed "s/^pkgver=.*\$/pkgver=$VERSION/" -i ffsend/PKGBUILD
- sed "s/^pkgver=.*\$/pkgver=$VERSION/" -i ffsend-bin/PKGBUILD
- sed "s/^pkgver=.*\$/pkgver=$VERSION.$CI_COMMIT_SHORT_SHA/" -i ffsend-git/PKGBUILD
- sed "s/^source=(\".*\").*\$/source=(\"$(echo $URL_SOURCE | sed 's/\//\\\//g')\")/" -i ffsend/PKGBUILD
- sed "s/\(\"ffsend-v\$pkgver::\).*\"/\1$(echo $URL_BIN | sed 's/\//\\\//g')\"/" -i ffsend-bin/PKGBUILD
- sed "s/\(\"ffsend-v\$pkgver.bash::\).*\"/\1$(echo $URL_BASH_COMPLETION | sed 's/\//\\\//g')\"/" -i ffsend-bin/PKGBUILD
- sed "s/\(\"ffsend-v\$pkgver.zsh::\).*\"/\1$(echo $URL_ZSH_COMPLETION | sed 's/\//\\\//g')\"/" -i ffsend-bin/PKGBUILD
- sed "s/\(\"ffsend-v\$pkgver.fish::\).*\"/\1$(echo $URL_FISH_COMPLETION | sed 's/\//\\\//g')\"/" -i ffsend-bin/PKGBUILD
- sed "s/\(\"LICENSE-v\$pkgver::\).*\"/\1$(echo $URL_LICENSE | sed 's/\//\\\//g')\"/" -i ffsend-bin/PKGBUILD
- sed "s/^sha256sums=.*\$/sha256sums=('$SHA_SOURCE')/" -i ffsend/PKGBUILD
- sed "s/^sha256sums=.*\$/sha256sums=('$SHA_BIN' '$SHA_BASH_COMPLETION' '$SHA_ZSH_COMPLETION' '$SHA_FISH_COMPLETION' '$SHA_LICENSE')/" -i ffsend-bin/PKGBUILD
# Get SHA hash for local and remote file w/o version, update if it has changed
- 'SHA_STRIP_LOCAL=$(cat ffsend-git/PKGBUILD | sed /^pkgver=.\*/d | sha256sum | cut -d" " -f1)'
- 'SHA_STRIP_REMOTE=$(curl -sSL "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=ffsend-git" | sed /^pkgver=.\*/d | sha256sum | cut -d" " -f1)'
# Install dependencies
- echo "Installing required build packages..."
- pacman -Syu --noconfirm sudo base-devel binutils openssh rust cargo cmake git openssl
# Make AUR package
- mkdir -p /.cargo
- chmod -R 777 /.cargo
- cd ffsend-bin/
- echo "Making binary package..."
- sudo -u nobody makepkg -c
- sudo -u nobody makepkg --printsrcinfo > .SRCINFO
- cd ../ffsend
- echo "Making main source package..."
- sudo -u nobody makepkg -c
- sudo -u nobody makepkg --printsrcinfo > .SRCINFO
# Make git package if different than the remote
- |
if [ ! "$SHA_STRIP_LOCAL" == "$SHA_STRIP_REMOTE" ]; then
cd ../ffsend-git
echo "Making git source package..."
sudo -u nobody makepkg -c
sudo -u nobody makepkg --printsrcinfo > .SRCINFO
else
echo "Not making git source package, it has not changed"
fi
- cd ..
# Set up SSH for publishing
- mkdir -p /root/.ssh
- cp ./aur.pub /root/.ssh/id_rsa.pub
- echo "$AUR_SSH_PRIVATE" > /root/.ssh/id_rsa
- echo "Host aur.archlinux.org" >> /root/.ssh/config
- echo " IdentityFile /root/.ssh/aur" >> /root/.ssh/config
- echo " User aur" >> /root/.ssh/config
- chmod 600 /root/.ssh/{id_rsa*,config}
- eval `ssh-agent -s`
- ssh-add /root/.ssh/id_rsa
- ssh-keyscan -H aur.archlinux.org >> /root/.ssh/known_hosts
- git config --global user.name "timvisee"
- git config --global user.email "tim@visee.me"
# Publish main package: clone AUR repo, commit update and push
- git clone ssh://aur@aur.archlinux.org/ffsend.git aur-ffsend
- cd aur-ffsend
- cp ../ffsend/{PKGBUILD,.SRCINFO} ./
- git add PKGBUILD .SRCINFO
- git commit -m "Release v$VERSION"
- git push
- cd ..
# Publish binary package: clone AUR repo, commit update and push
- git clone ssh://aur@aur.archlinux.org/ffsend-bin.git aur-ffsend-bin
- cd aur-ffsend-bin
- cp ../ffsend-bin/{PKGBUILD,.SRCINFO} ./
- git add PKGBUILD .SRCINFO
- git commit -m "Release v$VERSION"
- git push
- cd ..
# Publish git package: clone AUR repo, commit update and push
# Only publish it if it is different than the remote
- |
if [ ! "$SHA_STRIP_LOCAL" == "$SHA_STRIP_REMOTE" ]; then
git clone ssh://aur@aur.archlinux.org/ffsend-git.git aur-ffsend-git
cd aur-ffsend-git
cp ../ffsend-git/{PKGBUILD,.SRCINFO} ./
git add PKGBUILD .SRCINFO
git commit -m "Update PKGBUILD for release v$VERSION"
git push
cd ..
else
echo "Not pushing git package, it has not changed"
fi
# TODO: add job to test ffsend{-git} AUR packages

View file

@ -1,157 +1,41 @@
# Configuration for Travis CI
language: rust
sudo: required
services:
- docker
# Travis CI configuration for building macOS binaries for ffsend.
# These macOS binaries are published on GitHub as release files.
#
# The main CI runs on GitLab CI at: https://gitlab.com/timvisee/ffsend/pipelines
language: rust
# Only build release jobs
stages:
- build
- test
- name: release
if: tag =~ ^v(\d+\.)*\d+$
jobs:
include:
################################
# Build stage #
################################
# Linux with Rust stable/beta/nightly
- stage: build
rust: stable
env: TARGET=x86_64-unknown-linux-gnu
cache: cargo
script: &build-script
- cargo build --verbose --all
- cargo build --no-default-features --verbose --all
- cargo build --features no-color --verbose --all
- stage: build
rust: beta
env: TARGET=x86_64-unknown-linux-gnu
cache: cargo
script: *build-script
- stage: build
rust: nightly
env: TARGET=x86_64-unknown-linux-gnu
cache: cargo
script: *build-script
# macOS with Rust stable
- stage: build
- stage: release
rust: stable
os: osx
env: TARGET=x86_64-apple-darwin
cache: cargo
script: *build-script
################################
# Test stage #
################################
- stage: test
env: TARGET=x86_64-unknown-linux-gnu
cache: cargo
script: cargo test --verbose --all
################################
# Release stage #
################################
# Cargo crate release
- stage: release
env: TARGET=x86_64-unknown-linux-gnu
env: RUST_TARGET=x86_64-apple-darwin
cache: cargo
script:
- echo "Creating release crate on crates.io..."
- echo $CARGO_TOKEN | cargo login
- cargo publish --verbose
# Create release binary for macOS
- echo "Creating release binary for $RUST_TARGET..."
- cargo build --target=$RUST_TARGET --release --verbose --all
- cp target/$RUST_TARGET/release/ffsend ./ffsend
# GitHub binary release for Linux on x86/x86_64
- stage: release
rust: stable
env: TARGET=x86_64-unknown-linux-gnu TARGET_SIMPLE=linux-x64 DEB=y
cache: cargo
install: &install-github-release
- |
if [ ! $TARGET == "x86_64-unknown-linux-gnu" ] && [ ! $TARGET == "x86_64-apple-darwin" ]; then
cargo install cross
fi
- |
if [ -n "$DEB" ]; then
cargo install cargo-deb
fi
script: &script-github-release
- |
if [ $TARGET == "x86_64-unknown-linux-gnu" ] || [ $TARGET == "x86_64-apple-darwin" ]; then
echo "Creating release binary on GitHub for $TARGET..."
cargo build --release --verbose --all
cp target/release/ffsend ./ffsend
else
echo "Creating release binary on GitHub for $TARGET (cross compiled)..."
cross build --target $TARGET --release --verbose --all
cp target/$TARGET/release/ffsend ./ffsend
fi
- tar -czvf ./ffsend-$TRAVIS_TAG-$TARGET_SIMPLE.tar.gz ffsend
- |
if [ -n "$DEB" ]; then
cargo deb --verbose
fi
- mv ./ffsend ./ffsend-$TRAVIS_TAG-$TARGET_SIMPLE
deploy: &deploy-github-release
provider: releases
api_key: $GITHUB_OAUTH_TOKEN
skip_cleanup: true
overwrite: true
file_glob: true
file:
- target/debian/ffsend_*.deb
- ffsend-$TRAVIS_TAG-$TARGET_SIMPLE.tar.gz
- ffsend-$TRAVIS_TAG-$TARGET_SIMPLE
on:
tags: true
branch: master
- stage: release
rust: stable
env: TARGET=i686-unknown-linux-gnu TARGET_SIMPLE=linux-i386 DEB=y
cache: cargo
install: *install-github-release
script: *script-github-release
deploy: *deploy-github-release
# Download github-release binary
- wget https://github.com/tfausak/github-release/releases/download/1.2.4/github-release-osx.gz -O github-release.gz
- gunzip github-release.gz
- chmod a+x ./github-release
# GitHub binary release for Linux on arch
- stage: release
rust: stable
env: TARGET=aarch64-unknown-linux-gnu TARGET_SIMPLE=linux-aarch64
cache: cargo
install: *install-github-release
script: *script-github-release
deploy: *deploy-github-release
- stage: release
rust: stable
env: TARGET=arm-unknown-linux-gnueabi TARGET_SIMPLE=linux-arm
cache: cargo
install: *install-github-release
script: *script-github-release
deploy: *deploy-github-release
- stage: release
rust: stable
env: TARGET=armv7-unknown-linux-gnueabihf TARGET_SIMPLE=linux-armv7
cache: cargo
install: *install-github-release
script: *script-github-release
deploy: *deploy-github-release
# Create the release, upload binary
- ./github-release release --token "$GITHUB_TOKEN" --owner timvisee --repo ffsend --tag "$TRAVIS_TAG" --title "ffsend $TRAVIS_TAG"
- ./github-release upload --token "$GITHUB_TOKEN" --owner timvisee --repo ffsend --tag "$TRAVIS_TAG" --file ./ffsend --name ffsend-$TRAVIS_TAG-macos
# GitHub binary release for macOX
- stage: release
rust: stable
os: osx
env: TARGET=x86_64-apple-darwin TARGET_SIMPLE=osx-x64
cache: cargo
install: *install-github-release
script: *script-github-release
deploy: *deploy-github-release
# TODO: add Windows architecture (using AppVeyor)
# TODO: add (Free)BSD architecture
# TODO: enfore the git tag/crate version equality for releases
# TODO: disable addons/rust installation for GitHub release job
# TODO: disabled for now to throttle updates, manually enable on major release
# # Update homebrew package
# - git config --global user.name "timvisee"
# - git config --global user.email "$GIT_EMAIL"
# - git config --global credential.helper store
# - echo "https://timvisee:$HOMEBREW_GITHUB_API_TOKEN@github.com" >> ~/.git-credentials
# - brew bump-formula-pr --url="https://github.com/timvisee/ffsend/archive/$TRAVIS_TAG.tar.gz" --message="Automated release pull request using continuous integration." --no-browse -f -v ffsend

132
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,132 @@
# Contributing
**Repository:**
- [GitLab repository][gitlab]
- _Mirror: [GitHub repository][github]_
**Issues:** (bug reporting, feature requests, enhancements, etc.)
- [GitLab issue board][gitlab-issues]
- _Alternatively: [GitHub issue board][github-issues]_
**Pull/merge requests:** (fixes, implemented features, etc.)
- [GitLab merge requests][gitlab-mr]
- _Alternatively: [GitHub pull requests][github-pr]_
Contributions to the `ffsend` project are welcome!
When contributing new features, alternative implementations or bigger
improvements, please first discuss the change you wish to make via an issue
or email.
Small changes such as fixed commands, fixed spelling or dependency updates
are always welcome without discussion.
The `ffsend` repository is primarily hosted on [GitLab][gitlab].
[GitHub][github] hosts a mirror, for publicity and findability.
Please open any issues or pull requests on the [GitLab][gitlab] pages if possible.
Otherwise opening these on [GitHub][github] is fine, though they might be
manually moved over to [GitLab][gitlab].
Please note we have a code of conduct, please follow it in all your interactions
with the project.
## Pull Request Process
1. Ensure you've discussed your improvements in an issue when working on a
bigger change
2. Ensure your branch is up-to-date with the latest [`master`][branch-master]
3. Ensure the project builds with your changes: `cargo build`
4. Ensure the project tests succeed with your changes: `cargo test`
5. Update the `README.md` with details of significant changes, this includes new
compiler features, command-line arguments, environment variables or new
package installation instructions.
6. Submit your pull request.
7. Fix any issues continuous integration might report.
Additional notes:
- Do not change version numbers, this is done by @timvisee
## Code of Conduct
### Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
### Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
### Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
### Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
### Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project owner at 3a4fb3964f@sinenomine.email. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
### Attribution
This Code of Conduct is adapted from the [Contributor Covenant][coc-homepage], version 1.4,
available at [https://contributor-covenant.org/version/1/4][coc-version]
## License
This project is released under the GNU GPL-3.0 license.
Check out the [LICENSE](LICENSE) file for more information.
[branch-master]: https://gitlab.com/timvisee/ffsend/tree/master
[gitlab]: https://gitlab.com/timvisee/ffsend
[gitlab-issues]: https://gitlab.com/timvisee/ffsend/issues
[gitlab-mr]: https://gitlab.com/timvisee/ffsend/merge_requests
[github]: https://github.com/timvisee/ffsend
[github-issues]: https://github.com/timvisee/ffsend/issues
[github-pr]: https://github.com/timvisee/ffsend/pulls
[coc-homepage]: https://contributor-covenant.org
[coc-version]: https://contributor-covenant.org/version/1/4/

3316
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,15 @@
[package]
name = "ffsend"
version = "0.0.7"
authors = ["Tim Visee <timvisee@gmail.com>"]
version = "0.2.76"
rust-version = "1.63.0"
authors = ["Tim Visee <3a4fb3964f@sinenomine.email>"]
license = "GPL-3.0"
readme = "README.md"
homepage = "https://gitlab.com/timvisee/ffsend"
homepage = "https://timvisee.com/projects/ffsend"
repository = "https://gitlab.com/timvisee/ffsend"
description = """\
Easily and securely share files from the command line.\n\
A fully featured Firefox Send client.\
A fully featured Send client.\
"""
keywords = ["send", "firefox", "cli"]
categories = [
@ -19,8 +20,16 @@ categories = [
"network-programming",
]
exclude = [
"res/*",
"/.github",
"/contrib",
"/pkg",
"/res",
"/*.yml",
"/CONTRIBUTING.md",
"/SECURITY.md",
]
edition = "2018"
build = "build.rs"
[package.metadata.deb]
section = "utility"
@ -29,7 +38,7 @@ Easily and securely share files and directories from the command line through
a safe, private and encrypted link using a single simple command. \
Files are shared using the Send service and may be up to 2GB. \
Others are able to download these files with this tool, \
or through their webbrowser.\n\
or through their web browser.\n\
\n\
All files are always encrypted on the client, \
and secrets are never shared with the remote host. \
@ -38,48 +47,89 @@ An optional password may be specified, and a default file lifetime of 1 \
remain online forever. This provides a secure platform to share your files."""
priority = "standard"
license-file = ["LICENSE", "3"]
depends = "$auto, xclip"
depends = "$auto, libssl1.1, ca-certificates, xclip"
maintainer-scripts = "pkg/deb"
[badges]
travis-ci = { repository = "timvisee/ffsend", branch = "master" }
[[bin]]
name = "ffsend"
path = "src/main.rs"
[features]
default = ["archive", "clipboard", "history"]
default = ["archive", "clipboard", "crypto-ring", "history", "infer-command", "qrcode", "send3", "urlshorten"]
# Compile with file archiving support
archive = ["tar"]
# Support for putting share URLs in clipboard
clipboard = ["clip", "which"]
# Compile with file history support
history = []
# Support for Send v2
send2 = ["ffsend-api/send2"]
# Support for Send v3
send3 = ["ffsend-api/send3"]
# Use OpenSSL as cryptography backend
crypto-openssl = ["ffsend-api/crypto-openssl"]
# Use ring as cryptography backend
crypto-ring = ["ffsend-api/crypto-ring"]
# Support for generating QR codes for share URLs
qrcode = ["qr2term"]
# Support for shortening share URLs
urlshorten = ["urlshortener"]
# Support for inferring subcommand when linking binary
infer-command = []
# Compile without colored output support
no-color = ["colored/no-color"]
# Automatic using build.rs: use xclip/xsel binary method for clipboard support
clipboard-bin = ["clipboard"]
# Automatic using build.rs: use native clipboard crate for clipboard support
clipboard-crate = ["clipboard"]
[dependencies]
chbs = "0.1.0"
chrono = "0.4"
clap = "2.31"
colored = "1.6"
derive_builder = "0.5"
directories = "0.10"
clap = "2.33"
colored = "2.0"
derive_builder = "0.10"
directories = "4.0"
failure = "0.1"
ffsend-api = "0.0"
ffsend-api = { version = "0.7.3", default-features = false }
fs2 = "0.4"
lazy_static = "1.0"
open = "1"
lazy_static = "1.4"
open = "2"
openssl-probe = "0.1"
pathdiff = "0.2"
pbr = "1"
prettytable-rs = "0.6"
rpassword = "2.0"
serde = "1.0"
serde_derive = "1.0"
prettytable-rs = { version = "0.10.0", default-features = false }
qr2term = { version = "0.2", optional = true }
rand = "0.8"
regex = "1.5"
rpassword = "5"
serde = "1"
serde_derive = "1"
tar = { version = "0.4", optional = true }
tempfile = "3"
toml = "0.4"
version-compare = "0.0.6"
toml = "0.5"
urlshortener = { version = "3", optional = true }
version-compare = "0.1"
[target.'cfg(not(target_os = "linux"))'.dependencies]
clipboard = { version = "0.4", optional = true }
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
which = { version = "4.0", optional = true }
[target.'cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd")))'.dependencies]
# Aliased to clip to prevent name collision with clipboard feature
clip = { version = "0.5", optional = true, package = "clipboard" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View file

@ -1,7 +1,7 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2018 Tim Visee. <https://timvisee.com/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
<https://www.gnu.org/licenses/why-not-lgpl.html>.

719
README.md
View file

@ -1,24 +1,27 @@
[![Build status on Travis CI][travis-master-badge]][travis-link]
[![Build status on GitLab CI][gitlab-ci-master-badge]][gitlab-ci-link]
[![Newest release on crates.io][crate-version-badge]][crate-link]
[![Number of downloads on crates.io][crate-download-badge]][crate-link]
[![Project license][crate-license-badge]](LICENSE)
[crate-link]: https://crates.io/crates/ffsend
[crate-download-badge]: https://img.shields.io/crates/d/ffsend.svg
[crate-version-badge]: https://img.shields.io/crates/v/ffsend.svg
[crate-license-badge]: https://img.shields.io/crates/l/ffsend.svg
[travis-master-badge]: https://travis-ci.org/timvisee/ffsend.svg?branch=master
[travis-link]: https://travis-ci.org/timvisee/ffsend
[crate-link]: https://crates.io/crates/ffsend
[crate-version-badge]: https://img.shields.io/crates/v/ffsend.svg
[gitlab-ci-link]: https://gitlab.com/timvisee/ffsend/pipelines
[gitlab-ci-master-badge]: https://gitlab.com/timvisee/ffsend/badges/master/pipeline.svg
*Notice: the default Send host is provided by [@timvisee][timvisee]
([info](https://gitlab.com/timvisee/ffsend/-/issues/111)).
Please consider to [donate] and help keep it running.*
# ffsend
# ffsend [WIP]
> Easily and securely share files from the command line.
> A fully featured [Firefox Send][send] client.
> A [Send][send] client.
Easily and securely share files and directories from the command line through a
safe, private and encrypted link using a single simple command.
Files are shared using the [Send][send] service and may be up
to 2GB. Others are able to download these files with this tool, or through
their webbrowser.
to 1GB. Others are able to download these files with this tool, or through
their web browser.
[![ffsend usage demo][usage-demo-svg]][usage-demo-asciinema]
_No demo visible here? View it on [asciinema][usage-demo-asciinema]._
@ -33,7 +36,7 @@ Find out more about security [here](#security).
- [Features](#features)
- [Usage](#usage)
- [Requirements](#requirements)
- [Install](#install)
- [Install](#install) ([Linux](#linux-all-distributions), [macOS](#macos), [Windows](#windows), [FreeBSD](#freebsd), [Android](#android), [_Other OS/arch_](#other-os-or-architecture))
- [Build](#build)
- [Configuration and environment](#configuration-and-environment)
- [Security](#security)
@ -42,27 +45,28 @@ Find out more about security [here](#security).
- [License](#license)
The public [Send][send] service that is used as default host is provided by
[Mozilla][mozilla].
This application is not affiliated with [Mozilla][mozilla], [Firefox][firefox]
or [Firefox Send][send] in any way.
[@timvisee][timvisee] ([info](https://gitlab.com/timvisee/ffsend/-/issues/111)).
This application is not affiliated with [Firefox][firefox] or
[Mozilla][mozilla] in any way.
_Note: this tool is currently in the alpha phase_
_Note: this tool is currently in beta, as some extra desired features are yet to be implemented_
## Features
- Fully featured and friendly command line tool
- Upload and download files and directories securely
- Always encrypted on the client
- Additional password protection and configurable download limits
- Built-in file and directory archiving and extraction
- Upload and download files and directories securely, always encrypted on the client
- Additional password protection, generation and configurable download limits
- File and directory archiving and extraction
- Built-in share URL shortener and QR code generator
- Supports Send v3 (current) and v2
- History tracking your files for easy management
- Ability to use your own Send host
- Ability to use your own Send hosts
- Inspect or delete shared files
- Accurate error reporting
- Low memory footprint, due to encryption, download and upload streaming
- Intended to be used in scripts without interaction
- Streaming encryption and uploading/downloading, very low memory footprint
- Intended for use in [scripts](#scriptability) without interaction
For a list of upcoming features and ideas, take a look at the
[ROADMAP](ROADMAP.md) file.
current [open issues](https://gitlab.com/timvisee/ffsend/issues) over on GitLab.
## Usage
Easily upload and download:
@ -70,35 +74,36 @@ Easily upload and download:
```bash
# Simple upload
$ ffsend upload my-file.txt
Share link: https://send.firefox.com/#sample-share-url
https://send.vis.ee/#sample-share-url
# Advanced upload
# - Specify a download limit of 20
# - Specify a download limit of 1
# - Specify upload expiry time of 5 minutes
# - Enter a password to encrypt the file
# - Archive the file before uploading
# - Copy the shareable link to your clipboard
# - Open the shareable link in your browser
$ ffsend upload --downloads 20 --password --archive --copy --open my-file.txt
$ ffsend upload --downloads 1 --expiry-time 5m --password --archive --copy --open my-file.txt
Password: ******
Share link: https://send.firefox.com/#sample-share-url
https://send.vis.ee/#sample-share-url
# Upload to your own host
$ ffsend u -h https://example.com/ my-file.txt
Share link: https://example.com/#sample-share-url
https://example.com/#sample-share-url
# Simple download
$ ffsend download https://send.firefox.com/#sample-share-url
$ ffsend download https://send.vis.ee/#sample-share-url
```
Inspect remote files:
```bash
# Check if a file exists
$ ffsend exists https://send.firefox.com/#sample-share-url
$ ffsend exists https://send.vis.ee/#sample-share-url
Exists: yes
# Fetch remote file info
$ ffsend info https://send.firefox.com/#sample-share-url
$ ffsend info https://send.vis.ee/#sample-share-url
ID: b087066715
Name: my-file.txt
Size: 12 KiB
@ -108,132 +113,420 @@ Expiry: 18h2m (64928s)
```
Other commands include:
```bash
# View your file history
$ ffsend history
# LINK EXPIRY OWNER TOKEN
1 https://send.firefox.com/#sample-share-url 23h57m eea9f544f6d5df8a5afd
2 https://send.firefox.com/#other-sample-url 17h38m 1e9fef63fee3baaf54ce
3 https://example.com/#sample-share-url 37m30s 8eb28bc1bc85cfdab0e4
# LINK EXPIRE
1 https://send.vis.ee/#sample-share-url 23h57m
2 https://send.vis.ee/#other-sample-url 17h38m
3 https://example.com/#sample-share-url 37m30s
# Change the password after uploading
$ ffsend password https://send.firefox.com/#sample-share-url
$ ffsend password https://send.vis.ee/#sample-share-url
Password: ******
# Delete a file
$ ffsend delete https://send.firefox.com/#sample-share-url
$ ffsend delete https://send.vis.ee/#sample-share-url
```
Use the `--help` flag, `help` subcommand, or see the [help](#help) section for
all available subcommands.
## Requirements
- Linux, macOS or Windows
- Linux, macOS, Windows, FreeBSD, Android (other BSDs might work)
- A terminal :sunglasses:
- Linux specific:
- `xclip` for clipboard support (optional)
- Ubuntu/Debian: `apt install xclip`
- Internet connection
- Linux:
- OpenSSL & CA certificates:
- Ubuntu, Debian and derivatives: `apt install openssl ca-certificates`
- Optional: `xclip` or `xsel` for clipboard support
- Ubuntu, Debian and derivatives: `apt install xclip`
- CentOS/Red Hat/openSUSE/Fedora: `yum install xclip`
- Arch: `pacman -S xclip`
- Internet connection for uploading and downloading
- Windows specific:
- Optional OpenSSL with `crypto-openssl` feature: [» Installer][openssl-windows-installer] (`v1.1.0j` or above)
- macOS specific:
- Optional OpenSSL with `crypto-openssl` feature: `brew install openssl@1.1`
- FreeBSD specific:
- OpenSSL: `pkg install openssl`
- CA certificates: `pkg install ca_root_nss`
- Optional `xclip` & `xsel` for clipboard support: `pkg install xclip xsel-conrad`
- Android specific:
- Termux: [» Termux][termux]
## Install
<!-- Before installing, make sure you meet all requirements listed
[here](#requirements) -->
Because `ffsend` is still in early stages, only limited installation options are
available right now. Feel free to contribute additional packages.
Because `ffsend` is still in alpha, only limited installation options are
available right now.
Make sure you meet and install the [requirements](#requirements).
A set of pre-build binaries for Linux and macOS can be found as asset of the
[latest release][github-latest-release]. When downloading such release, mark
the binary as executable using `chmod a+x ffsend`, and move it into `/usr/bin/`.
See the operating system specific instructions below:
- [Linux](#linux-all-distributions)
- [macOS](#macos)
- [Windows](#windows)
- [FreeBSD](#freebsd)
- [Android](#android)
- [_Other OS or architecture_](#other-os-or-architecture)
A Windows binary and packaged versions for various Linux distributions is
currently being worked on.
### Linux (all distributions)
Using the [snap](#linux-snap-package) package is recommended if supported.
Alternatively you may install it manually using the
[prebuilt binaries](#linux-prebuilt-binaries).
It is recommended to build and install `ffsend` yourself using these fairly
easy steps [here](#build).
Only 64-bit (`x86_64`) packages and binaries are provided.
For other architectures and configurations you may [compile from source](#build).
More packages options will be coming soon.
#### Linux: snap package
_Note: The `ffsend` `snap` package is isolated, and can only access files in
your home directory. Choose a different installation option if you don't want
this limitation._
_Note: due to how `snap` is configured by default, you won't be able to use the
package from some contexts such as through SSH without manual modifications. If
you're experiencing problems, please refer to a different installation method
such as the [prebuilt binaries](#linux-prebuilt-binaries), or open an issue._
`ffsend`][snapcraft-ffsend]
```bash
snap install ffsend
ffsend --help
```
#### Linux: Arch AUR packages
`ffsend-bin`][aur-ffsend-bin] (precompiled binary, latest release, recommended)
`ffsend`][aur-ffsend] (compiles from source, latest release)
`ffsend-git`][aur-ffsend-git] (compiles from source, latest `master` commit)
```bash
yay -S ffsend
# or
aurto add ffsend-bin
sudo pacman -S ffsend-bin
# or using any other AUR helper
ffsend --help
```
#### Linux: Nix package
_Note: The Nix package is currently not automatically updated, and might be
slightly outdated._
[» ffsend][nix-ffsend]
```bash
nix-channel --update
nix-env --install ffsend
ffsend --help
```
#### Linux: Fedora package
_Note: The Fedora package is maintained by contributors, and might be
slightly outdated._
[» ffsend][fedora-ffsend]
```bash
sudo dnf install ffsend
ffsend --help
```
#### Linux: Alpine package
_Note: The Alpine package is maintained by contributors, it might be outdated.
Choose a different installation method if an important update is missing._
[» ffsend][alpine-ffsend]
```bash
apk add ffsend --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing
ffsend --help
```
#### Linux: Prebuilt binaries
Check out the [latest release][github-latest-release] assets for Linux binaries.
Use the `ffsend-v*-linux-x64-static` binary, to minimize the chance for issues.
If it isn't available yet, you may use an artifact from a
[previous version][github-releases] instead, until it is available.
Make sure you meet and install the [requirements](#requirements) before you
continue.
You must make the binary executable, and may want to move it into `/usr/bin` to
make it easily executable:
```bash
# Rename binary to ffsend
mv ./ffsend-* ./ffsend
# Mark binary as executable
chmod a+x ./ffsend
# Move binary into path, to make it easily usable
sudo mv ./ffsend /usr/local/bin/
ffsend --help
```
### macOS
Using the [`homebrew` package](#macos-homebrew-package) is recommended.
Alternatively you may install it via [MacPorts](#macos-macports), or manually using the
[prebuilt binaries](#macos-prebuilt-binaries).
#### macOS: homebrew package
Make sure you've [`homebrew`][homebrew] installed, and run:
```bash
brew install ffsend
ffsend --help
```
#### macOS: MacPorts
_Note: ffsend in MacPorts is currently not automatically updated, and might be
slightly outdated._
Once you have [MacPorts](https://www.macports.org) installed, you can run:
```bash
sudo port selfupdate
sudo port install ffsend
```
#### macOS: Nix package
_Note: The Nix package is currently not automatically updated, and might be
slightly outdated._
```bash
nix-channel --update
nix-env --install ffsend
ffsend --help
```
#### macOS: Prebuilt binaries
Check out the [latest release][github-latest-release] assets for a macOS binary.
If it isn't available yet, you may use an artifact from a
[previous version][github-releases] instead, until it is available.
Then, mark the downloaded binary as an executable.
You then may want to move it into `/usr/local/bin/` to make the `ffsend` command
globally available:
```bash
# Rename file to ffsend
mv ./ffsend-* ./ffsend
# Mark binary as executable
chmod a+x ./ffsend
# Move binary into path, to make it easily usable
sudo mv ./ffsend /usr/local/bin/
ffsend
```
### Windows
Using the [`scoop` package](#windows-scoop-package) is recommended.
Alternatively you may install it manually using the
[prebuilt binaries](#windows-prebuilt-binaries).
If you're using the [Windows Subsystem for Linux][wsl], it's highly recommended
to install the [prebuilt Linux binary](#prebuilt-binaries-for-linux) instead.
Only 64-bit (`x86_64`) binaries are provided.
For other architectures and configurations you may [compile from source](#build).
A `chocolatey` package along with an `.msi` installer will be coming soon.
#### Windows: scoop package
Make sure you've [`scoop`][scoop-install] installed, and run:
```bash
scoop install ffsend
ffsend --help
```
#### Windows: Prebuilt binaries
Check out the [latest release][github-latest-release] assets for Windows binaries.
Use the `ffsend-v*-windows-x64-static` binary, to minimize the chance for issues.
If it isn't available yet, you may use an artifact from a
[previous version][github-releases] instead, until it is available.
You can use `ffsend` from the command line in the same directory:
```cmd
.\ffsend.exe --help
```
To make it globally invocable as `ffsend`, you must make the binary available in
your systems `PATH`. The easiest solution is to move it into `System32`:
```cmd
move .\ffsend.exe C:\Windows\System32\ffsend.exe
```
### FreeBSD
`ffsend`][freshports-ffsend]
_Note: The FreeBSD package is currently maintained by FreeBSD contributors,
and might be slightly outdated._
```sh
# Precompiled binary.
pkg install ffsend
# Compiles and installs from source.
cd /usr/ports/www/ffsend && make install
```
### Android
`ffsend` can be used on Android through Termux, install it first:
[» Termux][termux]
_Note: The Android package is currently maintained by Termux contributors,
and might be slightly outdated._
```sh
# Install package.
pkg install ffsend
ffsend help
```
### Other OS or architecture
If your system runs Docker, you can use the [docker image](#docker-image).
There are currently no other binaries or packages available.
You can [build the project from source](#build) instead.
#### Docker image
A Docker image is available for using `ffsend` running in a container.
Mount a directory to `/data`, so it's accessible for `ffsend` in the container,
and use the command as you normally would.
`timvisee/ffsend`][docker-hub-ffsend]
```bash
# Invoke without arguments
docker run --rm -it -v $(pwd):/data timvisee/ffsend
# Upload my-file.txt
docker run --rm -it -v $(pwd):/data timvisee/ffsend upload my-file.txt
# Download from specified link
docker run --rm -it -v $(pwd):/data timvisee/ffsend download https://send.vis.ee/#sample-share-url
# Show help
docker run --rm -it -v $(pwd):/data timvisee/ffsend help
# To update the used image
docker pull timvisee/ffsend
```
On Linux or macOS you might define a alias in your shell configuration, to make
it invocable as `ffsend`:
```bash
alias ffsend='docker run --rm -it -v "$(pwd):/data" timvisee/ffsend'
```
_Note: This implementation is limited to accessing the paths you make available
through the specified mount._
## Build
To build and install `ffsend` yourself, you meet the following requirements
before proceeding:
### Build requirements
- Regular [requirements](#requirements)
- Runtime [requirements](#requirements)
- [`git`][git]
- [`rust`][rust] `v1.26` or higher (install using [`rustup`][rustup])
- [OpenSSL][openssl] or [LibreSSL][libressl] libraries and headers must be available
- Linux:
- Ubuntu/Debian: `apt install pkg-config libssl-dev`
- CentOS/Red Hat/openSUSE: `yum install openssl-devel`
- Arch: `pacman -S openssl`
- Fedora: `dnf install openssl-devel`
- Or see instructions [here](https://github.com/sfackler/rust-openssl#linux)
- macOS:
- Using `brew`: `brew install openssl`
- Or see instructions [here](https://github.com/sfackler/rust-openssl#osx)
- Windows:
- See instructions here [here](https://github.com/sfackler/rust-openssl#windows-msvc)
- [`rust`][rust] `v1.63` (MSRV) or higher (install using [`rustup`][rustup])
- [OpenSSL][openssl] or [LibreSSL][libressl] libraries/headers:
- Linux:
- Ubuntu, Debian and derivatives: `apt install build-essential cmake pkg-config libssl-dev`
- CentOS/Red Hat/openSUSE: `yum install gcc gcc-c++ make cmake openssl-devel`
- Arch: `pacman -S openssl base-devel`
- Gentoo: `emerge -a dev-util/pkgconfig dev-util/cmake dev-libs/openssl`
- Fedora: `dnf install gcc gcc-c++ make cmake openssl-devel`
- Or see instructions [here](https://github.com/sfackler/rust-openssl#linux)
- Windows:
- Optional with `crypto-openssl` feature: See instructions here [here](https://github.com/sfackler/rust-openssl#windows-msvc)
- macOS:
- Optional with `crypto-openssl` feature: `brew install cmake pkg-config openssl` or see instructions [here](https://github.com/sfackler/rust-openssl#osx)
- FreeBSD:
- `pkg install rust gmake pkgconf python36 libxcb xclip ca_root_nss xsel-conrad`
- It is a better idea to use & modify the existing `ffsend` port, which manages dependencies for you.
### Compile and install
Then, walk through one of the following steps to compile and install `ffsend`:
- Compile and install it directly from cargo:
```bash
# Compile and install from cargo
cargo install ffsend -f
```bash
# Compile and install from cargo
cargo install ffsend -f
# Start using ffsend
ffsend --help
```
# Start using ffsend
ffsend --help
```
- Or clone the repository and install it with `cargo`:
```bash
# Clone the project
git clone https://github.com/timvisee/ffsend.git
cd ffsend
```bash
# Clone the project
git clone https://github.com/timvisee/ffsend.git
cd ffsend
# Compile and install
cargo install -f
# Compile and install
cargo install --path . -f
# Start using ffsend
ffsend --help
# Start using ffsend
ffsend --help
# or run it directly from cargo
cargo run --release -- --help
```
# or run it directly from cargo
cargo run --release -- --help
```
- Or clone the repository and invoke the binary directly (Linux/macOS):
```bash
# Clone the project
git clone https://github.com/timvisee/ffsend.git
cd ffsend
```bash
# Clone the project
git clone https://github.com/timvisee/ffsend.git
cd ffsend
# Build the project (release version)
cargo build --release
# Build the project (release version)
cargo build --release
# Start using ffsend
./target/release/ffsend --help
```
# Start using ffsend
./target/release/ffsend --help
```
### Compile features / use flags
Different use flags are available for `ffsend` to toggle whether to include
various features.
The following features are available, some of which are enabled by default:
| Feature | Enabled | Description |
| :---------: | :-----: | :--------------------------------------------------------- |
| `clipboard` | Default | Support for copying links to the clipboard |
| `history` | Default | Support for tracking files in history |
| `archive` | Default | Support for archiving and extracting uploads and downloads |
| `no-color` | | Compile without color support in error and help messages |
| Feature | Enabled | Description |
| :-------------: | :-----: | :--------------------------------------------------------- |
| `send2` | Default | Support for Send v2 servers |
| `send3` | Default | Support for Send v3 servers |
| `crypto-ring` | Default | Use ring as cryptography backend |
| `crypto-openssl`| | Use OpenSSL as cryptography backend |
| `clipboard` | Default | Support for copying links to the clipboard |
| `history` | Default | Support for tracking files in history |
| `archive` | Default | Support for archiving and extracting uploads and downloads |
| `qrcode` | Default | Support for rendering a QR code for a share URL |
| `urlshorten` | Default | Support for shortening share URLs |
| `infer-command` | Default | Support for inferring subcommand based on binary name |
| `no-color` | | Compile without color support in error and help messages |
To enable features during building or installation, specify them with
`--features <features, >` when using `cargo`.
You may want to disable alisl default features first using
`--features <features...>` when using `cargo`.
You may want to disable default features first using
`--no-default-features`.
Here are some examples:
@ -242,46 +535,172 @@ Here are some examples:
cargo install --features no-color
cargo build --release --features no-color
# None of the features
cargo install --no-default-features
# No default features, except required
cargo install --no-default-features --features send3,crypto-ring
# Only history and clipboard support
cargo install --no-default--features --features history,clipboard
# With history and clipboard support
cargo install --no-default--features --features send3,crypto-ring,history,clipboard
```
For Windows systems it is recommended to provide the `no-color` flag, as color
support in Windows terminals is flaky.
## Configuration and environment
The following environment variables may be used to configure the following
defaults. The CLI flag is shown along with it, to better describe the relation
to command line arguments:
| Variable | CLI flag | Description |
| :--------------- | :----------------: | :---------------- |
| `FFSEND_HISTORY` | `--history <FILE>` | History file path |
| `FFSEND_HOST` | `--host <URL>` | Upload host |
| Variable | CLI flag | Description |
| :------------------------ | :----------------------------: | :-------------------------------------------- |
| `FFSEND_HISTORY` | `--history <FILE>` | History file path |
| `FFSEND_HOST` | `--host <URL>` | Upload host |
| `FFSEND_TIMEOUT` | `--timeout <SECONDS>` | Request timeout (0 to disable) |
| `FFSEND_TRANSFER_TIMEOUT` | `--transfer-timeout <SECONDS>` | Transfer timeout (0 to disable) |
| `FFSEND_EXPIRY_TIME` | `--expiry-time <SECONDS>` | Default upload expiry time |
| `FFSEND_DOWNLOAD_LIMIT` | `--download-limit <DOWNLOADS>` | Default download limit |
| `FFSEND_API` | `--api <VERSION>` | Server API version, `-` to lookup |
| `FFSEND_BASIC_AUTH` | `--basic-auth <USER:PASSWORD>` | Basic HTTP authentication credentials to use. |
These environment variables may be used to toggle a flag, simply by making them
available. The actual value of these variables is ignored, and variables may be
empty.
| Variable | CLI flag | Description |
| :------------------- | :-------------: | :-------------------------------- |
| `FFSEND_FORCE` | `--force` | Force operations |
| `FFSEND_NO_INTERACT` | `--no-interact` | No interaction for prompts |
| `FFSEND_YES` | `--yes` | Assume yes for prompts |
| `FFSEND_INCOGNITO` | `--incognito` | Incognito mode, don't use history |
| `FFSEND_OPEN` | `--open` | Open share link of uploaded file |
| `FFSEND_ARCHIVE` | `--archive` | Archive files uploaded |
| `FFSEND_EXTRACT` | `--extract` | Extract files downloaded |
| `FFSEND_COPY` | `--copy` | Copy share link to clipboard |
| `FFSEND_VERBOSE` | `--verbose` | Log verbose information |
| Variable | CLI flag | Description |
| :------------------- | :-------------: | :--------------------------------- |
| `FFSEND_FORCE` | `--force` | Force operations |
| `FFSEND_NO_INTERACT` | `--no-interact` | No interaction for prompts |
| `FFSEND_YES` | `--yes` | Assume yes for prompts |
| `FFSEND_INCOGNITO` | `--incognito` | Incognito mode, don't use history |
| `FFSEND_OPEN` | `--open` | Open share link of uploaded file |
| `FFSEND_ARCHIVE` | `--archive` | Archive files uploaded |
| `FFSEND_EXTRACT` | `--extract` | Extract files downloaded |
| `FFSEND_COPY` | `--copy` | Copy share link to clipboard |
| `FFSEND_COPY_CMD` | `--copy-cmd` | Copy download command to clipboard |
| `FFSEND_QUIET` | `--quiet` | Log quiet information |
| `FFSEND_VERBOSE` | `--verbose` | Log verbose information |
Some environment variables may be set at compile time to tweak some defaults.
| Variable | Description |
| :----------- | :------------------------------------------------------------------------- |
| `XCLIP_PATH` | Set fixed `xclip` binary path when using `clipboard-bin` (Linux, &ast;BSD) |
| `XSEL_PATH` | Set fixed `xsel` binary path when using `clipboard-bin` (Linux, &ast;BSD) |
At this time, no configuration or _dotfile_ file support is available.
This will be something added in a later release.
### Binary for each subcommand: `ffput`, `ffget`
`ffsend` supports having a separate binaries for single subcommands, such as
having `ffput` and `ffget` just for to upload and download using `ffsend`.
This allows simple and direct commands like:
```bash
ffput my-file.txt
ffget https://send.vis.ee/#sample-share-url
```
This works for a predefined list of binary names:
- `ffput``ffsend upload ...`
- `ffget``ffsend download ...`
- `ffdel``ffsend delete ...`
- _This list is defined in [`src/config.rs`](./src/config.rs) as `INFER_COMMANDS`_
You can use the following methods to set up these single-command binaries:
- Create a properly named symbolic link (recommended)
- Create a properly named hard link
- Clone the `ffsend` binary, and rename it
On Linux and macOS you can use the following command to set up symbolic links in
the current directory:
```bash
ln -s $(which ffsend) ./ffput
ln -s $(which ffsend) ./ffget
```
Support for this feature is only available when `ffsend` is compiled with the
[`infer-command`](#compile-features--use-flags) feature flag.
This is usually enabled by default.
To verify support is available with an existing installation, make sure the
feature is listed when invoking `ffsend debug`.
Note that the `snap` package does currently not support this due to how this
package format works.
### Scriptability
`ffsend` is optimized for use in automated scripts. It provides some specialized
arguments to control `ffsend` without user interaction.
- `--no-interact` (`-I`): do not allow user interaction. For prompts not having
a default value, the application will quit with an error, unless `--yes`
or `--force` is provided.
This should **always** be given when using automated scripting.
Example: when uploading a directory, providing this flag will stop the
archive question prompt form popping up, and will archive the directory as
default option.
- `--yes` (`-y`): assume the yes option for yes/no prompt by default.
Example: when downloading a file that already exists, providing this flag
will assume yes when asking to overwrite a file.
- `--force` (`-f`): force to continue with the action, skips any warnings that
would otherwise quit the application.
Example: when uploading a file that is too big, providing this flag will
ignore the file size warning and forcefully continues.
- `--quiet` (`-q`): be quiet, print as little information as possible.
Example: when uploading a file, providing this flag will only output the
final share URL.
Generally speaking, use the following rules when automating:
- Always provide `--no-interact` (`-I`).
- Provide any combination of `--yes` (`-y`) and `--force` (`-f`) for actions you
want to complete no matter what.
- When passing share URLs along, provide the `--quiet` (`-q`) flag, when
uploading for example.
These flags can also automatically be set by defining environment variables as
specified here:
[» Configuration and environment](#configuration-and-environment)
Here are some examples commands in `bash`:
```bash
# Stop on error
set -e
# Upload a file
# -I: no interaction
# -y: assume yes
# -q: quiet output, just return the share link
URL=$(ffsend -Iy upload -q my-file.txt)
# Render file information
# -I: no interaction
# -f: force, just show the info
ffsend -If info $URL
# Set a password for the uploaded file
ffsend -I password $URL --password="secret"
# Use the following flags automatically from now on
# -I: no interaction
# -f: force
# -y: yes
export FFSEND_NO_INTERACT=1 FFSEND_FORCE=1 FFSEND_YES=1
# Download the uploaded file, overwriting the local variant due to variables
ffsend download $URL --password="secret"
```
For more information on these arguments, invoke `ffsend help` and check out:
[» Configuration and environment](#configuration-and-environment)
For other questions regarding automation or feature requests, be sure to
[open](https://github.com/timvisee/ffsend/issues/) an issue.
## Security
In short; the `ffsend` tool and the [Send][send] service can be considered
secure, and may be used to share sensitive files. Note though that the
created share link for an upload will allow anyone to download the file.
created share link for an upload will allow anyone to download the file.
Make sure you don't share this link with unauthorized people.
For more detailed information on encryption, please read the rest of the
@ -295,7 +714,7 @@ somehow got decrypted without proper authorization._
`ffsend` uses client side encryption, to ensure your files are securely
encrypted before they are uploaded to the remote host. This makes it impossible
for third parties to decrypt your file without having the secret (encryption
key). The file and it's metadata are encrypted using `128-bit AES-GCM`, and a
key). The file and its metadata are encrypted using `128-bit AES-GCM`, and a
`HMAC SHA-256` signing key is used for request authentication.
This is consistent with the encryption documentation provided by the
[Send][send] service, `ffsend` is a tool for.
@ -325,10 +744,13 @@ documentation [here][send-encryption].
```
$ ffsend help
ffsend 0.0.7
Tim Visee <https://timvisee.com/>
ffsend 0.2.72
Tim Visee <3a4fb3964f@sinenomine.email>
Easily and securely share files from the command line.
A fully featured Firefox Send client.
A fully featured Send client.
The default public Send host is provided by Tim Visee, @timvisee.
Please consider to donate and help keep it running: https://vis.ee/donate
USAGE:
ffsend [FLAGS] [OPTIONS] [SUBCOMMAND]
@ -338,51 +760,76 @@ FLAGS:
-h, --help Prints help information
-i, --incognito Don't update local history for actions
-I, --no-interact Not interactive, do not prompt
-q, --quiet Produce output suitable for logging and automation
-V, --version Prints version information
-v, --verbose Enable verbose information and logging
-y, --yes Assume yes for prompts
OPTIONS:
-H, --history <FILE> Use the specified history file [env: FFSEND_HISTORY]
-A, --api <VERSION> Server API version to use, '-' to lookup [env: FFSEND_API]
--basic-auth <USER:PASSWORD> Protected proxy HTTP basic authentication credentials (not FxA) [env: FFSEND_BASIC_AUTH]
-H, --history <FILE> Use the specified history file [env: FFSEND_HISTORY]
-t, --timeout <SECONDS> Request timeout (0 to disable) [env: FFSEND_TIMEOUT]
-T, --transfer-timeout <SECONDS> Transfer timeout (0 to disable) [env: FFSEND_TRANSFER_TIMEOUT]
SUBCOMMANDS:
upload Upload files [aliases: u, up]
download Download files [aliases: d, down]
debug View debug information [aliases: dbg]
delete Delete a shared file [aliases: del]
delete Delete a shared file [aliases: del, rm]
exists Check whether a remote file exists [aliases: e]
generate Generate assets [aliases: gen]
help Prints this message or the help of the given subcommand(s)
history View file history [aliases: h]
info Fetch info about a shared file [aliases: i]
parameters Change parameters of a shared file [aliases: params]
password Change the password of a shared file [aliases: pass, p]
version Determine the Send server version [aliases: v]
The public Send service that is used as default host is provided by Mozilla.
This application is not affiliated with Mozilla, Firefox or Firefox Send.
This application is not affiliated with Firefox or Mozilla.
```
## Special thanks
- to [Mozilla][mozilla] for building and hosting the amazing
[Firefox Send][send] service
- to all `ffsend` source/package contributors
- to [Mozilla][mozilla] for building the amazing [Firefox Send][mozilla-send] service ([fork][timvisee-send])
- to everyone involved with [asciinema][asciinema] and [svg-term][svg-term] for
providing tools to make great visual demos
- to everyone involved in all crate dependencies used
## License
This project is released under the GNU GPL-3.0 license.
Check out the [LICENSE](LICENSE) file for more information.
Check out the [LICENSE](LICENSE) file for more information.
[usage-demo-asciinema]: https://asciinema.org/a/182225
[usage-demo-svg]: https://cdn.rawgit.com/timvisee/ffsend/6e8ef55b/res/demo.svg
[firefox]: https://firefox.com/
[git]: https://git-scm.com/
[libressl]: https://libressl.org/
[mozilla]: https://mozzilla.org/
[mozilla]: https://mozilla.org/
[openssl]: https://www.openssl.org/
[openssl-windows-installer]: https://u.visee.me/dl/openssl/Win64OpenSSL_Light-1_1_0j.exe
[termux]: https://termux.com/
[rust]: https://rust-lang.org/
[rustup]: https://rustup.rs/
[send]: https://send.firefox.com/
[send-encryption]: https://github.com/mozilla/send/blob/master/docs/encryption.md
[send]: https://github.com/timvisee/send
[mozilla-send]: https://github.com/mozilla/send
[timvisee-send]: https://github.com/timvisee/send
[send-encryption]: https://github.com/timvisee/send/blob/master/docs/encryption.md
[asciinema]: https://asciinema.org/
[svg-term]: https://github.com/marionebl/svg-term-cli
[github-releases]: https://github.com/timvisee/ffsend/releases
[github-latest-release]: https://github.com/timvisee/ffsend/releases/latest
[nix-ffsend]: https://nixos.org/nixos/packages.html?attr=ffsend&channel=nixos-unstable&query=ffsend
[fedora-ffsend]: https://src.fedoraproject.org/rpms/rust-ffsend
[aur-ffsend]: https://aur.archlinux.org/packages/ffsend/
[aur-ffsend-bin]: https://aur.archlinux.org/packages/ffsend-bin/
[aur-ffsend-git]: https://aur.archlinux.org/packages/ffsend-git/
[alpine-ffsend]: https://pkgs.alpinelinux.org/packages?name=ffsend&branch=edge
[snapcraft-ffsend]: https://snapcraft.io/ffsend
[homebrew]: https://brew.sh/
[wsl]: https://docs.microsoft.com/en-us/windows/wsl/install-win10
[docker-hub-ffsend]: https://hub.docker.com/r/timvisee/ffsend
[scoop-install]: https://scoop.sh/#installs-in-seconds
[freshports-ffsend]: https://www.freshports.org/www/ffsend
[timvisee]: https://timvisee.com/
[donate]: https://timvisee.com/donate

View file

@ -1,60 +0,0 @@
# Alpha release 0.0.1 (private feedback)
The first release used for gathering feedback on the application by selected
people.
Features:
- Do not show default values for `--host`, see `ffsend help upload`
- History clear command
- Ask to remove a file when downloading failed
- Polish command outputs, make it consistent (format, color)
- Automated releases through CI
- Release binaries on GitHub
- Ubuntu PPA package
- Gentoo portage package
- Arch AUR package
- Windows, macOS and Redox support
- Implement verbose logging with `-v`
- Make use of stdout and stderr consistent
- Allow empty owner token for info command
- Check and validate all errors, are some too verbose?
# Beta release 0.1 (public)
The first public release.
Features:
- Do not write archives to the disk (temporarily), stream their contents
- Implement error handling everywhere properly
- Extract utility module
- Embed/wrap request errors with failure
- Box errors
- Allow piping input/output files
- Allow hiding the progress bar, and/or showing simple progress (with `-q`)
- Implement a quiet `-q` mode
- Update all dependencies
- Check all TODOs, solve them when possible
- Allow multi file uploads (select more than one file or directory)
# Future releases
- Color usage flag
- A status command, to check the server status using `/__version__` and
heartbeat endpoints
- Host configuration file for host tags, to easily upload to other hosts
# Other ideas
- Check if extracting an archive overwrites files
- Flag to disable logging to stderr
- Rework encrypted reader/writer
- API actions contain duplicate code, create centralized functions
- Only allow file extension renaming on upload with `-f` flag
- Quick upload/download without `upload` or `download` subcommands?
- Flag to explicitly delete file after download
- Allow file deletion by consuming all download slots
- Download to a temporary location first
- Document all code components
- Dotfile for default properties
- Generate man pages
- Rename host to server?
- Ask to add MIME extension to downloaded files without one on Windows
- Fetch max file size from `server/jsconfig.js`
- Define a redirect policy (allow setting max redirects)
- Support servers that are hosted on a sub path (URL builder resets path)

18
SECURITY.md Normal file
View file

@ -0,0 +1,18 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 0.2.x | :white_check_mark: |
## Reporting a Vulnerability
To report a vulnerability in this project, or in one if it's dependency;
please [open](https://gitlab.com/timvisee/ffsend/issues/new) a new issue
on GitLab (or on [GitHub](https://github.com/timvisee/ffsend/issues/new)) describing the situation.
For confidential issues, you may send an email to the main developer (@timvisee) at: `3a4fb3964f@sinenomine.email`
Or see other methods of contacting over on: [timvisee.com/contact](https://timvisee.com/contact)
To contribute code for fixing a vulnerability, please refer to the [contribution](./CONTRIBUTING.md) document.

79
appveyor.yml Normal file
View file

@ -0,0 +1,79 @@
# AppVeyor CI configuration for building Windows binaries for ffsend.
# These Windows binaries are published on GitHub as release files.
#
# The main CI runs on GitLab CI at: https://gitlab.com/timvisee/ffsend/pipelines
# Only build version tags
skip_non_tags: true
branches:
only:
- /v\d*\.\d*\.\d*/
# Build for the x86_64 Windows target
platform: x64
environment:
RUSTUP_USE_HYPER: 1
CARGO_HTTP_CHECK_REVOKE: false
GITHUB_TOKEN:
secure: jqZ4q5oOthKX/pBL1tRsBJsfGPIee3q+N/UBSCZNjCrlFUNfQSfibBPzzICYg1he
CHOCOLATEY_TOKEN:
secure: k5Q57xoXa6qSFScSpRaww2puW0yjYoH19uIq3qS1emOG+lNs9TYCnWYhUzQ2gzfc
matrix:
- TARGET: x86_64-pc-windows-msvc
BITS: 64
# Extract release binary artifacts
artifacts:
- path: .\ffsend*.exe
- path: .\ffsend.*.nupkg
# Install dependencies: Rust
install:
# Install Rust
- appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain stable
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustc -V
- cargo -V
- git submodule update --init
# Build dynamic and static Windows binaries, release on GitHub
build_script:
# Build dynamic release binary
- cargo build --release --features no-color --verbose
- copy .\target\release\ffsend.exe .\ffsend-%TARGET%.exe
# Build static release binary
- set RUSTFLAGS=-Ctarget-feature=+crt-static
- cargo build --release --features no-color --verbose
- copy .\target\release\ffsend.exe .\ffsend-%TARGET%-static.exe
# Install github-release
- appveyor DownloadFile https://github.com/tfausak/github-release/releases/download/1.2.4/github-release-windows.zip -FileName github-release.zip
- 7z e github-release.zip
# Collect binary, set package version and build chocolatey package
- copy .\ffsend-%TARGET%-static.exe .\pkg\choco\ffsend\tools\ffsend.exe
- cd .\pkg\choco\ffsend\
- ps: echo $env:APPVEYOR_REPO_TAG_NAME
- ps: ((Get-Content -path .\ffsend.nuspec -Raw) -replace "0.0.0",$env:APPVEYOR_REPO_TAG_NAME.Substring(1)) | Set-Content -Path .\ffsend.nuspec
- choco pack
- copy ffsend.*.nupkg ..\..\..\
- cd ..\..\..\
# Create the release, upload the binaries
# TODO: fix the line below, which is causing CI to error
# - ps: Invoke-Expression -Command '$ErrorActionPreference = "SilentlyContinue"; .\github-release.exe release --token $env:GITHUB_TOKEN --owner timvisee --repo ffsend --tag $env:APPVEYOR_REPO_TAG_NAME --title "ffsend $env:APPVEYOR_REPO_TAG_NAME"; exit 0'
- ps: .\github-release.exe upload --token $env:GITHUB_TOKEN --owner timvisee --repo ffsend --tag $env:APPVEYOR_REPO_TAG_NAME --file .\ffsend-$env:TARGET.exe --name ffsend-$env:APPVEYOR_REPO_TAG_NAME-windows-x64.exe
- ps: .\github-release.exe upload --token $env:GITHUB_TOKEN --owner timvisee --repo ffsend --tag $env:APPVEYOR_REPO_TAG_NAME --file .\ffsend-$env:TARGET-static.exe --name ffsend-$env:APPVEYOR_REPO_TAG_NAME-windows-x64-static.exe
- ps: .\github-release.exe upload --token $env:GITHUB_TOKEN --owner timvisee --repo ffsend --tag $env:APPVEYOR_REPO_TAG_NAME --file (Get-Item .\ffsend.*.nupkg).FullName --name ffsend-$env:APPVEYOR_REPO_TAG_NAME.nupkg
# Publish the chocolatey package
# TODO: re-enable when chocolatey is properly accepting packages again
# - cd .\pkg\choco\ffsend\
# - choco push --api-key %CHOCOLATEY_TOKEN%
# - cd ..\..\..\
# We don't test anything here
test: false

37
build.rs Normal file
View file

@ -0,0 +1,37 @@
fn main() {
#[cfg(all(
feature = "clipboard",
any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "netbsd",
)
))]
{
// Select clipboard binary method
#[cfg(not(feature = "clipboard-crate"))]
println!("cargo:rustc-cfg=feature=\"clipboard-bin\"");
// xclip and xsel paths are inserted at compile time
println!("cargo:rerun-if-env-changed=XCLIP_PATH");
println!("cargo:rerun-if-env-changed=XSEL_PATH");
}
#[cfg(all(
feature = "clipboard",
not(any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "netbsd",
))
))]
{
// Select clipboard crate method
#[cfg(not(feature = "clipboard-bin"))]
println!("cargo:rustc-cfg=feature=\"clipboard-crate\"");
}
}

1919
contrib/completions/_ffsend vendored Normal file

File diff suppressed because it is too large Load diff

515
contrib/completions/_ffsend.ps1 vendored Normal file
View file

@ -0,0 +1,515 @@
using namespace System.Management.Automation
using namespace System.Management.Automation.Language
Register-ArgumentCompleter -Native -CommandName 'ffsend' -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
$commandElements = $commandAst.CommandElements
$command = @(
'ffsend'
for ($i = 1; $i -lt $commandElements.Count; $i++) {
$element = $commandElements[$i]
if ($element -isnot [StringConstantExpressionAst] -or
$element.StringConstantType -ne [StringConstantType]::BareWord -or
$element.Value.StartsWith('-')) {
break
}
$element.Value
}) -join ';'
$completions = @(switch ($command) {
'ffsend' {
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('debug', 'debug', [CompletionResultType]::ParameterValue, 'View debug information')
[CompletionResult]::new('delete', 'delete', [CompletionResultType]::ParameterValue, 'Delete a shared file')
[CompletionResult]::new('download', 'download', [CompletionResultType]::ParameterValue, 'Download files')
[CompletionResult]::new('exists', 'exists', [CompletionResultType]::ParameterValue, 'Check whether a remote file exists')
[CompletionResult]::new('generate', 'generate', [CompletionResultType]::ParameterValue, 'Generate assets')
[CompletionResult]::new('info', 'info', [CompletionResultType]::ParameterValue, 'Fetch info about a shared file')
[CompletionResult]::new('parameters', 'parameters', [CompletionResultType]::ParameterValue, 'Change parameters of a shared file')
[CompletionResult]::new('password', 'password', [CompletionResultType]::ParameterValue, 'Change the password of a shared file')
[CompletionResult]::new('upload', 'upload', [CompletionResultType]::ParameterValue, 'Upload files')
[CompletionResult]::new('version', 'version', [CompletionResultType]::ParameterValue, 'Determine the Send server version')
[CompletionResult]::new('history', 'history', [CompletionResultType]::ParameterValue, 'View file history')
[CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, 'Prints this message or the help of the given subcommand(s)')
break
}
'ffsend;debug' {
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'The remote host to upload to')
[CompletionResult]::new('--host', 'host', [CompletionResultType]::ParameterName, 'The remote host to upload to')
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
break
}
'ffsend;delete' {
[CompletionResult]::new('-o', 'o', [CompletionResultType]::ParameterName, 'Specify the file owner token')
[CompletionResult]::new('--owner', 'owner', [CompletionResultType]::ParameterName, 'Specify the file owner token')
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
break
}
'ffsend;download' {
[CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Unlock a password protected file')
[CompletionResult]::new('--password', 'password', [CompletionResultType]::ParameterName, 'Unlock a password protected file')
[CompletionResult]::new('-o', 'o', [CompletionResultType]::ParameterName, 'Output file or directory')
[CompletionResult]::new('--output', 'output', [CompletionResultType]::ParameterName, 'Output file or directory')
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('-e', 'e', [CompletionResultType]::ParameterName, 'Extract an archived file')
[CompletionResult]::new('--extract', 'extract', [CompletionResultType]::ParameterName, 'Extract an archived file')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
break
}
'ffsend;exists' {
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
break
}
'ffsend;generate' {
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('completions', 'completions', [CompletionResultType]::ParameterValue, 'Shell completions')
[CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, 'Prints this message or the help of the given subcommand(s)')
break
}
'ffsend;generate;completions' {
[CompletionResult]::new('-o', 'o', [CompletionResultType]::ParameterName, 'Shell completion files output directory')
[CompletionResult]::new('--output', 'output', [CompletionResultType]::ParameterName, 'Shell completion files output directory')
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
break
}
'ffsend;generate;help' {
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
break
}
'ffsend;info' {
[CompletionResult]::new('-o', 'o', [CompletionResultType]::ParameterName, 'Specify the file owner token')
[CompletionResult]::new('--owner', 'owner', [CompletionResultType]::ParameterName, 'Specify the file owner token')
[CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Unlock a password protected file')
[CompletionResult]::new('--password', 'password', [CompletionResultType]::ParameterName, 'Unlock a password protected file')
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
break
}
'ffsend;parameters' {
[CompletionResult]::new('-o', 'o', [CompletionResultType]::ParameterName, 'Specify the file owner token')
[CompletionResult]::new('--owner', 'owner', [CompletionResultType]::ParameterName, 'Specify the file owner token')
[CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'The file download limit')
[CompletionResult]::new('--download-limit', 'download-limit', [CompletionResultType]::ParameterName, 'The file download limit')
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
break
}
'ffsend;password' {
[CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Specify a password, do not prompt')
[CompletionResult]::new('--password', 'password', [CompletionResultType]::ParameterName, 'Specify a password, do not prompt')
[CompletionResult]::new('-o', 'o', [CompletionResultType]::ParameterName, 'Specify the file owner token')
[CompletionResult]::new('--owner', 'owner', [CompletionResultType]::ParameterName, 'Specify the file owner token')
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('-P', 'P', [CompletionResultType]::ParameterName, 'Protect the file with a generated passphrase')
[CompletionResult]::new('--gen-passphrase', 'gen-passphrase', [CompletionResultType]::ParameterName, 'Protect the file with a generated passphrase')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
break
}
'ffsend;upload' {
[CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Protect the file with a password')
[CompletionResult]::new('--password', 'password', [CompletionResultType]::ParameterName, 'Protect the file with a password')
[CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'The file download limit')
[CompletionResult]::new('--download-limit', 'download-limit', [CompletionResultType]::ParameterName, 'The file download limit')
[CompletionResult]::new('-e', 'e', [CompletionResultType]::ParameterName, 'The file expiry time')
[CompletionResult]::new('--expiry-time', 'expiry-time', [CompletionResultType]::ParameterName, 'The file expiry time')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'The remote host to upload to')
[CompletionResult]::new('--host', 'host', [CompletionResultType]::ParameterName, 'The remote host to upload to')
[CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'Rename the file being uploaded')
[CompletionResult]::new('--name', 'name', [CompletionResultType]::ParameterName, 'Rename the file being uploaded')
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('-P', 'P', [CompletionResultType]::ParameterName, 'Protect the file with a generated passphrase')
[CompletionResult]::new('--gen-passphrase', 'gen-passphrase', [CompletionResultType]::ParameterName, 'Protect the file with a generated passphrase')
[CompletionResult]::new('-o', 'o', [CompletionResultType]::ParameterName, 'Open the share link in your browser')
[CompletionResult]::new('--open', 'open', [CompletionResultType]::ParameterName, 'Open the share link in your browser')
[CompletionResult]::new('-D', 'D', [CompletionResultType]::ParameterName, 'Delete local file after upload')
[CompletionResult]::new('--delete', 'delete', [CompletionResultType]::ParameterName, 'Delete local file after upload')
[CompletionResult]::new('-a', 'a', [CompletionResultType]::ParameterName, 'Archive the upload in a single file')
[CompletionResult]::new('--archive', 'archive', [CompletionResultType]::ParameterName, 'Archive the upload in a single file')
[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Copy the share link to your clipboard')
[CompletionResult]::new('--copy', 'copy', [CompletionResultType]::ParameterName, 'Copy the share link to your clipboard')
[CompletionResult]::new('-C', 'C', [CompletionResultType]::ParameterName, 'Copy the ffsend download command to your clipboard')
[CompletionResult]::new('--copy-cmd', 'copy-cmd', [CompletionResultType]::ParameterName, 'Copy the ffsend download command to your clipboard')
[CompletionResult]::new('-S', 'S', [CompletionResultType]::ParameterName, 'Shorten share URLs with a public service')
[CompletionResult]::new('--shorten', 'shorten', [CompletionResultType]::ParameterName, 'Shorten share URLs with a public service')
[CompletionResult]::new('-Q', 'Q', [CompletionResultType]::ParameterName, 'Print a QR code for the share URL')
[CompletionResult]::new('--qrcode', 'qrcode', [CompletionResultType]::ParameterName, 'Print a QR code for the share URL')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
break
}
'ffsend;version' {
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'The remote host to upload to')
[CompletionResult]::new('--host', 'host', [CompletionResultType]::ParameterName, 'The remote host to upload to')
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
break
}
'ffsend;history' {
[CompletionResult]::new('-R', 'R', [CompletionResultType]::ParameterName, 'Remove history entry')
[CompletionResult]::new('--rm', 'rm', [CompletionResultType]::ParameterName, 'Remove history entry')
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('-C', 'C', [CompletionResultType]::ParameterName, 'Clear all history')
[CompletionResult]::new('--clear', 'clear', [CompletionResultType]::ParameterName, 'Clear all history')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
break
}
'ffsend;help' {
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Request timeout (0 to disable)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('--transfer-timeout', 'transfer-timeout', [CompletionResultType]::ParameterName, 'Transfer timeout (0 to disable)')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--api', 'api', [CompletionResultType]::ParameterName, 'Server API version to use, ''-'' to lookup')
[CompletionResult]::new('--basic-auth', 'basic-auth', [CompletionResultType]::ParameterName, 'Protected proxy HTTP basic authentication credentials (not FxA)')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('--history', 'history', [CompletionResultType]::ParameterName, 'Use the specified history file')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('--force', 'force', [CompletionResultType]::ParameterName, 'Force the action, ignore warnings')
[CompletionResult]::new('-I', 'I', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('--no-interact', 'no-interact', [CompletionResultType]::ParameterName, 'Not interactive, do not prompt')
[CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Assume yes for prompts')
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Produce output suitable for logging and automation')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Enable verbose information and logging')
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
[CompletionResult]::new('--incognito', 'incognito', [CompletionResultType]::ParameterName, 'Don''t update local history for actions')
break
}
})
$completions.Where{ $_.CompletionText -like "$wordToComplete*" } |
Sort-Object -Property ListItemText
}

2512
contrib/completions/ffsend.bash vendored Normal file

File diff suppressed because it is too large Load diff

493
contrib/completions/ffsend.elv vendored Normal file
View file

@ -0,0 +1,493 @@
edit:completion:arg-completer[ffsend] = [@words]{
fn spaces [n]{
repeat $n ' ' | joins ''
}
fn cand [text desc]{
edit:complex-candidate $text &display-suffix=' '(spaces (- 14 (wcswidth $text)))$desc
}
command = 'ffsend'
for word $words[1:-1] {
if (has-prefix $word '-') {
break
}
command = $command';'$word
}
completions = [
&'ffsend'= {
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
cand -h 'Prints help information'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand debug 'View debug information'
cand delete 'Delete a shared file'
cand download 'Download files'
cand exists 'Check whether a remote file exists'
cand generate 'Generate assets'
cand info 'Fetch info about a shared file'
cand parameters 'Change parameters of a shared file'
cand password 'Change the password of a shared file'
cand upload 'Upload files'
cand version 'Determine the Send server version'
cand history 'View file history'
cand help 'Prints this message or the help of the given subcommand(s)'
}
&'ffsend;debug'= {
cand -h 'The remote host to upload to'
cand --host 'The remote host to upload to'
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
}
&'ffsend;delete'= {
cand -o 'Specify the file owner token'
cand --owner 'Specify the file owner token'
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand -h 'Prints help information'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
}
&'ffsend;download'= {
cand -p 'Unlock a password protected file'
cand --password 'Unlock a password protected file'
cand -o 'Output file or directory'
cand --output 'Output file or directory'
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand -e 'Extract an archived file'
cand --extract 'Extract an archived file'
cand -h 'Prints help information'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
}
&'ffsend;exists'= {
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand -h 'Prints help information'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
}
&'ffsend;generate'= {
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand -h 'Prints help information'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
cand completions 'Shell completions'
cand help 'Prints this message or the help of the given subcommand(s)'
}
&'ffsend;generate;completions'= {
cand -o 'Shell completion files output directory'
cand --output 'Shell completion files output directory'
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand -h 'Prints help information'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
}
&'ffsend;generate;help'= {
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand -h 'Prints help information'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
}
&'ffsend;info'= {
cand -o 'Specify the file owner token'
cand --owner 'Specify the file owner token'
cand -p 'Unlock a password protected file'
cand --password 'Unlock a password protected file'
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand -h 'Prints help information'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
}
&'ffsend;parameters'= {
cand -o 'Specify the file owner token'
cand --owner 'Specify the file owner token'
cand -d 'The file download limit'
cand --download-limit 'The file download limit'
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand -h 'Prints help information'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
}
&'ffsend;password'= {
cand -p 'Specify a password, do not prompt'
cand --password 'Specify a password, do not prompt'
cand -o 'Specify the file owner token'
cand --owner 'Specify the file owner token'
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand -P 'Protect the file with a generated passphrase'
cand --gen-passphrase 'Protect the file with a generated passphrase'
cand -h 'Prints help information'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
}
&'ffsend;upload'= {
cand -p 'Protect the file with a password'
cand --password 'Protect the file with a password'
cand -d 'The file download limit'
cand --download-limit 'The file download limit'
cand -e 'The file expiry time'
cand --expiry-time 'The file expiry time'
cand -h 'The remote host to upload to'
cand --host 'The remote host to upload to'
cand -n 'Rename the file being uploaded'
cand --name 'Rename the file being uploaded'
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand -P 'Protect the file with a generated passphrase'
cand --gen-passphrase 'Protect the file with a generated passphrase'
cand -o 'Open the share link in your browser'
cand --open 'Open the share link in your browser'
cand -D 'Delete local file after upload'
cand --delete 'Delete local file after upload'
cand -a 'Archive the upload in a single file'
cand --archive 'Archive the upload in a single file'
cand -c 'Copy the share link to your clipboard'
cand --copy 'Copy the share link to your clipboard'
cand -C 'Copy the ffsend download command to your clipboard'
cand --copy-cmd 'Copy the ffsend download command to your clipboard'
cand -S 'Shorten share URLs with a public service'
cand --shorten 'Shorten share URLs with a public service'
cand -Q 'Print a QR code for the share URL'
cand --qrcode 'Print a QR code for the share URL'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
}
&'ffsend;version'= {
cand -h 'The remote host to upload to'
cand --host 'The remote host to upload to'
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
}
&'ffsend;history'= {
cand -R 'Remove history entry'
cand --rm 'Remove history entry'
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand -C 'Clear all history'
cand --clear 'Clear all history'
cand -h 'Prints help information'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
}
&'ffsend;help'= {
cand -t 'Request timeout (0 to disable)'
cand --timeout 'Request timeout (0 to disable)'
cand -T 'Transfer timeout (0 to disable)'
cand --transfer-timeout 'Transfer timeout (0 to disable)'
cand -A 'Server API version to use, ''-'' to lookup'
cand --api 'Server API version to use, ''-'' to lookup'
cand --basic-auth 'Protected proxy HTTP basic authentication credentials (not FxA)'
cand -H 'Use the specified history file'
cand --history 'Use the specified history file'
cand -h 'Prints help information'
cand --help 'Prints help information'
cand -V 'Prints version information'
cand --version 'Prints version information'
cand -f 'Force the action, ignore warnings'
cand --force 'Force the action, ignore warnings'
cand -I 'Not interactive, do not prompt'
cand --no-interact 'Not interactive, do not prompt'
cand -y 'Assume yes for prompts'
cand --yes 'Assume yes for prompts'
cand -q 'Produce output suitable for logging and automation'
cand --quiet 'Produce output suitable for logging and automation'
cand -v 'Enable verbose information and logging'
cand --verbose 'Enable verbose information and logging'
cand -i 'Don''t update local history for actions'
cand --incognito 'Don''t update local history for actions'
}
]
$completions[$command]
}

238
contrib/completions/ffsend.fish vendored Normal file
View file

@ -0,0 +1,238 @@
complete -c ffsend -n "__fish_use_subcommand" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_use_subcommand" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_use_subcommand" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_use_subcommand" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_use_subcommand" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_use_subcommand" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_use_subcommand" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_use_subcommand" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_use_subcommand" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_use_subcommand" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_use_subcommand" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_use_subcommand" -s h -l help -d 'Prints help information'
complete -c ffsend -n "__fish_use_subcommand" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_use_subcommand" -f -a "debug" -d 'View debug information'
complete -c ffsend -n "__fish_use_subcommand" -f -a "delete" -d 'Delete a shared file'
complete -c ffsend -n "__fish_use_subcommand" -f -a "download" -d 'Download files'
complete -c ffsend -n "__fish_use_subcommand" -f -a "exists" -d 'Check whether a remote file exists'
complete -c ffsend -n "__fish_use_subcommand" -f -a "generate" -d 'Generate assets'
complete -c ffsend -n "__fish_use_subcommand" -f -a "info" -d 'Fetch info about a shared file'
complete -c ffsend -n "__fish_use_subcommand" -f -a "parameters" -d 'Change parameters of a shared file'
complete -c ffsend -n "__fish_use_subcommand" -f -a "password" -d 'Change the password of a shared file'
complete -c ffsend -n "__fish_use_subcommand" -f -a "upload" -d 'Upload files'
complete -c ffsend -n "__fish_use_subcommand" -f -a "version" -d 'Determine the Send server version'
complete -c ffsend -n "__fish_use_subcommand" -f -a "history" -d 'View file history'
complete -c ffsend -n "__fish_use_subcommand" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -s h -l host -d 'The remote host to upload to'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from debug" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -s o -l owner -d 'Specify the file owner token'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -s h -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from delete" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s p -l password -d 'Unlock a password protected file'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s o -l output -d 'Output file or directory'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from download" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s e -l extract -d 'Extract an archived file'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s h -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from download" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_seen_subcommand_from exists" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from exists" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from exists" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from exists" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from exists" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from exists" -s h -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from exists" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from exists" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from exists" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from exists" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from exists" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from exists" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from exists" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -s h -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -f -a "completions" -d 'Shell completions'
complete -c ffsend -n "__fish_seen_subcommand_from generate" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -s o -l output -d 'Shell completion files output directory'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -s h -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from completions" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from help" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s o -l owner -d 'Specify the file owner token'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s p -l password -d 'Unlock a password protected file'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from info" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s h -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from info" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s o -l owner -d 'Specify the file owner token'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s d -l download-limit -d 'The file download limit'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s h -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from parameters" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s p -l password -d 'Specify a password, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s o -l owner -d 'Specify the file owner token'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from password" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s P -l gen-passphrase -d 'Protect the file with a generated passphrase'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s h -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from password" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s p -l password -d 'Protect the file with a password'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s d -l download-limit -d 'The file download limit'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s e -l expiry-time -d 'The file expiry time'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s h -l host -d 'The remote host to upload to'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s n -l name -d 'Rename the file being uploaded'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s P -l gen-passphrase -d 'Protect the file with a generated passphrase'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s o -l open -d 'Open the share link in your browser'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s D -l delete -d 'Delete local file after upload'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s a -l archive -d 'Archive the upload in a single file'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s c -l copy -d 'Copy the share link to your clipboard'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s C -l copy-cmd -d 'Copy the ffsend download command to your clipboard'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s S -l shorten -d 'Shorten share URLs with a public service'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s Q -l qrcode -d 'Print a QR code for the share URL'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from upload" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_seen_subcommand_from version" -s h -l host -d 'The remote host to upload to'
complete -c ffsend -n "__fish_seen_subcommand_from version" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from version" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from version" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from version" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from version" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from version" -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from version" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from version" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from version" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from version" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from version" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from version" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from version" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s R -l rm -d 'Remove history entry'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from history" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s C -l clear -d 'Clear all history'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s h -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from history" -s i -l incognito -d 'Don\'t update local history for actions'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s t -l timeout -d 'Request timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s T -l transfer-timeout -d 'Transfer timeout (0 to disable)'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s A -l api -d 'Server API version to use, \'-\' to lookup'
complete -c ffsend -n "__fish_seen_subcommand_from help" -l basic-auth -d 'Protected proxy HTTP basic authentication credentials (not FxA)'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s H -l history -d 'Use the specified history file'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s V -l version -d 'Prints version information'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s f -l force -d 'Force the action, ignore warnings'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s I -l no-interact -d 'Not interactive, do not prompt'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s y -l yes -d 'Assume yes for prompts'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s q -l quiet -d 'Produce output suitable for logging and automation'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s v -l verbose -d 'Enable verbose information and logging'
complete -c ffsend -n "__fish_seen_subcommand_from help" -s i -l incognito -d 'Don\'t update local history for actions'

View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Stop on error
set -e
echo "Generating all completions using cargo debug binary..."
cargo run -q -- generate completions all --output "$PWD"
echo "Done."

View file

@ -0,0 +1,6 @@
`firefox-send` is a script for Nautilus/Nemo/Caja (maybe it needs some adaptation for Caja) to send files directly from the file browser, using the contextual menu.
* Copy the `firefox-send` file to ~/.local/share/nautilus/scripts/firefox-send
* Modify the default options to your use case: host server, download number, retention time.
* Make the file executable (`chmod +x firefox-send`).
* Restart Nautilus/Nemo/Caja.

View file

@ -0,0 +1,46 @@
#!/bin/bash
#CONSTANTS
#FILEPATH=`echo $NAUTILUS_SCRIPT_SELECTED_URIS | sed 's@file://@@g'`
# Quote the paths
IFS=$'\n' read -d '' -r -a FILEPATH <<< "$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS"
FFSEND_BIN='/usr/bin/ffsend'
FFSEND_BIN_OPTS="upload --open --copy"
ZENITY='/usr/bin/zenity '
ZENITY_PROGRESS_OPTIONS='--auto-close --auto-kill' #you can remove this if you like
#sanity checks
for sanity_check in $FFSEND_BIN "${FILEPATH[@]}"
do
ZENITY_ERROR_SANITY="There is an error, it involved $sanity_check.\n Probably binary or file missing"
if [ ! -e $sanity_check ]
then
#zenity --error --text="$(eval "echo \"$ZENITY_ERROR_SANITY\"")"
zenity --error --text="$ZENITY_ERROR_SANITY"
exit
fi
done
# Use the following flags automatically from now on
# -I: no interaction
# -f: force
# -y: yes
# -q: quiet
export FFSEND_NO_INTERACT=1 FFSEND_FORCE=1 FFSEND_YES=1 FFSEND_QUIET=1
export FFSEND_HOST=https://send.boblorange.net
export FFSEND_EXPIRY_TIME=604800
export FFSEND_DOWNLOAD_LIMIT=5
#check whether copying file or directory
if [ ! -f "${FILEPATH[@]}" ]; then
FFSEND_BIN_OPTS="$FFSEND_BIN_OPTS --archive"
fi
# Upload a file
#zenity --info --text="Ready to send: $FFSEND_BIN $FFSEND_BIN_OPTS ${FILEPATH[@]}"
$FFSEND_BIN $FFSEND_BIN_OPTS "${FILEPATH[@]}" | $($ZENITY --progress --text="sending $(basename $FILEPATH)" --pulsate $ZENITY_PROGRESS_OPTIONS)
#echo -e "$FILEPATH" | xargs -i $FFSEND_BIN $FFSEND_BIN_OPTS {} | $($ZENITY --progress --text="sending $(basename $FILEPATH)" --pulsate $ZENITY_PROGRESS_OPTIONS)
# Upload a file
#echo -e "$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS" | xargs -i ffsend upload --open --copy {}

65
pkg/alpine/APKBUILD Normal file
View file

@ -0,0 +1,65 @@
# Contributor: Rasmus Thomsen <oss@cogitri.dev>
# Maintainer: Rasmus Thomsen <oss@cogitri.dev>
pkgname=ffsend
pkgver=0.2.62
pkgrel=0
pkgdesc=" A fully featured Send client"
url="https://gitlab.com/timvisee/ffsend"
arch="x86_64 x86 armhf armv7 aarch64 ppc64le" # limited by cargo
license="GPL-3.0-only"
makedepends="cargo openssl-dev"
subpackages="
$pkgname-zsh-completion:zshcomp:noarch
$pkgname-fish-completion:fishcomp:noarch
$pkgname-bash-completion:bashcomp:noarch
"
source="https://gitlab.com/timvisee/ffsend/-/archive/v$pkgver/ffsend-v$pkgver.tar.gz"
builddir="$srcdir/$pkgname-v$pkgver"
case "$CARCH" in
x86)
export CFLAGS="$CFLAGS -fno-stack-protector"
;;
esac
build() {
cargo build --release
}
check() {
cargo test --release
}
package() {
cargo install --path . --root="$pkgdir/usr"
rm "$pkgdir"/usr/.crates.toml
}
bashcomp() {
depends=""
pkgdesc="Bash completions for $pkgname"
install_if="$pkgname=$pkgver-r$pkgrel bash-completion"
install -Dm644 "$builddir"/contrib/completions/ffsend.bash \
"$subpkgdir"/usr/share/bash-completion/completions/ffsend
}
zshcomp() {
depends=""
pkgdesc="Zsh compltions for $pkgname"
install_if="$pkgname=$pkgver-r$pkgrel zsh"
install -Dm644 "$builddir"/contrib/completions/_ffsend \
"$subpkgdir"/usr/share/zsh/site-functions/_ffsend
}
fishcomp() {
depends=""
pkgdesc="Fish completions for $pkgname"
install_if="$pkgname=$pkgver-r$pkgrel fish"
install -Dm644 "$builddir"/contrib/completions/ffsend.fish \
"$subpkgdir"/usr/share/fish/completions/ffsend.fish
}
sha512sums="97477f59a498f4805340647c50c139d70ddbabaebb49b4acbae595ed9987fa2941b8d4dfd2edf76264b2a500fa10ac10f8583938fb29013936d9c092fe7fe7f1 ffsend-v0.2.51.tar.gz"

1
pkg/aur/aur.pub Normal file
View file

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHfiNi+rOCPKGLB6v9uuYR6GkN6Zd+CdaRbV82A26AUzs48ZG0xZGXHsoRuZY/yCUhcrS2u9xZ16fsAxnyf1QCF1hZVABUYtNhL1eEbSbLNiG9vIWJzbRjgegN/yyiG9ZVhFfNqXtPeapvuM3H44a2XeeFJcvTOfj/alkVjypi/DY/+XpC1IlX+CARC/e0zXHa3KZohn+CfBj8kWZWQEr7+EtKT59pslNxuJPcDUw7sKYLcBBz00BT0vv3lntyvZI1rRsD7AvItOwSZPp6or78Tgp8+O0HvFpjrlNipPEqDPpETIPcjTIVAlvlPFK1J0Rpzud38YdoWGfPiM77k7L7 timvisee@aur

View file

@ -0,0 +1,37 @@
# Maintainer: Tim Visee <tim@visee.me>
# Contributor: Ariel AxionL <i at axionl dot me>
#
# This PKGBUILD is managed externally, and is automatically updated here:
# - https://gitlab.com/timvisee/ffsend/blob/master/pkg/aur/ffsend-bin/PKGBUILD
# - Mirror: https://github.com/timvisee/ffsend/blob/master/pkg/aur/ffsend-bin/PKGBUILD
pkgname=ffsend-bin
pkgver=0.0.0 # automatically set in CI, see: /.gitlab-ci.yml
pkgrel=1
pkgdesc="Easily and securely share files from the command line. A Send client."
url="https://gitlab.com/timvisee/ffsend"
license=('GPL3')
source=("ffsend-v$pkgver::https://github.com/timvisee/ffsend/releases/download/v$pkgver/ffsend-v$pkgver-linux-x64-static"
"ffsend-v$pkgver.bash::https://raw.githubusercontent.com/timvisee/ffsend/v$pkgver/contrib/completions/ffsend.bash"
"ffsend-v$pkgver.zsh::https://raw.githubusercontent.com/timvisee/ffsend/v$pkgver/contrib/completions/_ffsend"
"ffsend-v$pkgver.fish::https://raw.githubusercontent.com/timvisee/ffsend/v$pkgver/contrib/completions/ffsend.fish"
"LICENSE-v$pkgver::https://raw.githubusercontent.com/timvisee/ffsend/v$pkgver/LICENSE") # automatically set in CI, see: /.gitlab-ci.yml
sha256sums=('SKIP')
arch=('x86_64')
provides=('ffsend')
conflicts=('ffsend')
depends=('ca-certificates')
optdepends=('xclip: clipboard support'
'bash-completion: support auto completion for bash')
package() {
cd "$srcdir"
install -Dm755 "ffsend-v$pkgver" "$pkgdir/usr/bin/ffsend"
# Shell completions and LICENSE file
install -Dm644 "ffsend-v$pkgver.bash" "$pkgdir/usr/share/bash-completion/completions/ffsend"
install -Dm644 "ffsend-v$pkgver.zsh" "$pkgdir/usr/share/zsh/site-functions/_ffsend"
install -Dm644 "ffsend-v$pkgver.fish" "$pkgdir/usr/share/fish/vendor_completions.d/ffsend.fish"
install -Dm644 "LICENSE-v$pkgver" "$pkgdir/usr/share/licenses/ffsend/LICENSE"
}

View file

@ -0,0 +1,57 @@
# Maintainer: Tim Visee <tim@visee.me>
#
# This PKGBUILD is managed externally, and is automatically updated here:
# - https://gitlab.com/timvisee/ffsend/blob/master/pkg/aur/ffsend-git/PKGBUILD
# - Mirror: https://github.com/timvisee/ffsend/blob/master/pkg/aur/ffsend-git/PKGBUILD
pkgname=ffsend-git
pkgver=0.0.0 # automatically set in CI, see: /.gitlab-ci.yml
pkgrel=1
pkgdesc="Easily and securely share files from the command line. A Send client."
url="https://gitlab.com/timvisee/ffsend"
license=('GPL3')
source=("git+${url}")
sha256sums=('SKIP')
arch=('x86_64' 'i686')
provides=('ffsend')
conflicts=('ffsend')
depends=('ca-certificates')
makedepends=('cargo' 'cmake' 'openssl>=1.0')
optdepends=('xclip: clipboard support')
prepare() {
cd "${pkgname%-git}"
cargo fetch --locked --target "$CARCH-unknown-linux-gnu"
}
build() {
cd "${pkgname%-git}"
export RUSTUP_TOOLCHAIN=stable
export CARGO_TARGET_DIR=target
cargo build --frozen --release
}
check() {
cd "${pkgname%-git}"
export RUSTUP_TOOLCHAIN=stable
cargo test --frozen
}
package() {
cd "${pkgname%-git}"
install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/ffsend"
# Shell completions and LICENSE file
install -Dm644 "contrib/completions/ffsend.bash" \
"$pkgdir/etc/bash_completion.d/ffsend"
install -Dm644 "contrib/completions/_ffsend" \
"$pkgdir/usr/share/zsh/site-functions/_ffsend"
install -Dm644 "contrib/completions/ffsend.fish" \
"$pkgdir/usr/share/fish/vendor_completions.d/ffsend.fish"
install -Dm644 "LICENSE" \
"$pkgdir/usr/share/licenses/ffsend/LICENSE"
}

55
pkg/aur/ffsend/PKGBUILD Normal file
View file

@ -0,0 +1,55 @@
# Maintainer: Tim Visee <tim@visee.me>
#
# This PKGBUILD is managed externally, and is automatically updated here:
# - https://gitlab.com/timvisee/ffsend/blob/master/pkg/aur/ffsend/PKGBUILD
# - Mirror: https://github.com/timvisee/ffsend/blob/master/pkg/aur/ffsend/PKGBUILD
pkgname=ffsend
pkgver=0.0.0 # automatically set in CI, see: /.gitlab-ci.yml
pkgrel=1
pkgdesc="Easily and securely share files from the command line. A Send client."
url="https://gitlab.com/timvisee/ffsend"
license=('GPL3')
source=("$url/-/archive/v$pkgver/ffsend-v$pkgver.tar.gz") # automatically set in CI, see: /.gitlab-ci.yml
sha256sums=('SKIP') # automatically set in CI, see: /.gitlab-ci.yml
arch=('x86_64' 'i686')
depends=('ca-certificates')
makedepends=('cargo' 'cmake' 'openssl>=1.0')
optdepends=('xclip: clipboard support')
prepare() {
cd "$pkgname-v$pkgver"
cargo fetch --locked --target "$CARCH-unknown-linux-gnu"
}
build() {
cd "$pkgname-v$pkgver"
export RUSTUP_TOOLCHAIN=stable
export CARGO_TARGET_DIR=target
cargo build --frozen --release
}
check() {
cd "$pkgname-v$pkgver"
export RUSTUP_TOOLCHAIN=stable
cargo test --frozen
}
package() {
cd "$pkgname-v$pkgver"
install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/$pkgname"
# Shell completions and LICENSE file
install -Dm644 "contrib/completions/ffsend.bash" \
"$pkgdir/etc/bash_completion.d/ffsend"
install -Dm644 "contrib/completions/_ffsend" \
"$pkgdir/usr/share/zsh/site-functions/_ffsend"
install -Dm644 "contrib/completions/ffsend.fish" \
"$pkgdir/usr/share/fish/vendor_completions.d/ffsend.fish"
install -Dm644 "LICENSE" \
"$pkgdir/usr/share/licenses/ffsend/LICENSE"
}

View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Read this before creating packages: https://chocolatey.org/docs/create-packages -->
<!-- It is especially important to read the above link to understand additional requirements when publishing packages to the community feed aka dot org (https://chocolatey.org/packages). -->
<!-- Test your packages in a test environment: https://github.com/chocolatey/chocolatey-test-environment -->
<!--
This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Reference. Chocolatey uses a special version of NuGet.Core that allows us to do more than was initially possible. As such there are certain things to be aware of:
* the package xmlns schema url may cause issues with nuget.exe
* Any of the following elements can ONLY be used by choco tools - projectSourceUrl, docsUrl, mailingListUrl, bugTrackerUrl, packageSourceUrl, provides, conflicts, replaces
* nuget.exe can still install packages with those elements but they are ignored. Any authoring tools or commands will error on those elements
-->
<!-- You can embed software files directly into packages, as long as you are not bound by distribution rights. -->
<!-- * If you are an organization making private packages, you probably have no issues here -->
<!-- * If you are releasing to the community feed, you need to consider distribution rights. -->
<!-- Do not remove this test for UTF-8: if “Ω” doesnt appear as greek uppercase omega letter enclosed in quotation marks, you should use an editor that supports UTF-8, not this one. -->
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
<metadata>
<!-- == PACKAGE SPECIFIC SECTION == -->
<!-- This section is about this package, although id and version have ties back to the software -->
<!-- id is lowercase and if you want a good separator for words, use '-', not '.'. Dots are only acceptable as suffixes for certain types of packages, e.g. .install, .portable, .extension, .template -->
<!-- If the software is cross-platform, attempt to use the same id as the debian/rpm package(s) if possible. -->
<id>ffsend</id>
<!-- version should MATCH as closely as possible with the underlying software -->
<!-- Is the version a prerelease of a version? https://docs.nuget.org/create/versioning#creating-prerelease-packages -->
<!-- Note that unstable versions like 0.0.1 can be considered a released version, but it's possible that one can release a 0.0.1-beta before you release a 0.0.1 version. If the version number is final, that is considered a released version and not a prerelease. -->
<version>0.0.0</version>
<packageSourceUrl>https://github.com/timvisee/ffsend/tree/master/pkg/choco/ffsend</packageSourceUrl>
<!-- owners is a poor name for maintainers of the package. It sticks around by this name for compatibility reasons. It basically means you. -->
<owners>Tim Visee</owners>
<!-- ============================== -->
<!-- == SOFTWARE SPECIFIC SECTION == -->
<!-- This section is about the software itself -->
<title>ffsend (Install)</title>
<authors>Tim Visee</authors>
<projectUrl>https://github.com/timvisee/ffsend</projectUrl>
<iconUrl>http://cdn.rawgit.com/timvisee/ffsend/master/res/ffsend.png</iconUrl>
<copyright>2017-2019 Tim Visee</copyright>
<licenseUrl>https://raw.githubusercontent.com/timvisee/ffsend/master/LICENSE</licenseUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<projectSourceUrl>https://github.com/timvisee/ffsend</projectSourceUrl>
<docsUrl>https://github.com/timvisee/ffsend</docsUrl>
<bugTrackerUrl>https://gitlab.com/timvisee/ffsend/issues</bugTrackerUrl>
<tags>ffsend firefox-send cli file-sharing file-upload encryption rust</tags>
<summary>Easily and securely share files from the command line. A fully featured Send client.</summary>
<description>Easily and securely share files from the command line. A fully featured Send client.</description>
<!-- =============================== -->
<!-- Specifying dependencies and version ranges? https://docs.nuget.org/create/versioning#specifying-version-ranges-in-.nuspec-files -->
<dependencies></dependencies>
</metadata>
<files>
<!-- this section controls what actually gets packaged into the Chocolatey package -->
<file src="tools\**" target="tools" />
<!-- Building from Linux? You may need this instead: <file src="tools/**" target="tools" /> -->
</files>
</package>

View file

@ -0,0 +1,678 @@
From: https://www.gnu.org/licenses/gpl-3.0.en.html
LICENSE
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2018 Tim Visee. <https://timvisee.com/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View file

@ -0,0 +1,5 @@
VERIFICATION
Verification is intended to assist the Chocolatey moderators and community
in verifying that this package's contents are trustworthy.
Binaries can be compared to versions available at: https://github.com/timvisee/ffsend/releases

View file

@ -9,13 +9,13 @@ if [[ ! $TRAVIS_TAG =~ ^v([0-9]+\.)*[0-9]+$ ]]; then
fi
# Ensure the debian architecture is set
if [[ -z "$DEB_ARCH" ]]; then
if [[ -z $DEB_ARCH ]]; then
echo "Error: debian architecture not configured in \$DEB_ARCH"
exit 1
fi
# Define some useful variables
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
VERSION=${TRAVIS_TAG:1}
# Ensure the binary file exists
@ -25,23 +25,23 @@ if [[ ! -f "$DIR/../ffsend" ]]; then
fi
# Create an application directory, copy the binary into it
mkdir -p $DIR/ffsend-$VERSION
cp $DIR/../ffsend $DIR/ffsend-$VERSION/ffsend
mkdir -p "$DIR/ffsend-$VERSION"
cp -- "$DIR/../ffsend" "$DIR/ffsend-$VERSION/ffsend"
# Create an application tarbal
cd $DIR/..
git archive --format tar.gz -o $DIR/ffsend-$VERSION/ffsend-$VERSION.tar.gz $TRAVIS_TAG
# Create an application tarball
cd -- "$DIR/.."
git archive --format tar.gz -o "$DIR/ffsend-$VERSION/ffsend-$VERSION.tar.gz" "$TRAVIS_TAG"
# Change into the app directory
cd $DIR/ffsend-$VERSION
cd -- "$DIR/ffsend-$VERSION"
# Build the debian package
# TODO: define GPG?
dh_make -e "timvisee@gmail.com" -c gpl3 -f ffsend-$VERSION.tar.gz -s -y
rm *.ex README.Debian README.source
dh_make -e "timvisee@gmail.com" -c gpl3 -f "ffsend-$VERSION.tar.gz" -s -y
rm -- *.ex README.Debian README.source
# Remove the project tar ball, we're not using it anymore
rm $DIR/ffsend-$VERSION/ffsend-$VERSION.tar.gz
rm -- "$DIR/ffsend-$VERSION/ffsend-$VERSION.tar.gz"
# TODO: configure the debian/control file
# TODO: configure copyright file

View file

@ -2,7 +2,7 @@
# Unlink the ffs alias if it links to ffsend
if [[ -L /usr/bin/ffs ]] \
&& [[ $(ls -l /usr/bin/ffs | sed -e 's/.* -> //') == "/usr/bin/ffsend" ]]; \
&& [[ $(realpath /usr/bin/ffs) == "/usr/bin/ffsend" ]]; \
then
echo "Removing ffs alias for ffsend..."
unlink /usr/bin/ffs

7
pkg/docker/Dockerfile Normal file
View file

@ -0,0 +1,7 @@
FROM alpine:latest
LABEL maintainer="Tim Visée <3a4fb3964f@sinenomine.email>"
COPY ./ffsend /
WORKDIR /data/
ENTRYPOINT ["/ffsend"]

8
pkg/scoop/README.md Normal file
View file

@ -0,0 +1,8 @@
# scoop manifest for ffsend
This directory contains a [`scoop`][scoop] manifest template for `ffsend`.
The currently published manifest can be found in the `scoop` repository,
and is automatically updated:
https://github.com/ScoopInstaller/Main/blob/master/bucket/ffsend.json
[scoop]: https://scoop.rs/

21
pkg/scoop/ffsend.json Normal file
View file

@ -0,0 +1,21 @@
{
"homepage": "https://github.com/timvisee/ffsend",
"description": "Easily and securely share files from the command line. A fully featured Send client.",
"license": "GPL-3.0-only",
"version": "0.0.0",
"architecture": {
"64bit": {
"url": "https://github.com/timvisee/ffsend/releases/download/v0.2.30/ffsend-v0.2.30-windows-x64-static.exe#/ffsend.exe",
"hash": "297f1405bacdc34948cd94ba785336c48e1ac862984268d66a8928abd3e322e1"
}
},
"bin": "ffsend.exe",
"checkver": "github",
"autoupdate": {
"architecture": {
"64bit": {
"url": "https://github.com/timvisee/ffsend/releases/download/v$version/ffsend-v$version-windows-x64-static.exe#/ffsend.exe"
}
}
}
}

4
pkg/snap/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
parts/
prime/
snap/
stage/

35
pkg/snap/snapcraft.yaml Normal file
View file

@ -0,0 +1,35 @@
name: ffsend
version: git
summary: Easily and securely share files from the command line.
description: |
Easily and securely share files and directories from the command line through
a safe, private and encrypted link using a single simple command.
Files are shared using the Send service and may be up to 2GB.
Others are able to download these files with this tool
or through their web browser.
All files are always encrypted on the client,
and secrets are never shared with the remote host.
An optional password may be specified, and a default file lifetime of 1
(up to 20) download or 24 hours is enforced to ensure your stuff does not
remain online forever. This provides a secure platform to share your files.
# license: GPL-3.0
architectures:
- amd64
grade: stable
confinement: strict
apps:
ffsend:
command: ffsend
plugs: [desktop, home, network, removable-media, x11]
parts:
ffsend:
source: ../../
plugin: rust
build-attributes: [no-system-libraries]
build-packages: [make, cmake, pkg-config, libssl-dev]
stage-packages: [libssl1.0.0, xclip]

View file

@ -4,7 +4,7 @@
"height": 19,
"duration": 78.431135,
"command": null,
"title": "ffsend v0.0.1 demo",
"title": "ffsend v0.0.9 demo",
"env": {
"TERM": "xterm-256color",
"SHELL": "/bin/bash"
@ -44,7 +44,7 @@
],
[
0.004048,
"ffsend 0.0.1\r\nUsage: ffsend [FLAGS] <SUBCOMMAND> ...\r\n\r\nSecurely and easily share files from the command line.\r\nA fully featured Firefox Send client.\r\n\r\nMissing subcommand. Here are the most used:\r\n"
"ffsend 0.0.7\r\nUsage: ffsend [FLAGS] <SUBCOMMAND> ...\r\n\r\nSecurely and easily share files from the command line.\r\nA fully featured Firefox Send client.\r\n\r\nMissing subcommand. Here are the most used:\r\n"
],
[
0.000148,

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# Ensure svg-term is installed
if ! [ -x "$(command -v svg-term)" ]; then

BIN
res/ffsend.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

1
snapcraft.login Normal file
View file

@ -0,0 +1 @@
eyJyIjogIk1EQXlPV3h2WTJGMGFXOXVJRzE1WVhCd2N5NWtaWFpsYkc5d1pYSXVkV0oxYm5SMUxtTnZiUW93TURFMmFXUmxiblJwWm1sbGNpQk5lVUZ3Y0hNS01EQTBZbU5wWkNCdGVXRndjSE11WkdWMlpXeHZjR1Z5TG5WaWRXNTBkUzVqYjIxOGRtRnNhV1JmYzJsdVkyVjhNakF5TWkwd05pMHlNRlF3T0Rvd016b3hNaTQwTlRreU9EUUtNREUzWkdOcFpDQjdJblpsY25OcGIyNGlPaUF4TENBaWMyVmpjbVYwSWpvZ0ltOWFVRzlpVG1vdlJYTmhkazlYUVZoRmNIQlRNM1JNY0RGak1tdFZaVU5NYzFaWlNUQmFaa1JFZERkcVozaHhTVlI2VEhselFtOVZjV3N5VURad1JXdFFRblpNUkhOMFkwTTNSRlU1VUVFMGFXVlpPV0U0WWxGU2VtNHJNbTQxVEVsRWMwY3ZTVEphVEM4eU1uSTBjakZDYmlzM1VqRnhUVWt2WjNoR1kzVnVVV05pUlZkeVVWQXdZV3BrUTBrd1pWRmxOVEZsVmpWSVJrbzNjMlZGU0c5WVpFRlZTVVZ1Vm1GNU5sRnFVMDQzTW1SSFpWSlVabVZvYVVacmFWbFFaVVJJZVN0d1VqbHVjMXBNZW5aMFNYbHNPRnBYYVhkSVdIcFpRVTl2T0Rob1VXdFhZakZ6UzNGSGFYWjZWRFZEWlZoV1IyaElaalJSUVdRNVIwOWxLMjUxUmpWM2JHMWtaRlIwY1U1YWNWQnNjVkZHYjBoelprUk9jRFpvVFd4dWFHbDVMMGxvY2tOclZscENUVzFMYld0cFIwVk1VWFZMTUVSUVFtdFFNSE5RVWpaVVdsWnFjVGQzZVRaaFdIa3dVM1ZyZDBaclp6MDlJbjBLTURBMU1YWnBaQ0ExYU5mbjFKU3NBa1VoMFM1NXB4bnp2VUxESlRtcldMLTZSa3B0c3kzbFdVSG02T0hVQnJHeGtfYmlnbnEtbkxIMm9hYVdwaENKRkM1YzNod1BqWjBXUl83bUZ5QWN2QlFLTURBeE9HTnNJR3h2WjJsdUxuVmlkVzUwZFM1amIyMEtNREJoT1dOcFpDQnRlV0Z3Y0hNdVpHVjJaV3h2Y0dWeUxuVmlkVzUwZFM1amIyMThZV05zZkZzaWNHRmphMkZuWlY5aFkyTmxjM01pTENBaWNHRmphMkZuWlY5dFlXNWhaMlVpTENBaWNHRmphMkZuWlY5dFpYUnlhV056SWl3Z0luQmhZMnRoWjJWZmNIVnphQ0lzSUNKd1lXTnJZV2RsWDNKbFoybHpkR1Z5SWl3Z0luQmhZMnRoWjJWZmNtVnNaV0Z6WlNJc0lDSndZV05yWVdkbFgzVndaR0YwWlNKZENqQXdORGRqYVdRZ2JYbGhjSEJ6TG1SbGRtVnNiM0JsY2k1MVluVnVkSFV1WTI5dGZHVjRjR2x5WlhOOE1qQXlNeTB3TmkweU1GUXdPRG93TXpveE1pNHdNREF3TVRjS01EQXlabk5wWjI1aGRIVnlaU0JOYm05MFAyTkpYV3RON0I1aktWbkVBQUZqM0c3dzItLWNhOGloR2lJWUhRbyIsICJkIjogIk1EQXhaV3h2WTJGMGFXOXVJR3h2WjJsdUxuVmlkVzUwZFM1amIyMEtNREU0Tkdsa1pXNTBhV1pwWlhJZ2V5SjJaWEp6YVc5dUlqb2dNU3dnSW5ObFkzSmxkQ0k2SUNKdldsQnZZazVxTDBWellYWlBWMEZZUlhCd1V6TjBUSEF4WXpKclZXVkRUSE5XV1Vrd1dtWkVSSFEzYW1kNGNVbFVla3g1YzBKdlZYRnJNbEEyY0VWclVFSjJURVJ6ZEdORE4wUlZPVkJCTkdsbFdUbGhPR0pSVW5wdUt6SnVOVXhKUkhOSEwwa3lXa3d2TWpKeU5ISXhRbTRyTjFJeGNVMUpMMmQ0Um1OMWJsRmpZa1ZYY2xGUU1HRnFaRU5KTUdWUlpUVXhaVlkxU0VaS04zTmxSVWh2V0dSQlZVbEZibFpoZVRaUmFsTk9OekprUjJWU1ZHWmxhR2xHYTJsWlVHVkVTSGtyY0ZJNWJuTmFUSHAyZEVsNWJEaGFWMmwzU0ZoNldVRlBiemc0YUZGclYySXhjMHR4UjJsMmVsUTFRMlZZVmtkb1NHWTBVVUZrT1VkUFpTdHVkVVkxZDJ4dFpHUlVkSEZPV25GUWJIRlJSbTlJYzJaRVRuQTJhRTFzYm1ocGVTOUphSEpEYTFaYVFrMXRTMjFyYVVkRlRGRjFTekJFVUVKclVEQnpVRkkyVkZwV2FuRTNkM2syWVZoNU1GTjFhM2RHYTJjOVBTSjlDakF3WkRaamFXUWdiRzluYVc0dWRXSjFiblIxTG1OdmJYeGhZMk52ZFc1MGZHVjVTakZqTWxaNVltMUdkRnBUU1RaSlEwb3dZVmN4TW1GWVRteGFVMGx6U1VOS2RtTkhWblZoVjFGcFQybEJhVlZ1WkV4UlZXaDBVME5KYzBsRFNtdGhXRTUzWWtkR05XSnRSblJhVTBrMlNVTktWV0ZYTUdkV2JXeDZXRWhWZDAxSFZUVmFVMGx6U1VOS2JHSlhSbkJpUTBrMlNVTktNR0ZYTUhKa1Ywb3hZbTVTTVdJeU5XeFJTRnB3WXpKV2JFeHRNV3hKYVhkblNXMXNlbGd6V214amJXeHRZVmRXYTBscWIyZGtTRW94V2xnd1BRb3dNRFF3WTJsa0lHeHZaMmx1TG5WaWRXNTBkUzVqYjIxOGRtRnNhV1JmYzJsdVkyVjhNakF5TWkwd05pMHlNRlF3T0Rvd016b3hNeTR5T0RJd09UTUtNREF6WldOcFpDQnNiMmRwYmk1MVluVnVkSFV1WTI5dGZHeGhjM1JmWVhWMGFId3lNREl5TFRBMkxUSXdWREE0T2pBek9qRXpMakk0TWpBNU13b3dNREptYzJsbmJtRjBkWEpsSVBtdzV2aGZjWXhjRVFzX1RHV2VHWUFrQWhxdkJONVJjOFpHTEpnVFZ2eGxDZyJ9

View file

@ -1,20 +1,14 @@
use chrono::Duration;
use clap::ArgMatches;
use ffsend_api::config::SEND_DEFAULT_EXPIRE_TIME;
use prettytable::{
cell::Cell,
format::FormatBuilder,
row::Row,
Table,
};
use prettytable::{format::FormatBuilder, Cell, Row, Table};
use cmd::matcher::{
debug::DebugMatcher,
main::MainMatcher,
Matcher,
};
use error::ActionError;
use util::{features_list, format_bool, format_duration};
use crate::client::to_duration;
use crate::cmd::matcher::{debug::DebugMatcher, main::MainMatcher, Matcher};
use crate::error::ActionError;
#[cfg(feature = "clipboard-bin")]
use crate::util::ClipboardType;
use crate::util::{api_version_list, features_list, format_bool, format_duration};
/// A file debug action.
pub struct Debug<'a> {
@ -24,9 +18,7 @@ pub struct Debug<'a> {
impl<'a> Debug<'a> {
/// Construct a new debug action.
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
Self {
cmd_matches,
}
Self { cmd_matches }
}
/// Invoke the debug action.
@ -40,6 +32,12 @@ impl<'a> Debug<'a> {
let mut table = Table::new();
table.set_format(FormatBuilder::new().padding(0, 2).build());
// The crate version
table.add_row(Row::new(vec![
Cell::new("Version:"),
Cell::new(crate_version!()),
]));
// The default host
table.add_row(Row::new(vec![
Cell::new("Host:"),
@ -53,10 +51,39 @@ impl<'a> Debug<'a> {
Cell::new(matcher_main.history().to_str().unwrap_or("?")),
]));
// The timeouts
table.add_row(Row::new(vec![
Cell::new("Timeout:"),
Cell::new(
&to_duration(matcher_main.timeout())
.map(|t| {
format_duration(
Duration::from_std(t).expect("failed to convert timeout duration"),
)
})
.unwrap_or("disabled".into()),
),
]));
table.add_row(Row::new(vec![
Cell::new("Transfer timeout:"),
Cell::new(
&to_duration(matcher_main.transfer_timeout())
.map(|t| {
format_duration(
Duration::from_std(t)
.expect("failed to convert transfer timeout duration"),
)
})
.unwrap_or("disabled".into()),
),
]));
// The default host
table.add_row(Row::new(vec![
Cell::new("Default expiry:"),
Cell::new(&format_duration(Duration::seconds(SEND_DEFAULT_EXPIRE_TIME))),
Cell::new(&format_duration(Duration::seconds(
SEND_DEFAULT_EXPIRE_TIME as i64,
))),
]));
// Render a list of compiled features
@ -65,6 +92,34 @@ impl<'a> Debug<'a> {
Cell::new(&features_list().join(", ")),
]));
// Render a list of compiled features
table.add_row(Row::new(vec![
Cell::new("API support:"),
Cell::new(&api_version_list().join(", ")),
]));
// Show used crypto backend
table.add_row(Row::new(vec![
Cell::new("Crypto backend:"),
#[cfg(feature = "crypto-ring")]
Cell::new("ring"),
#[cfg(feature = "crypto-openssl")]
Cell::new("OpenSSL"),
]));
// Clipboard information
#[cfg(feature = "clipboard-bin")]
table.add_row(Row::new(vec![
Cell::new("Clipboard:"),
Cell::new(&format!("{}", ClipboardType::select())),
]));
// Show whether quiet is used
table.add_row(Row::new(vec![
Cell::new("Quiet:"),
Cell::new(format_bool(matcher_main.quiet())),
]));
// Show whether verbose is used
table.add_row(Row::new(vec![
Cell::new("Verbose:"),

View file

@ -1,23 +1,13 @@
use clap::ArgMatches;
use ffsend_api::action::delete::{
Error as DeleteError,
Delete as ApiDelete,
};
use ffsend_api::file::remote_file::{
FileParseError,
RemoteFile,
};
use ffsend_api::reqwest::Client;
use ffsend_api::action::delete::{Delete as ApiDelete, Error as DeleteError};
use ffsend_api::file::remote_file::{FileParseError, RemoteFile};
use cmd::matcher::{
Matcher,
delete::DeleteMatcher,
main::MainMatcher,
};
use error::ActionError;
use crate::client::create_config;
use crate::cmd::matcher::{delete::DeleteMatcher, main::MainMatcher, Matcher};
use crate::error::ActionError;
#[cfg(feature = "history")]
use history_tool;
use util::{ensure_owner_token, print_success};
use crate::history_tool;
use crate::util::{ensure_owner_token, print_success};
/// A file delete action.
pub struct Delete<'a> {
@ -27,9 +17,7 @@ pub struct Delete<'a> {
impl<'a> Delete<'a> {
/// Construct a new delete action.
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
Self {
cmd_matches,
}
Self { cmd_matches }
}
/// Invoke the delete action.
@ -42,8 +30,9 @@ impl<'a> Delete<'a> {
// Get the share link
let url = matcher_delete.url();
// Create a reqwest client
let client = Client::new();
// Create client
let client_config = create_config(&matcher_main);
let client = client_config.client(false);
// Parse the remote file based on the share link, derive the owner token from history
let mut file = RemoteFile::parse_url(url, matcher_delete.owner())?;
@ -51,7 +40,7 @@ impl<'a> Delete<'a> {
history_tool::derive_file_properties(&matcher_main, &mut file);
// Ensure the owner token is set
ensure_owner_token(file.owner_token_mut(), &matcher_main);
ensure_owner_token(file.owner_token_mut(), &matcher_main, false);
// Send the file deletion request
let result = ApiDelete::new(&file, None).invoke(&client);

View file

@ -7,45 +7,26 @@ use std::sync::{Arc, Mutex};
use clap::ArgMatches;
use failure::Fail;
use ffsend_api::action::download::{
Download as ApiDownload,
Error as DownloadError,
};
use ffsend_api::action::exists::{
Error as ExistsError,
Exists as ApiExists,
};
use ffsend_api::action::metadata::{
Error as MetadataError,
Metadata as ApiMetadata,
};
use ffsend_api::action::download::{Download as ApiDownload, Error as DownloadError};
use ffsend_api::action::exists::{Error as ExistsError, Exists as ApiExists};
use ffsend_api::action::metadata::{Error as MetadataError, Metadata as ApiMetadata};
use ffsend_api::action::version::Error as VersionError;
use ffsend_api::file::remote_file::{FileParseError, RemoteFile};
use ffsend_api::reader::ProgressReporter;
use ffsend_api::reqwest::Client;
use ffsend_api::pipe::ProgressReporter;
#[cfg(feature = "archive")]
use tempfile::{
Builder as TempBuilder,
NamedTempFile,
};
use tempfile::{Builder as TempBuilder, NamedTempFile};
use super::select_api_version;
#[cfg(feature = "archive")]
use archive::archive::Archive;
use cmd::matcher::{
Matcher,
download::DownloadMatcher,
main::MainMatcher,
};
use crate::archive::archive::Archive;
use crate::client::create_config;
use crate::cmd::matcher::{download::DownloadMatcher, main::MainMatcher, Matcher};
#[cfg(feature = "history")]
use history_tool;
use progress::ProgressBar;
use util::{
ensure_enough_space,
ensure_password,
ErrorHints,
prompt_yes,
quit,
quit_error,
quit_error_msg,
use crate::history_tool;
use crate::progress::ProgressBar;
use crate::util::{
ensure_enough_space, ensure_password, follow_url, print_error, prompt_yes, quit, quit_error,
quit_error_msg, ErrorHints,
};
/// A file download action.
@ -56,9 +37,7 @@ pub struct Download<'a> {
impl<'a> Download<'a> {
/// Construct a new download action.
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
Self {
cmd_matches,
}
Self { cmd_matches }
}
/// Invoke the download action.
@ -68,11 +47,27 @@ impl<'a> Download<'a> {
let matcher_main = MainMatcher::with(self.cmd_matches).unwrap();
let matcher_download = DownloadMatcher::with(self.cmd_matches).unwrap();
// Get the share URL
let url = matcher_download.url();
// Create a regular client
let client_config = create_config(&matcher_main);
let client = client_config.clone().client(false);
// Create a reqwest client
let client = Client::new();
// Get the share URL, attempt to follow it
let url = matcher_download.url();
let url = match follow_url(&client, &url) {
Ok(url) => url,
Err(err) => {
print_error(err.context("failed to follow share URL, ignoring").compat());
url
}
};
// Guess the host
let host = matcher_download.guess_host(Some(url.clone()));
// Determine the API version to use
let mut desired_version = matcher_main.api();
select_api_version(&client, host, &mut desired_version)?;
let api_version = desired_version.version().unwrap();
// Parse the remote file based on the share URL
let file = RemoteFile::parse_url(url, None)?;
@ -92,14 +87,15 @@ impl<'a> Download<'a> {
}
// Ensure a password is set when required
ensure_password(&mut password, exists.has_password(), &matcher_main);
ensure_password(
&mut password,
exists.requires_password(),
&matcher_main,
false,
);
// Fetch the file metadata
let metadata = ApiMetadata::new(
&file,
password.clone(),
false,
).invoke(&client)?;
let metadata = ApiMetadata::new(&file, password.clone(), false).invoke(&client)?;
// A temporary archive file, only used when archiving
// The temporary file is stored here, to ensure it's lifetime exceeds the upload process
@ -110,7 +106,8 @@ impl<'a> Download<'a> {
#[cfg(feature = "archive")]
let mut extract = matcher_download.extract();
#[cfg(feature = "archive")] {
#[cfg(feature = "archive")]
{
// Ask to extract if downloading an archive
if !extract && metadata.metadata().is_archive() {
if prompt_yes(
@ -138,10 +135,11 @@ impl<'a> Download<'a> {
#[cfg(feature = "archive")]
let output_path = target.clone();
#[cfg(feature = "archive")] {
#[cfg(feature = "archive")]
{
// Allocate an archive file, and update the download and target paths
if extract {
// TODO: select the extention dynamically
// TODO: select the extension dynamically
let archive_extention = ".tar";
// Allocate a temporary file to download the archive to
@ -150,7 +148,7 @@ impl<'a> Download<'a> {
.prefix(&format!(".{}-archive-", crate_name!()))
.suffix(archive_extention)
.tempfile()
.map_err(ExtractError::TempFile)?
.map_err(ExtractError::TempFile)?,
);
if let Some(tmp_archive) = &tmp_archive {
target = tmp_archive.path().to_path_buf();
@ -165,19 +163,23 @@ impl<'a> Download<'a> {
// Create a progress bar reporter
let progress_bar = Arc::new(Mutex::new(ProgressBar::new_download()));
let progress_reader: Arc<Mutex<ProgressReporter>> = progress_bar;
let progress_reader: Arc<Mutex<dyn ProgressReporter>> = progress_bar;
// Create a transfer client
let transfer_client = client_config.client(true);
// Execute an download action
ApiDownload::new(
&file,
target,
password,
false,
Some(metadata),
).invoke(&client, &progress_reader)?;
let progress = if !matcher_main.quiet() {
Some(progress_reader)
} else {
None
};
ApiDownload::new(api_version, &file, target, password, false, Some(metadata))
.invoke(&transfer_client, progress)?;
// Extract the downloaded file if working with an archive
#[cfg(feature = "archive")] {
#[cfg(feature = "archive")]
{
if extract {
eprintln!("Extracting...");
@ -208,7 +210,7 @@ impl<'a> Download<'a> {
///
/// The full path including the name is returned.
///
/// This method will check whether a file is overwitten, and whether
/// This method will check whether a file is overwritten, and whether
/// parent directories must be created.
///
/// The program will quit with an error message if a problem occurs.
@ -243,10 +245,7 @@ impl<'a> Download<'a> {
let dir = if file {
match target.parent() {
Some(parent) => parent,
None => quit_error_msg(
"invalid output file path",
ErrorHints::default(),
),
None => quit_error_msg("invalid output file path", ErrorHints::default()),
}
} else {
&target
@ -268,9 +267,10 @@ impl<'a> Download<'a> {
// Create the parent directories
if let Err(err) = create_dir_all(dir) {
quit_error(err.context(
"failed to create parent directories for output file",
), ErrorHints::default());
quit_error(
err.context("failed to create parent directories for output file"),
ErrorHints::default(),
);
}
}
}
@ -313,16 +313,15 @@ impl<'a> Download<'a> {
// Get the path string
let path = target.to_str();
// If the path is emtpy, use the working directory with the name hint
let use_workdir = path
.map(|path| path.trim().is_empty())
.unwrap_or(true);
// If the path is empty, use the working directory with the name hint
let use_workdir = path.map(|path| path.trim().is_empty()).unwrap_or(true);
if use_workdir {
match current_dir() {
Ok(target) => return target.join(name_hint),
Err(err) => quit_error(err.context(
"failed to determine working directory to use for the output file"
), ErrorHints::default()),
Err(err) => quit_error(
err.context("failed to determine working directory to use for the output file"),
ErrorHints::default(),
),
}
}
let path = path.unwrap();
@ -339,9 +338,10 @@ impl<'a> Download<'a> {
if target.is_relative() {
match current_dir() {
Ok(workdir) => target = workdir.join(target),
Err(err) => quit_error(err.context(
"failed to determine working directory to use for the output file"
), ErrorHints::default()),
Err(err) => quit_error(
err.context("failed to determine working directory to use for the output file"),
ErrorHints::default(),
),
}
}
@ -351,6 +351,11 @@ impl<'a> Download<'a> {
#[derive(Debug, Fail)]
pub enum Error {
/// Selecting the API version to use failed.
// TODO: enable `api` hint!
#[fail(display = "failed to select API version to use")]
Version(#[cause] VersionError),
/// Failed to parse a share URL, it was invalid.
/// This error is not related to a specific action.
#[fail(display = "invalid share link")]
@ -378,6 +383,12 @@ pub enum Error {
Expired,
}
impl From<VersionError> for Error {
fn from(err: VersionError) -> Error {
Error::Version(err)
}
}
impl From<FileParseError> for Error {
fn from(err: FileParseError) -> Error {
Error::InvalidUrl(err)

View file

@ -1,17 +1,13 @@
use clap::ArgMatches;
use ffsend_api::action::exists::{
Error as ExistsError,
Exists as ApiExists,
};
use ffsend_api::action::exists::{Error as ExistsError, Exists as ApiExists};
use ffsend_api::file::remote_file::{FileParseError, RemoteFile};
use ffsend_api::reqwest::Client;
use cmd::matcher::{Matcher, exists::ExistsMatcher};
use crate::client::create_config;
use crate::cmd::matcher::main::MainMatcher;
use crate::cmd::matcher::{exists::ExistsMatcher, Matcher};
use crate::error::ActionError;
#[cfg(feature = "history")]
use cmd::matcher::main::MainMatcher;
use error::ActionError;
#[cfg(feature = "history")]
use history_tool;
use crate::history_tool;
/// A file exists action.
pub struct Exists<'a> {
@ -21,9 +17,7 @@ pub struct Exists<'a> {
impl<'a> Exists<'a> {
/// Construct a new exists action.
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
Self {
cmd_matches,
}
Self { cmd_matches }
}
/// Invoke the exists action.
@ -31,27 +25,26 @@ impl<'a> Exists<'a> {
pub fn invoke(&self) -> Result<(), ActionError> {
// Create the command matchers
let matcher_exists = ExistsMatcher::with(self.cmd_matches).unwrap();
#[cfg(feature = "history")]
let matcher_main = MainMatcher::with(self.cmd_matches).unwrap();
// Get the share URL
let url = matcher_exists.url();
// Create a reqwest client
let client = Client::new();
let client_config = create_config(&matcher_main);
let client = client_config.client(false);
// Parse the remote file based on the share URL
let file = RemoteFile::parse_url(url, None)?;
// Make sure the file exists
let exists_response = ApiExists::new(&file)
.invoke(&client)?;
let exists_response = ApiExists::new(&file).invoke(&client)?;
let exists = exists_response.exists();
// Print the results
println!("Exists: {:?}", exists);
if exists {
println!("Password: {:?}", exists_response.has_password());
println!("Password: {:?}", exists_response.requires_password());
}
// Add or remove the file from the history

View file

@ -0,0 +1,61 @@
use std::fs;
use std::io;
use clap::ArgMatches;
use crate::cmd::matcher::{generate::completions::CompletionsMatcher, main::MainMatcher, Matcher};
use crate::error::ActionError;
/// A file completions action.
pub struct Completions<'a> {
cmd_matches: &'a ArgMatches<'a>,
}
impl<'a> Completions<'a> {
/// Construct a new completions action.
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
Self { cmd_matches }
}
/// Invoke the completions action.
// TODO: create a trait for this method
pub fn invoke(&self) -> Result<(), ActionError> {
// Create the command matchers
let matcher_main = MainMatcher::with(self.cmd_matches).unwrap();
let matcher_completions = CompletionsMatcher::with(self.cmd_matches).unwrap();
// Obtain shells to generate completions for, build application definition
let shells = matcher_completions.shells();
let dir = matcher_completions.output();
let quiet = matcher_main.quiet();
let mut app = crate::cmd::handler::Handler::build();
// If the directory does not exist yet, attempt to create it
if !dir.is_dir() {
fs::create_dir_all(&dir).map_err(Error::CreateOutputDir)?;
}
// Generate completions
for shell in shells {
if !quiet {
eprint!(
"Generating completions for {}...",
format!("{}", shell).to_lowercase()
);
}
app.gen_completions(crate_name!(), shell, &dir);
if !quiet {
eprintln!(" done.");
}
}
Ok(())
}
}
#[derive(Debug, Fail)]
pub enum Error {
/// An error occurred while creating the output directory.
#[fail(display = "failed to create output directory, it doesn't exist")]
CreateOutputDir(#[cause] io::Error),
}

View file

@ -0,0 +1,34 @@
pub mod completions;
use clap::ArgMatches;
use crate::cmd::matcher::{generate::GenerateMatcher, Matcher};
use crate::error::ActionError;
use completions::Completions;
/// A file generate action.
pub struct Generate<'a> {
cmd_matches: &'a ArgMatches<'a>,
}
impl<'a> Generate<'a> {
/// Construct a new generate action.
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
Self { cmd_matches }
}
/// Invoke the generate action.
// TODO: create a trait for this method
pub fn invoke(&self) -> Result<(), ActionError> {
// Create the command matcher
let matcher_generate = GenerateMatcher::with(self.cmd_matches).unwrap();
// Match shell completions
if matcher_generate.matcher_completions().is_some() {
return Completions::new(self.cmd_matches).invoke();
}
// Unreachable, clap will print help for missing sub command instead
unreachable!()
}
}

View file

@ -1,21 +1,11 @@
use clap::ArgMatches;
use prettytable::{
cell::Cell,
format::FormatBuilder,
row::Row,
Table,
};
use failure::Fail;
use prettytable::{format::FormatBuilder, Cell, Row, Table};
use cmd::matcher::{
Matcher,
main::MainMatcher,
};
use error::ActionError;
use history::{
History as HistoryManager,
LoadError as HistoryLoadError,
};
use util::format_duration;
use crate::cmd::matcher::{history::HistoryMatcher, main::MainMatcher, Matcher};
use crate::error::ActionError;
use crate::history::{History as HistoryManager, LoadError as HistoryLoadError};
use crate::util::{format_duration, quit_error, quit_error_msg, ErrorHintsBuilder};
/// A history action.
pub struct History<'a> {
@ -25,9 +15,7 @@ pub struct History<'a> {
impl<'a> History<'a> {
/// Construct a new history action.
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
Self {
cmd_matches,
}
Self { cmd_matches }
}
/// Invoke the history action.
@ -35,63 +23,121 @@ impl<'a> History<'a> {
pub fn invoke(&self) -> Result<(), ActionError> {
// Create the command matchers
let matcher_main = MainMatcher::with(self.cmd_matches).unwrap();
let matcher_history = HistoryMatcher::with(self.cmd_matches).unwrap();
// Get the history path, make sure it exists
let history_path = matcher_main.history();
if !history_path.is_file() {
eprintln!("No files in history");
if !matcher_main.quiet() {
eprintln!("No files in history");
}
return Ok(());
}
// History
let history = HistoryManager::load(history_path)?;
let mut history = HistoryManager::load(history_path)?;
// Do not report any files if there aren't any
if history.files().is_empty() {
eprintln!("No files in history");
if !matcher_main.quiet() {
eprintln!("No files in history");
}
return Ok(());
}
// Create a new table
let mut table = Table::new();
table.set_format(FormatBuilder::new().padding(0, 2).build());
table.add_row(Row::new(vec![
Cell::new("#"),
Cell::new("LINK"),
Cell::new("EXPIRY"),
Cell::new("OWNER TOKEN"),
]));
// Clear all history
if matcher_history.clear() {
history.clear();
// Save history
if let Err(err) = history.save() {
quit_error(
err,
ErrorHintsBuilder::default().verbose(true).build().unwrap(),
);
}
eprintln!("History cleared");
return Ok(());
}
// Remove history item
if let Some(url) = matcher_history.rm() {
// Remove item, print error if no item with URL was found
match history.remove_url(url) {
Ok(removed) if !removed => quit_error_msg(
"could not remove item from history, no item matches given URL",
ErrorHintsBuilder::default().verbose(true).build().unwrap(),
),
Err(err) => quit_error(
err.context("could not remove item from history"),
ErrorHintsBuilder::default().verbose(true).build().unwrap(),
),
_ => {}
}
// Save history
if let Err(err) = history.save() {
quit_error(
err,
ErrorHintsBuilder::default().verbose(true).build().unwrap(),
);
}
eprintln!("Item removed from history");
return Ok(());
}
// Get the list of files, and sort the first expiring files to be last
let mut files = history.files().clone();
files.sort_by(|a, b| b.expire_at().cmp(&a.expire_at()));
// Add an entry for each file
for (i, file) in files.iter().enumerate() {
// Build the expiry time string
let mut expiry = format_duration(&file.expire_duration());
if file.expire_uncertain() {
expiry.insert(0, '~');
// Log a history table, or just the URLs in quiet mode
if !matcher_main.quiet() {
// Build the list of column names
let mut columns = vec!["#", "LINK", "EXPIRE"];
if matcher_main.verbose() {
columns.push("OWNER TOKEN");
}
// Get the owner token
let owner_token: String = match file.owner_token() {
Some(token) => token.clone(),
None => "?".into(),
};
// Create a new table
let mut table = Table::new();
table.set_format(FormatBuilder::new().padding(0, 2).build());
table.add_row(Row::new(columns.into_iter().map(Cell::new).collect()));
// Add the row
table.add_row(Row::new(vec![
Cell::new(&format!("{}", i + 1)),
Cell::new(file.download_url(true).as_str()),
Cell::new(&expiry),
Cell::new(&owner_token),
]));
// Add an entry for each file
for (i, file) in files.iter().enumerate() {
// Build the expiry time string
let mut expiry = format_duration(&file.expire_duration());
if file.expire_uncertain() {
expiry.insert(0, '~');
}
// Get the owner token
let owner_token: String = match file.owner_token() {
Some(token) => token.clone(),
None => "?".into(),
};
// Define the cell values
let mut cells: Vec<String> =
vec![format!("{}", i + 1), file.download_url(true).into(), expiry];
if matcher_main.verbose() {
cells.push(owner_token);
}
// Add the row
table.add_row(Row::new(cells.into_iter().map(|c| Cell::new(&c)).collect()));
}
// Print the table
table.printstd();
} else {
files
.iter()
.for_each(|f| println!("{}", f.download_url(true)));
}
// Print the table
table.printstd();
Ok(())
}
}

View file

@ -1,41 +1,18 @@
use chrono::Duration;
use clap::ArgMatches;
use failure::Fail;
use ffsend_api::action::exists::{
Error as ExistsError,
Exists as ApiExists,
};
use ffsend_api::action::info::{
Error as InfoError,
Info as ApiInfo,
};
use ffsend_api::action::exists::{Error as ExistsError, Exists as ApiExists};
use ffsend_api::action::info::{Error as InfoError, Info as ApiInfo};
use ffsend_api::action::metadata::Metadata as ApiMetadata;
use ffsend_api::file::remote_file::{
FileParseError,
RemoteFile,
};
use ffsend_api::reqwest::Client;
use prettytable::{
cell::Cell,
format::FormatBuilder,
row::Row,
Table,
};
use ffsend_api::file::remote_file::{FileParseError, RemoteFile};
use prettytable::{format::FormatBuilder, Cell, Row, Table};
use cmd::matcher::{
Matcher,
info::InfoMatcher,
main::MainMatcher,
};
use crate::client::create_config;
use crate::cmd::matcher::{info::InfoMatcher, main::MainMatcher, Matcher};
#[cfg(feature = "history")]
use history_tool;
use util::{
ensure_owner_token,
ensure_password,
format_bytes,
format_duration,
print_error,
use crate::history_tool;
use crate::util::{
ensure_owner_token, ensure_password, format_bytes, format_duration, print_error,
};
/// A file info action.
@ -46,9 +23,7 @@ pub struct Info<'a> {
impl<'a> Info<'a> {
/// Construct a new info action.
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
Self {
cmd_matches,
}
Self { cmd_matches }
}
/// Invoke the info action.
@ -62,15 +37,16 @@ impl<'a> Info<'a> {
let url = matcher_info.url();
// Create a reqwest client
let client = Client::new();
let client_config = create_config(&matcher_main);
let client = client_config.client(false);
// Parse the remote file based on the share URL, derive the owner token from history
let mut file = RemoteFile::parse_url(url, matcher_info.owner())?;
#[cfg(feature = "history")]
history_tool::derive_file_properties(&matcher_main, &mut file);
// Ensure the owner token is set
ensure_owner_token(file.owner_token_mut(), &matcher_main);
// Ask the user to set the owner token for more detailed information
let has_owner = ensure_owner_token(file.owner_token_mut(), &matcher_main, true);
// Check whether the file exists
let exists = ApiExists::new(&file).invoke(&client)?;
@ -82,27 +58,38 @@ impl<'a> Info<'a> {
return Err(Error::Expired);
}
// Get the password
// Get the password, ensure the password is set when required
let mut password = matcher_info.password();
// Ensure a password is set when required
ensure_password(&mut password, exists.has_password(), &matcher_main);
let has_password = ensure_password(
&mut password,
exists.requires_password(),
&matcher_main,
true,
);
// Fetch both file info and metadata
let info = ApiInfo::new(&file, None).invoke(&client)?;
let metadata = ApiMetadata::new(&file, password, false)
.invoke(&client)
.map_err(|err| print_error(err.context(
"failed to fetch file metadata, showing limited info",
)))
.ok();
let info = if has_owner {
Some(ApiInfo::new(&file, None).invoke(&client)?)
} else {
None
};
let metadata = if has_password {
ApiMetadata::new(&file, password, false)
.invoke(&client)
.map_err(|err| {
print_error(err.context("failed to fetch file metadata, showing limited info"))
})
.ok()
} else {
None
};
// Get the TTL duration
let ttl_millis = info.ttl_millis() as i64;
let ttl = Duration::milliseconds(ttl_millis);
// Update file properties
file.set_expire_duration(ttl);
// Update history file TTL if info is known
if let Some(info) = &info {
let ttl_millis = info.ttl_millis() as i64;
let ttl = Duration::milliseconds(ttl_millis);
file.set_expire_duration(ttl);
}
// Add the file to the history
#[cfg(feature = "history")]
@ -113,13 +100,10 @@ impl<'a> Info<'a> {
table.set_format(FormatBuilder::new().padding(0, 2).build());
// Add the ID
table.add_row(Row::new(vec![
Cell::new("ID:"),
Cell::new(file.id()),
]));
table.add_row(Row::new(vec![Cell::new("ID:"), Cell::new(file.id())]));
// Metadata related details
if let Some(metadata) = metadata {
// Show file metadata if available
if let Some(metadata) = &metadata {
// The file name
table.add_row(Row::new(vec![
Cell::new("Name:"),
@ -130,13 +114,11 @@ impl<'a> Info<'a> {
let size = metadata.size();
table.add_row(Row::new(vec![
Cell::new("Size:"),
Cell::new(
&if size >= 1024 {
format!("{} ({} B)", format_bytes(size), size)
} else {
format_bytes(size)
}
),
Cell::new(&if size >= 1024 {
format!("{} ({} B)", format_bytes(size), size)
} else {
format_bytes(size)
}),
]));
// The file MIME
@ -146,23 +128,30 @@ impl<'a> Info<'a> {
]));
}
// The download count
table.add_row(Row::new(vec![
Cell::new("Downloads:"),
Cell::new(&format!("{} of {}", info.download_count(), info.download_limit())),
]));
// Show file info if available
if let Some(info) = &info {
// The download count
table.add_row(Row::new(vec![
Cell::new("Downloads:"),
Cell::new(&format!(
"{} of {}",
info.download_count(),
info.download_limit()
)),
]));
// The time to live
table.add_row(Row::new(vec![
Cell::new("Expiry:"),
Cell::new(
&if ttl_millis >= 60 * 1000 {
// The time to live
let ttl_millis = info.ttl_millis() as i64;
let ttl = Duration::milliseconds(ttl_millis);
table.add_row(Row::new(vec![
Cell::new("Expiry:"),
Cell::new(&if ttl_millis >= 60 * 1000 {
format!("{} ({}s)", format_duration(&ttl), ttl.num_seconds())
} else {
format_duration(&ttl)
}
),
]));
}),
]));
}
// Print the info table
table.printstd();

View file

@ -2,9 +2,57 @@ pub mod debug;
pub mod delete;
pub mod download;
pub mod exists;
pub mod generate;
#[cfg(feature = "history")]
pub mod history;
pub mod info;
pub mod params;
pub mod password;
pub mod upload;
pub mod version;
use ffsend_api::action::version::{Error as VersionError, Version as ApiVersion};
use ffsend_api::api::DesiredVersion;
use ffsend_api::client::Client;
use ffsend_api::url::Url;
use crate::config::API_VERSION_ASSUME;
use crate::util::print_warning;
/// Based on the given desired API version, select a version we can use.
///
/// If the current desired version is set to the `DesiredVersion::Lookup` variant, this method
/// will look up the server API version. It it's `DesiredVersion::Use` it will return and
/// attempt to use the specified version.
fn select_api_version(
client: &Client,
host: Url,
desired: &mut DesiredVersion,
) -> Result<(), VersionError> {
// Break if already specified
if let DesiredVersion::Use(_) = desired {
return Ok(());
}
// TODO: only lookup if `DesiredVersion::Assume` after first operation attempt failed
// Look up the version
match ApiVersion::new(host).invoke(&client) {
// Use the probed version
Ok(v) => *desired = DesiredVersion::Use(v),
// If unknown, just assume the default version
Err(VersionError::Unknown) => {
*desired = DesiredVersion::Use(API_VERSION_ASSUME);
print_warning(format!(
"server API version could not be determined, assuming v{}",
API_VERSION_ASSUME,
));
}
// Propagate other errors
Err(e) => return Err(e),
}
Ok(())
}

View file

@ -1,21 +1,14 @@
use clap::ArgMatches;
use ffsend_api::action::params::{
Error as ParamsError,
Params as ApiParams,
ParamsDataBuilder,
};
use ffsend_api::action::params::{Error as ParamsError, Params as ApiParams, ParamsDataBuilder};
use ffsend_api::file::remote_file::RemoteFile;
use ffsend_api::reqwest::Client;
use cmd::matcher::{
Matcher,
main::MainMatcher,
params::ParamsMatcher,
};
use error::ActionError;
use super::select_api_version;
use crate::client::create_config;
use crate::cmd::matcher::{main::MainMatcher, params::ParamsMatcher, Matcher};
use crate::error::ActionError;
#[cfg(feature = "history")]
use history_tool;
use util::{ensure_owner_token, print_success};
use crate::history_tool;
use crate::util::{ensure_owner_token, print_success};
/// A file parameters action.
pub struct Params<'a> {
@ -25,9 +18,7 @@ pub struct Params<'a> {
impl<'a> Params<'a> {
/// Construct a new parameters action.
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
Self {
cmd_matches,
}
Self { cmd_matches }
}
/// Invoke the parameters action.
@ -37,11 +28,22 @@ impl<'a> Params<'a> {
let matcher_main = MainMatcher::with(self.cmd_matches).unwrap();
let matcher_params = ParamsMatcher::with(self.cmd_matches).unwrap();
// Get the share URL
// Get the share URL and the host
// TODO: derive host through helper function
let url = matcher_params.url();
let mut host = url.clone();
host.set_path("");
host.set_query(None);
host.set_fragment(None);
// Create a reqwest client
let client = Client::new();
let client_config = create_config(&matcher_main);
let client = client_config.client(false);
// Determine the API version to use
let mut desired_version = matcher_main.api();
select_api_version(&client, host, &mut desired_version)?;
let api_version = desired_version.version().unwrap();
// Parse the remote file based on the share URL, derive the owner token from history
let mut file = RemoteFile::parse_url(url, matcher_params.owner())?;
@ -49,11 +51,18 @@ impl<'a> Params<'a> {
history_tool::derive_file_properties(&matcher_main, &mut file);
// Ensure the owner token is set
ensure_owner_token(file.owner_token_mut(), &matcher_main);
ensure_owner_token(file.owner_token_mut(), &matcher_main, false);
// We don't authenticate for now
let auth = false;
// Build the parameters data object
let data = ParamsDataBuilder::default()
.download_limit(matcher_params.download_limit())
.download_limit(
matcher_params
.download_limit(&matcher_main, api_version, auth)
.map(|d| d as u8),
)
.build()
.unwrap();

View file

@ -1,20 +1,14 @@
use clap::ArgMatches;
use ffsend_api::action::password::{
Error as PasswordError,
Password as ApiPassword,
};
use ffsend_api::action::password::{Error as PasswordError, Password as ApiPassword};
use ffsend_api::file::remote_file::RemoteFile;
use ffsend_api::reqwest::Client;
use prettytable::{format::FormatBuilder, Cell, Row, Table};
use cmd::matcher::{
main::MainMatcher,
Matcher,
password::PasswordMatcher,
};
use error::ActionError;
use crate::client::create_config;
use crate::cmd::matcher::{main::MainMatcher, password::PasswordMatcher, Matcher};
use crate::error::ActionError;
#[cfg(feature = "history")]
use history_tool;
use util::{ensure_owner_token, print_success};
use crate::history_tool;
use crate::util::{ensure_owner_token, print_success};
/// A file password action.
pub struct Password<'a> {
@ -24,9 +18,7 @@ pub struct Password<'a> {
impl<'a> Password<'a> {
/// Construct a new password action.
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
Self {
cmd_matches,
}
Self { cmd_matches }
}
/// Invoke the password action.
@ -40,7 +32,8 @@ impl<'a> Password<'a> {
let url = matcher_password.url();
// Create a reqwest client
let client = Client::new();
let client_config = create_config(&matcher_main);
let client = client_config.client(false);
// Parse the remote file based on the share URL, derive the owner token from history
let mut file = RemoteFile::parse_url(url, matcher_password.owner())?;
@ -48,14 +41,13 @@ impl<'a> Password<'a> {
history_tool::derive_file_properties(&matcher_main, &mut file);
// Ensure the owner token is set
ensure_owner_token(file.owner_token_mut(), &matcher_main);
ensure_owner_token(file.owner_token_mut(), &matcher_main, false);
// Get the password to use and whether it was generated
let (password, password_generated) = matcher_password.password();
// Execute an password action
let result = ApiPassword::new(
&file,
&matcher_password.password(),
None,
).invoke(&client);
let result = ApiPassword::new(&file, &password, None).invoke(&client);
if let Err(PasswordError::Expired) = result {
// Remove the file from the history if expired
#[cfg(feature = "history")]
@ -67,6 +59,17 @@ impl<'a> Password<'a> {
#[cfg(feature = "history")]
history_tool::add(&matcher_main, file, true);
// Print the passphrase if one was generated
if password_generated {
let mut table = Table::new();
table.set_format(FormatBuilder::new().padding(0, 2).build());
table.add_row(Row::new(vec![
Cell::new("Passphrase:"),
Cell::new(&password),
]));
table.printstd();
}
// Print a success message
print_success("Password set");

View file

@ -1,49 +1,42 @@
use std::fs::File;
#[cfg(feature = "archive")]
use std::io::Error as IoError;
use std::env::current_dir;
use std::fs;
use std::io::{Error as IoError, Write};
use std::path::Path;
#[cfg(feature = "archive")]
use std::path::PathBuf;
#[cfg(feature = "archive")]
use std::process::exit;
use std::sync::{Arc, Mutex};
use clap::ArgMatches;
use failure::Fail;
use ffsend_api::action::params::ParamsDataBuilder;
use ffsend_api::action::upload::{
Error as UploadError,
Upload as ApiUpload,
};
use ffsend_api::config::{UPLOAD_SIZE_MAX, UPLOAD_SIZE_MAX_RECOMMENDED};
use ffsend_api::reader::ProgressReporter;
use ffsend_api::reqwest::Client;
use prettytable::{
cell::Cell,
format::FormatBuilder,
row::Row,
Table,
};
#[cfg(feature = "archive")]
use tempfile::{
Builder as TempBuilder,
NamedTempFile,
};
use ffsend_api::action::upload::{Error as UploadError, Upload as ApiUpload};
use ffsend_api::action::version::Error as VersionError;
use ffsend_api::config::{upload_size_max, UPLOAD_SIZE_MAX_RECOMMENDED};
use ffsend_api::pipe::ProgressReporter;
use pathdiff::diff_paths;
use prettytable::{format::FormatBuilder, Cell, Row, Table};
#[cfg(feature = "qrcode")]
use qr2term::print_qr;
use tempfile::{Builder as TempBuilder, NamedTempFile};
use super::select_api_version;
#[cfg(feature = "archive")]
use archive::archiver::Archiver;
use cmd::matcher::{Matcher, MainMatcher, UploadMatcher};
use crate::archive::archiver::Archiver;
use crate::client::create_config;
use crate::cmd::matcher::{MainMatcher, Matcher, UploadMatcher};
#[cfg(feature = "history")]
use history_tool;
use progress::ProgressBar;
use util::{
ErrorHintsBuilder,
format_bytes,
open_url,
print_error,
print_error_msg,
prompt_yes,
quit,
quit_error_msg,
};
use crate::history_tool;
use crate::progress::ProgressBar;
#[cfg(feature = "urlshorten")]
use crate::urlshorten;
#[cfg(feature = "clipboard")]
use util::set_clipboard;
use crate::util::set_clipboard;
use crate::util::{
format_bytes, open_url, print_error, print_error_msg, prompt_yes, quit, quit_error_msg,
rand_alphanum_string, stdin_read_file, ErrorHintsBuilder, StdinErr,
};
/// A file upload action.
pub struct Upload<'a> {
@ -53,9 +46,7 @@ pub struct Upload<'a> {
impl<'a> Upload<'a> {
/// Construct a new upload action.
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
Self {
cmd_matches,
}
Self { cmd_matches }
}
/// Invoke the upload action.
@ -65,93 +56,93 @@ impl<'a> Upload<'a> {
let matcher_main = MainMatcher::with(self.cmd_matches).unwrap();
let matcher_upload = UploadMatcher::with(self.cmd_matches).unwrap();
// Get API parameters
#[allow(unused_mut)]
let mut path = Path::new(matcher_upload.file()).to_path_buf();
let host = matcher_upload.host();
// TODO: ensure the file exists and is accessible
// Get the file size to warn about large files
if let Ok(size) = File::open(&path)
.and_then(|f| f.metadata())
.map(|m| m.len())
{
if size > UPLOAD_SIZE_MAX && !matcher_main.force() {
// The file is too large, show an error and quit
quit_error_msg(
format!(
"the file size is {}, bigger than the maximum allowed of {}",
format_bytes(size),
format_bytes(UPLOAD_SIZE_MAX),
),
ErrorHintsBuilder::default()
.force(true)
.verbose(false)
.build()
.unwrap(),
);
} else if size > UPLOAD_SIZE_MAX_RECOMMENDED && !matcher_main.force() {
// The file is larger than the recommended maximum, warn
eprintln!(
"The file size is {}, bigger than the recommended maximum of {}",
format_bytes(size),
format_bytes(UPLOAD_SIZE_MAX_RECOMMENDED),
);
// Prompt the user to continue, quit if the user answered no
if !prompt_yes("Continue uploading?", Some(true), &matcher_main) {
println!("Upload cancelled");
quit();
}
}
} else {
print_error_msg("failed to check the file size, ignoring");
}
// Create a reqwest client
let client = Client::new();
// Create a progress bar reporter
let progress_bar = Arc::new(Mutex::new(ProgressBar::new_upload()));
// Build a parameters object to set for the file
let params = {
// Build the parameters data object
let mut params = ParamsDataBuilder::default()
.download_limit(matcher_upload.download_limit())
.build()
.unwrap();
// Wrap the data in an option if not empty
if params.is_empty() {
None
} else {
Some(params)
}
};
// The file name to use
#[allow(unused_mut)]
let mut file_name = matcher_upload.name().map(|s| s.to_owned());
// The selected files
let mut files = matcher_upload.files();
// If file is `-`, upload from stdin
// TODO: write stdin directly to file, or directly to upload buffer
let mut tmp_stdin: Option<NamedTempFile> = None;
if files.len() == 1 && files[0] == "-" {
// Obtain data from stdin
let data = stdin_read_file(!matcher_main.quiet()).map_err(Error::Stdin)?;
// Create temporary stdin buffer file
tmp_stdin = Some(
TempBuilder::new()
.prefix(&format!(".{}-stdin-", crate_name!()))
.tempfile()
.map_err(Error::StdinTempFile)?,
);
let file = tmp_stdin.as_ref().unwrap();
// Fill temporary file with data, update list of files we upload, suggest name
file.as_file()
.write_all(&data)
.map_err(Error::StdinTempFile)?;
files = vec![file
.path()
.to_str()
.expect("failed to obtain file name for stdin buffer file")];
file_name = file_name.or_else(|| Some("stdin.txt".into()));
}
// Get API parameters
#[allow(unused_mut)]
let mut paths: Vec<_> = files
.into_iter()
.map(|p| Path::new(p).to_path_buf())
.collect();
let mut path = Path::new(paths.first().unwrap()).to_path_buf();
let host = matcher_upload.host();
// All paths must exist
// TODO: ensure the file exists and is accessible
for path in &paths {
if !path.exists() {
quit_error_msg(
format!("the path '{}' does not exist", path.to_str().unwrap_or("?")),
ErrorHintsBuilder::default().build().unwrap(),
);
}
}
// A temporary archive file, only used when archiving
// The temporary file is stored here, to ensure it's lifetime exceeds the upload process
#[allow(unused_mut)]
#[cfg(feature = "archive")]
let mut tmp_archive: Option<NamedTempFile> = None;
#[cfg(feature = "archive")] {
// Determine whether to archive, ask if a directory was selected
#[cfg(feature = "archive")]
{
// Determine whether to archive, we must archive for multiple files/directory
let mut archive = matcher_upload.archive();
if !archive && path.is_dir() {
if prompt_yes(
"You've selected a directory, only a single file may be uploaded.\n\
Archive the directory into a single file?",
Some(true),
&matcher_main,
) {
archive = true;
if !archive {
if paths.len() > 1 {
if prompt_yes(
"You've selected multiple files, only a single file may be uploaded.\n\
Archive the files into a single file?",
Some(true),
&matcher_main,
) {
archive = true;
} else {
exit(1);
}
} else if path.is_dir() {
if prompt_yes(
"You've selected a directory, only a single file may be uploaded.\n\
Archive the directory into a single file?",
Some(true),
&matcher_main,
) {
archive = true;
} else {
exit(1);
}
}
}
@ -166,72 +157,297 @@ impl<'a> Upload<'a> {
.prefix(&format!(".{}-archive-", crate_name!()))
.suffix(archive_extention)
.tempfile()
.map_err(ArchiveError::TempFile)?
.map_err(ArchiveError::TempFile)?,
);
if let Some(tmp_archive) = &tmp_archive {
// Get the path, and the actual file
let archive_path = tmp_archive.path().to_path_buf();
let archive_file = tmp_archive.as_file()
let archive_file = tmp_archive
.as_file()
.try_clone()
.map_err(ArchiveError::CloneHandle)?;
// Select the file name to use if not set
if file_name.is_none() {
file_name = Some(
path.canonicalize()
.map_err(|err| ArchiveError::FileName(Some(err)))?
.file_name()
.ok_or(ArchiveError::FileName(None))?
.to_str()
.map(|s| s.to_owned())
.ok_or(ArchiveError::FileName(None))?
);
// Derive name from given file
if paths.len() == 1 {
file_name = Some(
path.canonicalize()
.map_err(|err| ArchiveError::FileName(Some(err)))?
.file_name()
.ok_or(ArchiveError::FileName(None))?
.to_str()
.map(|s| s.to_owned())
.ok_or(ArchiveError::FileName(None))?,
);
} else {
// Unable to derive file name from paths, generate random
file_name = Some(format!("ffsend-archive-{}", rand_alphanum_string(8)));
}
}
// Build an archiver and append the file
// Get the current working directory, including working directory as highest possible root, canonicalize it
let working_dir =
current_dir().expect("failed to get current working directory");
let shared_dir = {
let mut paths = paths.clone();
paths.push(working_dir.clone());
match shared_dir(paths) {
Some(p) => p,
None => quit_error_msg(
"when archiving, all files must be within a same directory",
ErrorHintsBuilder::default().verbose(false).build().unwrap(),
),
}
};
// Build an archiver, append each file
let mut archiver = Archiver::new(archive_file);
archiver.append_path(file_name.as_ref().unwrap(), &path)
.map_err(ArchiveError::AddFile)?;
for path in &paths {
// Canonicalize the path
let mut path = Path::new(path).to_path_buf();
if let Ok(p) = path.canonicalize() {
path = p;
}
// Find relative name to share dir, used to derive name from
let name = diff_paths(&path, &shared_dir)
.expect("failed to determine relative path of file to archive");
let name = name.to_str().expect("failed to get file path");
// Add file to archiver
archiver
.append_path(name, &path)
.map_err(ArchiveError::AddFile)?;
}
// Finish the archival process, writes the archive file
archiver.finish().map_err(ArchiveError::Write)?;
// Append archive extention to name, set to upload archived file
// Append archive extension to name, set to upload archived file
if let Some(ref mut file_name) = file_name {
file_name.push_str(archive_extention);
}
path = archive_path;
paths.clear();
}
}
}
// Build the progress reporter
let progress_reporter: Arc<Mutex<ProgressReporter>> = progress_bar;
// Quit with error when uploading multiple files or directory, if we cannot archive
#[cfg(not(feature = "archive"))]
{
if paths.len() > 1 {
quit_error_msg(
"uploading multiple files is not supported, ffsend must be compiled with 'archive' feature for this",
ErrorHintsBuilder::default()
.verbose(false)
.build()
.unwrap(),
);
}
if path.is_dir() {
quit_error_msg(
"uploading a directory is not supported, ffsend must be compiled with 'archive' feature for this",
ErrorHintsBuilder::default()
.verbose(false)
.build()
.unwrap(),
);
}
}
// Execute an upload action
// Create a reqwest client capable for uploading files
let client_config = create_config(&matcher_main);
let client = client_config.clone().client(false);
// Determine the API version to use
let mut desired_version = matcher_main.api();
select_api_version(&client, host.clone(), &mut desired_version)?;
let api_version = desired_version.version().unwrap();
// We do not authenticate for now
let auth = false;
// TODO: extract this into external function
{
// Determine the max file size
// TODO: set false parameter to authentication state
let max_size = upload_size_max(api_version, auth);
// Get the file size, fail on empty files, warn about large files
if let Ok(size) = path.metadata().map(|m| m.len()) {
// Enforce files not being 0 bytes
if size == 0 && !matcher_main.force() {
quit_error_msg(
"uploading a file with a size of 0 bytes is not supported",
ErrorHintsBuilder::default()
.force(true)
.verbose(false)
.build()
.unwrap(),
)
}
// Enforce maximum file size
if size > max_size && !matcher_main.force() {
// The file is too large, show an error and quit
quit_error_msg(
format!(
"the file size is {}, bigger than the maximum allowed of {}",
format_bytes(size),
format_bytes(max_size),
),
ErrorHintsBuilder::default()
.force(true)
.verbose(false)
.build()
.unwrap(),
);
}
// Enforce maximum recommended size
if size > UPLOAD_SIZE_MAX_RECOMMENDED && !matcher_main.force() {
// The file is larger than the recommended maximum, warn
eprintln!(
"The file size is {}, bigger than the recommended maximum of {}",
format_bytes(size),
format_bytes(UPLOAD_SIZE_MAX_RECOMMENDED),
);
// Prompt the user to continue, quit if the user answered no
if !prompt_yes("Continue uploading?", Some(true), &matcher_main) {
eprintln!("Upload cancelled");
quit();
}
}
} else {
print_error_msg("failed to check the file size, ignoring");
}
}
// TODO: assert max expiry time for file
// Create a reqwest client capable for uploading files
let transfer_client = client_config.client(true);
// Create a progress bar reporter
let progress_bar = Arc::new(Mutex::new(ProgressBar::new_upload()));
// Build a parameters object to set for the file
let params = {
// Build the parameters data object
let params = ParamsDataBuilder::default()
.download_limit(
matcher_upload
.download_limit(&matcher_main, api_version, auth)
.map(|d| d as u8),
)
.expiry_time(matcher_upload.expiry_time(&matcher_main, api_version, auth))
.build()
.unwrap();
// Wrap the data in an option if not empty
if params.is_empty() {
None
} else {
Some(params)
}
};
// Build the progress reporter
let progress_reporter: Arc<Mutex<dyn ProgressReporter>> = progress_bar;
// Get the password to use and whether it was generated
let password = matcher_upload.password();
let (password, password_generated) =
password.map(|(p, g)| (Some(p), g)).unwrap_or((None, false));
// Execute an upload action, obtain the URL
let reporter = if !matcher_main.quiet() {
Some(&progress_reporter)
} else {
None
};
let file = ApiUpload::new(
api_version,
host,
path.clone(),
file_name,
matcher_upload.password(),
password.clone(),
params,
).invoke(&client, &progress_reporter)?;
)
.invoke(&transfer_client, reporter)?;
#[allow(unused_mut)]
let mut url = file.download_url(true);
// Get the download URL, and report it in the console in a table
let url = file.download_url(true);
let mut table = Table::new();
table.set_format(FormatBuilder::new().padding(0, 2).build());
table.add_row(Row::new(vec![
Cell::new("Share link:"),
Cell::new(url.as_str()),
]));
if matcher_main.verbose() {
table.add_row(Row::new(vec![
Cell::new("Owner token:"),
Cell::new(file.owner_token().unwrap()),
]));
// Shorten the share URL if requested, prompt the user to confirm
#[cfg(feature = "urlshorten")]
{
if matcher_upload.shorten() {
if prompt_yes("URL shortening is a security risk. This shares the secret URL with a 3rd party.\nDo you want to shorten the share URL?", Some(false), &matcher_main) {
match urlshorten::shorten_url(&client, &url) {
Ok(short) => url = short,
Err(err) => print_error(
err.context("failed to shorten share URL, ignoring")
.compat(),
),
}
}
}
}
// Report the result
if !matcher_main.quiet() {
// Create a table
let mut table = Table::new();
table.set_format(FormatBuilder::new().padding(0, 2).build());
// Show the original URL when shortening, verbose and different
#[cfg(feature = "urlshorten")]
{
let full_url = file.download_url(true);
if matcher_main.verbose() && matcher_upload.shorten() && url != full_url {
table.add_row(Row::new(vec![
Cell::new("Full share link:"),
Cell::new(full_url.as_str()),
]));
}
}
if matcher_main.verbose() {
// Show the share URL
table.add_row(Row::new(vec![
Cell::new("Share link:"),
Cell::new(url.as_str()),
]));
// Show a generate passphrase
if password_generated {
table.add_row(Row::new(vec![
Cell::new("Passphrase:"),
Cell::new(&password.unwrap_or("?".into())),
]));
}
// Show the owner token
table.add_row(Row::new(vec![
Cell::new("Owner token:"),
Cell::new(file.owner_token().unwrap()),
]));
} else {
table.add_row(Row::new(vec![Cell::new(url.as_str())]));
// Show a generate passphrase
if password_generated {
table.add_row(Row::new(vec![Cell::new(&password.unwrap_or("?".into()))]));
}
}
table.printstd();
} else {
println!("{}", url);
}
table.printstd();
// Add the file to the history manager
#[cfg(feature = "history")]
@ -240,38 +456,157 @@ impl<'a> Upload<'a> {
// Open the URL in the browser
if matcher_upload.open() {
if let Err(err) = open_url(&url) {
print_error(
err.context("failed to open the share link in the browser")
);
print_error(err.context("failed to open the share link in the browser"));
};
}
// Copy the URL in the user's clipboard
#[cfg(feature = "clipboard")] {
if matcher_upload.copy() {
if let Err(err) = set_clipboard(url.as_str().to_owned()) {
print_error(err.context("failed to copy the share link to the clipboard, ignoring"));
// Copy the URL or command to the user's clipboard
#[cfg(feature = "clipboard")]
{
if let Some(copy_mode) = matcher_upload.copy() {
if let Err(err) = set_clipboard(copy_mode.build(url.as_str())) {
print_error(
err.context("failed to copy the share link to the clipboard, ignoring"),
);
}
}
}
#[cfg(feature = "archive")] {
// Print a QR code for the share URL
#[cfg(feature = "qrcode")]
{
if matcher_upload.qrcode() {
if let Err(err) = print_qr(url.as_str()) {
print_error(err.context("failed to print QR code, ignoring").compat());
}
}
}
// Close the temporary stdin buffer file, to ensure it's removed
if let Some(tmp_stdin) = tmp_stdin.take() {
if let Err(err) = tmp_stdin.close() {
print_error(
err.context("failed to clean up temporary stdin buffer file, ignoring")
.compat(),
);
}
}
#[cfg(feature = "archive")]
{
// Close the temporary zip file, to ensure it's removed
if let Some(tmp_archive) = tmp_archive.take() {
if let Err(err) = tmp_archive.close() {
print_error(
err.context("failed to clean up temporary archive file, ignoring").compat(),
err.context("failed to clean up temporary archive file, ignoring")
.compat(),
);
}
}
}
// Delete local files after uploading
if matcher_upload.delete() {
for path in &paths {
if path.is_file() {
if let Err(err) = fs::remove_file(path) {
print_error(
Error::Delete(err)
.context("failed to delete local file after upload, ignoring")
.compat(),
);
}
} else {
if let Err(err) = fs::remove_dir_all(path) {
print_error(
Error::Delete(err)
.context("failed to delete local directory after upload, ignoring")
.compat(),
);
}
}
}
}
Ok(())
}
}
/// Find the deepest directory all given paths share.
///
/// This function canonicalizes the paths, make sure the paths exist.
///
/// Returns `None` if paths are using a different root.
///
/// # Examples
///
/// If the following paths are given:
///
/// - `/home/user/git/ffsend/src`
/// - `/home/user/git/ffsend/src/main.rs`
/// - `/home/user/git/ffsend/Cargo.toml`
///
/// The following is returned:
///
/// `/home/user/git/ffsend`
#[cfg(feature = "archive")]
fn shared_dir(paths: Vec<PathBuf>) -> Option<PathBuf> {
// Any path must be given
if paths.is_empty() {
return None;
}
// Build vector
let c: Vec<Vec<PathBuf>> = paths
.into_iter()
.map(|p| p.canonicalize().expect("failed to canonicalize path"))
.map(|mut p| {
// Start with parent if current path is file
if p.is_file() {
p = match p.parent() {
Some(p) => p.to_path_buf(),
None => return vec![],
};
}
// Build list of path buffers for each path component
let mut items = vec![p];
#[allow(mutable_borrow_reservation_conflict)]
while let Some(item) = items.last().unwrap().parent() {
items.push(item.to_path_buf());
}
// Reverse as we built it in the wrong order
items.reverse();
items
})
.collect();
// Find the index at which the paths are last shared at by walking through indices
let i = (0..)
.take_while(|i| {
// Get path for first item, stop if none
let base = &c[0].get(*i);
if base.is_none() {
return false;
};
// All other paths must equal at this index
c.iter().skip(1).all(|p| &p.get(*i) == base)
})
.last();
// Find the shared path
i.map(|i| c[0][i].to_path_buf())
}
#[derive(Debug, Fail)]
pub enum Error {
/// Selecting the API version to use failed.
// TODO: enable `api` hint!
#[fail(display = "failed to select API version to use")]
Version(#[cause] VersionError),
/// An error occurred while archiving the file to upload.
#[cfg(feature = "archive")]
#[fail(display = "failed to archive file to upload")]
@ -280,6 +615,24 @@ pub enum Error {
/// An error occurred while uploading the file.
#[fail(display = "")]
Upload(#[cause] UploadError),
/// An error occurred while deleting a local file after upload.
#[fail(display = "failed to delete local file")]
Delete(#[cause] IoError),
/// An error occurred while reading data from stdin.
#[fail(display = "failed to read data from stdin")]
Stdin(#[cause] StdinErr),
/// An error occurred while creating the temporary stdin file.
#[fail(display = "failed to create temporary stdin buffer file")]
StdinTempFile(#[cause] IoError),
}
impl From<VersionError> for Error {
fn from(err: VersionError) -> Error {
Error::Version(err)
}
}
#[cfg(feature = "archive")]

60
src/action/version.rs Normal file
View file

@ -0,0 +1,60 @@
use clap::ArgMatches;
use ffsend_api::action::version::{Error as VersionError, Version as ApiVersion};
use crate::client::create_config;
use crate::cmd::matcher::main::MainMatcher;
use crate::cmd::matcher::{version::VersionMatcher, Matcher};
use crate::error::ActionError;
/// A file version action.
pub struct Version<'a> {
cmd_matches: &'a ArgMatches<'a>,
}
impl<'a> Version<'a> {
/// Construct a new version action.
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
Self { cmd_matches }
}
/// Invoke the version action.
// TODO: create a trait for this method
pub fn invoke(&self) -> Result<(), ActionError> {
// Create the command matchers
let matcher_version = VersionMatcher::with(self.cmd_matches).unwrap();
let matcher_main = MainMatcher::with(self.cmd_matches).unwrap();
// Get the host
let host = matcher_version.host();
// Create a reqwest client
let client_config = create_config(&matcher_main);
let client = client_config.client(false);
// Make sure the file version
let response = ApiVersion::new(host).invoke(&client);
// Print the result
match response {
Ok(v) => println!("API version: {}", v),
Err(VersionError::Unknown) => println!("Version: unknown"),
Err(VersionError::Unsupported(v)) => println!("Version: {} (unsupported)", v),
Err(e) => return Err(e.into()),
}
Ok(())
}
}
#[derive(Debug, Fail)]
pub enum Error {
/// An error occurred while attempting to determine the Send server version.
#[fail(display = "failed to check the server version")]
Version(#[cause] VersionError),
}
impl From<VersionError> for Error {
fn from(err: VersionError) -> Error {
Error::Version(err)
}
}

View file

@ -1,10 +1,7 @@
use std::io::{
Error as IoError,
Read,
};
use std::io::{Error as IoError, Read};
use std::path::Path;
use super::tar::Archive as TarArchive;
use tar::Archive as TarArchive;
pub type Result<T> = ::std::result::Result<T, IoError>;

View file

@ -1,11 +1,8 @@
use std::fs::File;
use std::io::{
Error as IoError,
Write,
};
use std::io::{Error as IoError, Write};
use std::path::Path;
use super::tar::Builder as TarBuilder;
use tar::Builder as TarBuilder;
pub type Result<T> = ::std::result::Result<T, IoError>;
@ -29,9 +26,9 @@ impl<W: Write> Archiver<W> {
///
/// If no entry exists at the given `src_path`, an error is returned.
pub fn append_path<P, Q>(&mut self, path: P, src_path: Q) -> Result<()>
where
P: AsRef<Path>,
Q: AsRef<Path>,
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
// Append the path as file or directory
if src_path.as_ref().is_file() {
@ -46,8 +43,8 @@ impl<W: Write> Archiver<W> {
/// Append a file to the archive builder.
pub fn append_file<P>(&mut self, path: P, file: &mut File) -> Result<()>
where
P: AsRef<Path>,
where
P: AsRef<Path>,
{
self.inner.append_file(path, file)
}
@ -55,9 +52,9 @@ impl<W: Write> Archiver<W> {
/// Append a directory to the archive builder.
// TODO: Define a flag to add recursively or not
pub fn append_dir<P, Q>(&mut self, path: P, src_path: Q) -> Result<()>
where
P: AsRef<Path>,
Q: AsRef<Path>,
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
self.inner.append_dir_all(path, src_path)
}

View file

@ -1,4 +1,2 @@
extern crate tar;
pub mod archive;
pub mod archiver;

29
src/client.rs Normal file
View file

@ -0,0 +1,29 @@
use std::time::Duration;
use ffsend_api::client::{ClientConfig, ClientConfigBuilder};
use crate::cmd::matcher::MainMatcher;
/// Create a client configuration for ffsend actions.
///
/// A client configuration allows you to build a client, which must be passed to ffsend API
/// actions.
// TODO: properly handle errors, do not unwrap
pub fn create_config(matcher_main: &MainMatcher) -> ClientConfig {
// TODO: configure HTTP authentication properties
ClientConfigBuilder::default()
.timeout(to_duration(matcher_main.timeout()))
.transfer_timeout(to_duration(matcher_main.transfer_timeout()))
.basic_auth(matcher_main.basic_auth())
.build()
.expect("failed to create network client configuration")
}
/// Convert the given number of seconds into an optional duration, used for clients.
pub fn to_duration(secs: u64) -> Option<Duration> {
if secs > 0 {
Some(Duration::from_secs(secs))
} else {
None
}
}

65
src/cmd/arg/api.rs Normal file
View file

@ -0,0 +1,65 @@
use clap::{Arg, ArgMatches};
use ffsend_api::api::{DesiredVersion, Version};
use super::{CmdArg, CmdArgOption};
use crate::config::API_VERSION_DESIRED_DEFAULT;
use crate::util::{quit_error_msg, ErrorHints};
/// The api argument.
pub struct ArgApi {}
impl CmdArg for ArgApi {
fn name() -> &'static str {
"api"
}
fn build<'b, 'c>() -> Arg<'b, 'c> {
Arg::with_name("api")
.long("api")
.short("A")
.value_name("VERSION")
.env("FFSEND_API")
.hide_env_values(true)
.global(true)
.help("Server API version to use, '-' to lookup")
.long_help(
"Server API version to use, one of:\n\
2, 3: Send API versions\n\
auto, -: probe server to determine\
",
)
}
}
impl<'a> CmdArgOption<'a> for ArgApi {
type Value = DesiredVersion;
fn value<'b: 'a>(matches: &'a ArgMatches<'b>) -> Self::Value {
// Get the version string
let version = match Self::value_raw(matches) {
Some(version) => version,
None => return API_VERSION_DESIRED_DEFAULT,
};
// Parse the lookup version string
if is_auto(version) {
return DesiredVersion::Lookup;
}
// Parse the given API version
match Version::parse(version) {
Ok(version) => DesiredVersion::Use(version),
Err(_) => quit_error_msg(
"failed to determine given server API version, version unknown",
ErrorHints::default(),
),
}
}
}
/// Check whether the given API version argument means we've to probe the server for the proper
/// version.
fn is_auto(arg: &str) -> bool {
let arg = arg.trim().to_lowercase();
arg == "a" || arg == "auto" || arg == "-"
}

44
src/cmd/arg/basic_auth.rs Normal file
View file

@ -0,0 +1,44 @@
use clap::{Arg, ArgMatches};
use super::{CmdArg, CmdArgOption};
/// The basicauth argument.
pub struct ArgBasicAuth {}
impl CmdArg for ArgBasicAuth {
fn name() -> &'static str {
"basic-auth"
}
fn build<'b, 'c>() -> Arg<'b, 'c> {
Arg::with_name("basic-auth")
.long("basic-auth")
.alias("basic-authentication")
.alias("http-basic-authentication")
.alias("http-basic-auth")
.value_name("USER:PASSWORD")
.env("FFSEND_BASIC_AUTH")
.hide_env_values(true)
.global(true)
.help("Protected proxy HTTP basic authentication credentials (not FxA)")
}
}
impl<'a> CmdArgOption<'a> for ArgBasicAuth {
type Value = Option<(String, Option<String>)>;
fn value<'b: 'a>(matches: &'a ArgMatches<'b>) -> Self::Value {
// Get the authentication credentials
let raw = match Self::value_raw(matches) {
Some(raw) => raw,
None => return None,
};
// Split the properties
let mut iter = raw.splitn(2, ':');
Some((
iter.next().unwrap_or("").to_owned(),
iter.next().map(|pass| pass.to_owned()),
))
}
}

View file

@ -1,15 +1,60 @@
use clap::{Arg, ArgMatches};
use ffsend_api::action::params::{
PARAMS_DOWNLOAD_MIN as DOWNLOAD_MIN,
PARAMS_DOWNLOAD_MAX as DOWNLOAD_MAX,
};
use ffsend_api::api::Version as ApiVersion;
use ffsend_api::config::downloads_max;
use super::{CmdArg, CmdArgFlag, CmdArgOption};
use util::{ErrorHintsBuilder, quit_error_msg};
use crate::cmd::matcher::MainMatcher;
use crate::util::{highlight, prompt_yes, quit};
/// The download limit argument.
pub struct ArgDownloadLimit { }
pub struct ArgDownloadLimit {}
impl ArgDownloadLimit {
pub fn value_checked<'a>(
matches: &ArgMatches<'a>,
main_matcher: &MainMatcher,
api_version: ApiVersion,
auth: bool,
) -> Option<usize> {
// Get the download value
let mut downloads = Self::value(matches)?;
// Get number of allowed downloads, return if allowed or when forcing
let allowed = downloads_max(api_version, auth);
if allowed.contains(&downloads) || main_matcher.force() {
return Some(downloads);
}
// Prompt the user the specified downloads limit is invalid
let allowed_str = allowed
.iter()
.map(|value| format!("{}", value))
.collect::<Vec<_>>()
.join(", ");
eprintln!("The downloads limit must be one of: {}", allowed_str);
if auth {
eprintln!("Use '{}' to force", highlight("--force"));
} else {
eprintln!(
"Use '{}' to force, authenticate for higher limits",
highlight("--force")
);
}
// Ask to use closest limit, quit if user cancelled
let closest = closest(allowed, downloads);
if !prompt_yes(
&format!("Would you like to limit downloads to {} instead?", closest),
None,
main_matcher,
) {
quit();
}
downloads = closest;
Some(downloads)
}
}
impl CmdArg for ArgDownloadLimit {
fn name() -> &'static str {
@ -23,38 +68,34 @@ impl CmdArg for ArgDownloadLimit {
.alias("downloads")
.alias("download")
.value_name("COUNT")
.env("FFSEND_DOWNLOAD_LIMIT")
.help("The file download limit")
}
}
impl CmdArgFlag for ArgDownloadLimit { }
impl CmdArgFlag for ArgDownloadLimit {}
impl<'a> CmdArgOption<'a> for ArgDownloadLimit {
type Value = Option<u8>;
type Value = Option<usize>;
fn value<'b: 'a>(matches: &'a ArgMatches<'b>) -> Self::Value {
// TODO: do not unwrap, report an error
Self::value_raw(matches)
.map(|d| d.parse::<u8>().expect("invalid download limit"))
.and_then(|d| {
// Check the download limit bounds
// TODO: somehow allow to force a different number here
if d < DOWNLOAD_MIN || d > DOWNLOAD_MAX {
quit_error_msg(
format!(
"invalid download limit, must be between {} and {}",
DOWNLOAD_MIN,
DOWNLOAD_MAX,
),
ErrorHintsBuilder::default()
.force(false)
.verbose(false)
.build()
.unwrap(),
);
}
Some(d)
})
Self::value_raw(matches).map(|d| d.parse::<usize>().expect("invalid download limit"))
}
}
/// Find the closest value to `current` in the given `values` range.
fn closest(values: &[usize], current: usize) -> usize {
// Own the values, sort and reverse, start with biggest first
let mut values = values.to_vec();
values.sort_unstable();
// Find the closest value, return it
*values
.iter()
.rev()
.map(|value| (value, (current as i64 - *value as i64).abs()))
.min_by_key(|value| value.1)
.expect("failed to find closest value, none given")
.0
}

View file

@ -0,0 +1,97 @@
use chrono::Duration;
use clap::{Arg, ArgMatches};
use failure::Fail;
use ffsend_api::api::Version as ApiVersion;
use ffsend_api::config::expiry_max;
use super::{CmdArg, CmdArgFlag, CmdArgOption};
use crate::cmd::matcher::MainMatcher;
use crate::util::{
format_duration, highlight, parse_duration, prompt_yes, quit, quit_error, ErrorHints,
};
/// The download limit argument.
pub struct ArgExpiryTime {}
impl ArgExpiryTime {
pub fn value_checked<'a>(
matches: &ArgMatches<'a>,
main_matcher: &MainMatcher,
api_version: ApiVersion,
auth: bool,
) -> Option<usize> {
// Get the expiry time value
let mut expiry = Self::value(matches)?;
// Get expiry time, return if allowed or when forcing
let max = *expiry_max(api_version, auth).into_iter().max().unwrap();
if expiry <= max || main_matcher.force() {
return Some(expiry);
}
// Define function to format seconds
let format_secs = |secs: usize| format_duration(Duration::seconds(secs as i64));
// Prompt the user the specified expiry time is invalid
eprintln!(
"The expiry time must equal to or less than: {}",
format_secs(max),
);
if auth {
eprintln!("Use '{}' to force", highlight("--force"));
} else {
eprintln!(
"Use '{}' to force, authenticate for higher limits",
highlight("--force")
);
}
// Ask to use maximum expiry time instead, quit if user cancelled
if !prompt_yes(
&format!(
"Would you like to set expiry time to {} instead?",
format_secs(max),
),
None,
main_matcher,
) {
quit();
}
expiry = max;
Some(expiry)
}
}
impl CmdArg for ArgExpiryTime {
fn name() -> &'static str {
"expiry-time"
}
fn build<'b, 'c>() -> Arg<'b, 'c> {
Arg::with_name("expiry-time")
.long("expiry-time")
.short("e")
.alias("expire")
.alias("expiry")
.value_name("TIME")
.env("FFSEND_EXPIRY_TIME")
.help("The file expiry time")
}
}
impl CmdArgFlag for ArgExpiryTime {}
impl<'a> CmdArgOption<'a> for ArgExpiryTime {
type Value = Option<usize>;
fn value<'b: 'a>(matches: &'a ArgMatches<'b>) -> Self::Value {
Self::value_raw(matches).map(|t| match parse_duration(t) {
Ok(seconds) => seconds,
Err(err) => quit_error(
err.context("specified invalid file expiry time"),
ErrorHints::default(),
),
})
}
}

View file

@ -0,0 +1,34 @@
use chbs;
use clap::Arg;
use super::{CmdArg, CmdArgFlag};
/// The passphrase generation argument.
pub struct ArgGenPassphrase {}
impl ArgGenPassphrase {
/// Generate a cryptographically secure passphrase that is easily
/// remembered using diceware.
pub fn gen_passphrase() -> String {
chbs::passphrase()
}
}
impl CmdArg for ArgGenPassphrase {
fn name() -> &'static str {
"gen-passphrase"
}
fn build<'b, 'c>() -> Arg<'b, 'c> {
Arg::with_name("gen-passphrase")
.long("gen-passphrase")
.alias("gen-password")
.alias("generate-passphrase")
.alias("generate-password")
.short("P")
.conflicts_with("password")
.help("Protect the file with a generated passphrase")
}
}
impl CmdArgFlag for ArgGenPassphrase {}

View file

@ -1,14 +1,13 @@
use clap::{Arg, ArgMatches};
use failure::Fail;
use ffsend_api::config::SEND_DEFAULT_HOST;
use ffsend_api::url::Url;
use host::parse_host;
use super::{CmdArg, CmdArgOption};
use util::{ErrorHints, quit_error};
use crate::host::parse_host;
use crate::util::{quit_error, ErrorHints};
/// The host argument.
pub struct ArgHost { }
pub struct ArgHost {}
impl CmdArg for ArgHost {
fn name() -> &'static str {
@ -20,7 +19,7 @@ impl CmdArg for ArgHost {
.long("host")
.short("h")
.value_name("URL")
.default_value(SEND_DEFAULT_HOST)
.default_value("https://send.vis.ee/")
.env("FFSEND_HOST")
.hide_env_values(true)
.help("The remote host to upload to")

View file

@ -1,11 +1,19 @@
pub mod api;
pub mod basic_auth;
pub mod download_limit;
pub mod expiry_time;
pub mod gen_passphrase;
pub mod host;
pub mod owner;
pub mod password;
pub mod url;
// Reexport to arg module
// Re-export to arg module
pub use self::api::ArgApi;
pub use self::basic_auth::ArgBasicAuth;
pub use self::download_limit::ArgDownloadLimit;
pub use self::expiry_time::ArgExpiryTime;
pub use self::gen_passphrase::ArgGenPassphrase;
pub use self::host::ArgHost;
pub use self::owner::ArgOwner;
pub use self::password::ArgPassword;
@ -45,5 +53,6 @@ pub trait CmdArgOption<'a>: CmdArg {
/// Get the raw argument value, as a string reference.
fn value_raw<'b: 'a>(matches: &'a ArgMatches<'b>) -> Option<&'a str> {
matches.value_of(Self::name()) }
matches.value_of(Self::name())
}
}

View file

@ -1,11 +1,11 @@
use clap::{Arg, ArgMatches};
use cmd::matcher::{MainMatcher, Matcher};
use super::{CmdArg, CmdArgFlag, CmdArgOption};
use util::prompt_owner_token;
use crate::cmd::matcher::{MainMatcher, Matcher};
use crate::util::prompt_owner_token;
/// The owner argument.
pub struct ArgOwner { }
pub struct ArgOwner {}
impl CmdArg for ArgOwner {
fn name() -> &'static str {
@ -24,7 +24,7 @@ impl CmdArg for ArgOwner {
}
}
impl CmdArgFlag for ArgOwner { }
impl CmdArgFlag for ArgOwner {}
impl<'a> CmdArgOption<'a> for ArgOwner {
type Value = Option<String>;
@ -37,7 +37,7 @@ impl<'a> CmdArgOption<'a> for ArgOwner {
// Get the owner token from the argument if set
match Self::value_raw(matches) {
None => {},
None => {}
p => return p.map(|p| p.into()),
}
@ -45,6 +45,7 @@ impl<'a> CmdArgOption<'a> for ArgOwner {
let matcher_main = MainMatcher::with(matches).unwrap();
// Prompt for the owner token
Some(prompt_owner_token(&matcher_main))
// TODO: should this be optional?
Some(prompt_owner_token(&matcher_main, false))
}
}

View file

@ -1,11 +1,11 @@
use clap::{Arg, ArgMatches};
use cmd::matcher::{MainMatcher, Matcher};
use super::{CmdArg, CmdArgFlag, CmdArgOption};
use util::{check_empty_password, prompt_password};
use crate::cmd::matcher::{MainMatcher, Matcher};
use crate::util::{check_empty_password, prompt_password};
/// The password argument.
pub struct ArgPassword { }
pub struct ArgPassword {}
impl CmdArg for ArgPassword {
fn name() -> &'static str {
@ -23,7 +23,7 @@ impl CmdArg for ArgPassword {
}
}
impl CmdArgFlag for ArgPassword { }
impl CmdArgFlag for ArgPassword {}
impl<'a> CmdArgOption<'a> for ArgPassword {
type Value = Option<String>;
@ -40,7 +40,7 @@ impl<'a> CmdArgOption<'a> for ArgPassword {
// Get the password argument value, or prompt
let password = match Self::value_raw(matches) {
Some(password) => password.into(),
None => prompt_password(&matcher_main),
None => prompt_password(&matcher_main, false).unwrap(),
};
// Check for empty passwords

View file

@ -2,12 +2,12 @@ use clap::{Arg, ArgMatches};
use failure::Fail;
use ffsend_api::url::Url;
use host::parse_host;
use super::{CmdArg, CmdArgOption};
use util::{ErrorHints, quit_error};
use crate::host::parse_host;
use crate::util::{quit_error, ErrorHints};
/// The URL argument.
pub struct ArgUrl { }
pub struct ArgUrl {}
impl CmdArg for ArgUrl {
fn name() -> &'static str {

View file

@ -1,34 +1,29 @@
extern crate directories;
#[cfg(feature = "infer-command")]
use std::ffi::OsString;
use clap::{App, AppSettings, Arg, ArgMatches};
use super::matcher::{
DebugMatcher,
DeleteMatcher,
DownloadMatcher,
ExistsMatcher,
InfoMatcher,
Matcher,
ParamsMatcher,
PasswordMatcher,
UploadMatcher,
};
use super::arg::{ArgApi, ArgBasicAuth, CmdArg};
#[cfg(feature = "history")]
use super::matcher::HistoryMatcher;
use super::subcmd::{
CmdDebug,
CmdDelete,
CmdDownload,
CmdExists,
CmdInfo,
CmdParams,
CmdPassword,
CmdUpload,
use super::matcher::{
DebugMatcher, DeleteMatcher, DownloadMatcher, ExistsMatcher, GenerateMatcher, InfoMatcher,
Matcher, ParamsMatcher, PasswordMatcher, UploadMatcher, VersionMatcher,
};
#[cfg(feature = "history")]
use super::subcmd::CmdHistory;
use super::subcmd::{
CmdDebug, CmdDelete, CmdDownload, CmdExists, CmdGenerate, CmdInfo, CmdParams, CmdPassword,
CmdUpload, CmdVersion,
};
#[cfg(feature = "infer-command")]
use crate::config::INFER_COMMANDS;
use crate::config::{CLIENT_TIMEOUT, CLIENT_TRANSFER_TIMEOUT};
#[cfg(feature = "history")]
use util::app_history_file_path_string;
use crate::util::app_history_file_path_string;
#[cfg(feature = "infer-command")]
use crate::util::bin_name;
use crate::util::parse_duration;
#[cfg(feature = "history")]
lazy_static! {
@ -36,6 +31,23 @@ lazy_static! {
static ref DEFAULT_HISTORY_FILE: String = app_history_file_path_string();
}
lazy_static! {
/// The default client timeout in seconds as a string
static ref DEFAULT_TIMEOUT: String = format!("{}", CLIENT_TIMEOUT);
/// The default client transfer timeout in seconds as a string
static ref DEFAULT_TRANSFER_TIMEOUT: String = format!("{}", CLIENT_TRANSFER_TIMEOUT);
/// The about notice in command output.
static ref APP_ABOUT: String = format!(
"{}\n\n\
The default public Send host is provided by Tim Visee, @timvisee.\n\
Please consider to donate and help keep it running: https://vis.ee/donate\
",
crate_description!(),
);
}
/// CLI argument handler.
pub struct Handler<'a> {
/// The CLI matches.
@ -49,67 +61,134 @@ impl<'a: 'b, 'b> Handler<'a> {
let app = App::new(crate_name!())
.version(crate_version!())
.author(crate_authors!())
.about(crate_description!())
.after_help("\
The public Send service that is used as default host is provided by Mozilla.\n\
This application is not affiliated with Mozilla, Firefox or Firefox Send.\
")
.about(APP_ABOUT.as_ref())
.after_help("This application is not affiliated with Firefox or Mozilla.")
.global_setting(AppSettings::GlobalVersion)
.global_setting(AppSettings::VersionlessSubcommands)
// TODO: enable below command when it doesn't break `p` anymore.
// .global_setting(AppSettings::InferSubcommands)
.arg(Arg::with_name("force")
.long("force")
.short("f")
.global(true)
.help("Force the action, ignore warnings"))
.arg(Arg::with_name("no-interact")
.long("no-interact")
.short("I")
.alias("no-interactive")
.global(true)
.help("Not interactive, do not prompt"))
.arg(Arg::with_name("yes")
.long("yes")
.short("y")
.alias("assume-yes")
.global(true)
.help("Assume yes for prompts"))
.arg(Arg::with_name("verbose")
.long("verbose")
.short("v")
.multiple(true)
.global(true)
.help("Enable verbose information and logging"))
.arg(
Arg::with_name("force")
.long("force")
.short("f")
.global(true)
.help("Force the action, ignore warnings"),
)
.arg(
Arg::with_name("no-interact")
.long("no-interact")
.short("I")
.alias("no-interactive")
.alias("non-interactive")
.global(true)
.help("Not interactive, do not prompt"),
)
.arg(
Arg::with_name("yes")
.long("yes")
.short("y")
.alias("assume-yes")
.global(true)
.help("Assume yes for prompts"),
)
.arg(
Arg::with_name("timeout")
.long("timeout")
.short("t")
.alias("time")
.global(true)
.value_name("SECONDS")
.help("Request timeout (0 to disable)")
.default_value(&DEFAULT_TIMEOUT)
.hide_default_value(true)
.env("FFSEND_TIMEOUT")
.hide_env_values(true)
.validator(|arg| {
parse_duration(&arg).map(drop).map_err(|_| {
String::from(
"Timeout time must be a positive number of seconds, or 0 to disable."
)
})
}),
)
.arg(
Arg::with_name("transfer-timeout")
.long("transfer-timeout")
.short("T")
.alias("trans-time")
.alias("trans-timeout")
.alias("transfer-time")
.alias("time-trans")
.alias("timeout-trans")
.alias("time-transfer")
.global(true)
.value_name("SECONDS")
.help("Transfer timeout (0 to disable)")
.default_value(&DEFAULT_TRANSFER_TIMEOUT)
.hide_default_value(true)
.env("FFSEND_TRANSFER_TIMEOUT")
.hide_env_values(true)
.validator(|arg| {
parse_duration(&arg).map(drop).map_err(|_| {
String::from(
"Timeout time must be a positive number of seconds, or 0 to disable."
)
})
}),
)
.arg(
Arg::with_name("quiet")
.long("quiet")
.short("q")
.global(true)
.help("Produce output suitable for logging and automation"),
)
.arg(
Arg::with_name("verbose")
.long("verbose")
.short("v")
.multiple(true)
.global(true)
.help("Enable verbose information and logging"),
)
.arg(ArgApi::build())
.arg(ArgBasicAuth::build())
.subcommand(CmdDebug::build())
.subcommand(CmdDelete::build())
.subcommand(CmdDownload::build().display_order(2))
.subcommand(CmdExists::build())
.subcommand(CmdGenerate::build())
.subcommand(CmdInfo::build())
.subcommand(CmdParams::build())
.subcommand(CmdPassword::build())
.subcommand(CmdUpload::build().display_order(1));
.subcommand(CmdUpload::build().display_order(1))
.subcommand(CmdVersion::build());
// With history support, a flag for the history file and incognito mode
#[cfg(feature = "history")]
let app = app.arg(Arg::with_name("history")
.long("history")
.short("H")
.value_name("FILE")
.global(true)
.help("Use the specified history file")
.default_value(&DEFAULT_HISTORY_FILE)
.hide_default_value(true)
.env("FFSEND_HISTORY")
.hide_env_values(true))
.arg(Arg::with_name("incognito")
.long("incognito")
.short("i")
.alias("incog")
.alias("private")
.alias("priv")
.global(true)
.help("Don't update local history for actions"))
let app = app
.arg(
Arg::with_name("history")
.long("history")
.short("H")
.value_name("FILE")
.global(true)
.help("Use the specified history file")
.default_value(&DEFAULT_HISTORY_FILE)
.hide_default_value(true)
.env("FFSEND_HISTORY")
.hide_env_values(true),
)
.arg(
Arg::with_name("incognito")
.long("incognito")
.short("i")
.alias("incog")
.alias("private")
.alias("priv")
.global(true)
.help("Don't update local history for actions"),
)
.subcommand(CmdHistory::build());
// Disable color usage if compiled without color support
@ -121,9 +200,40 @@ impl<'a: 'b, 'b> Handler<'a> {
/// Parse CLI arguments.
pub fn parse() -> Handler<'a> {
// Obtain the program arguments
#[allow(unused_mut)]
let mut args: Vec<_> = ::std::env::args_os().collect();
// Infer subcommand based on binary name
#[cfg(feature = "infer-command")]
Self::infer_subcommand(&mut args);
// Build the application CLI definition, get the matches
Handler {
matches: Handler::build().get_matches(),
matches: Handler::build().get_matches_from(args),
}
}
/// Infer subcommand when the binary has a predefined name,
/// modifying the given program arguments.
///
/// If no subcommand could be inferred, the `args` list leaves unchanged.
/// See `crate::config::INFER_COMMANDS` for a list of commands.
///
/// When the `ffsend` binary is called with such a name, the corresponding subcommand is
/// automatically inserted as argument. This also works when calling binaries through symbolic
/// or hard links.
#[cfg(feature = "infer-command")]
fn infer_subcommand(args: &mut Vec<OsString>) {
// Get the name of the called binary
let name = bin_name();
// Infer subcommands
for (bin, subcmd) in INFER_COMMANDS.iter() {
if &name == bin {
args.insert(1, subcmd.into());
break;
}
}
}
@ -152,6 +262,11 @@ impl<'a: 'b, 'b> Handler<'a> {
ExistsMatcher::with(&self.matches)
}
/// Get the generate sub command, if matched.
pub fn generate(&'a self) -> Option<GenerateMatcher> {
GenerateMatcher::with(&self.matches)
}
/// Get the history sub command, if matched.
#[cfg(feature = "history")]
pub fn history(&'a self) -> Option<HistoryMatcher> {
@ -177,4 +292,9 @@ impl<'a: 'b, 'b> Handler<'a> {
pub fn upload(&'a self) -> Option<UploadMatcher> {
UploadMatcher::with(&self.matches)
}
/// Get the version sub command, if matched.
pub fn version(&'a self) -> Option<VersionMatcher> {
VersionMatcher::with(&self.matches)
}
}

View file

@ -1,8 +1,8 @@
use clap::ArgMatches;
use ffsend_api::url::Url;
use cmd::arg::{ArgHost, CmdArgOption};
use super::Matcher;
use crate::cmd::arg::{ArgHost, CmdArgOption};
/// The debug command matcher.
pub struct DebugMatcher<'a> {
@ -22,11 +22,8 @@ impl<'a: 'b, 'b> DebugMatcher<'a> {
impl<'a> Matcher<'a> for DebugMatcher<'a> {
fn with(matches: &'a ArgMatches) -> Option<Self> {
matches.subcommand_matches("debug")
.map(|matches|
DebugMatcher {
matches,
}
)
matches
.subcommand_matches("debug")
.map(|matches| DebugMatcher { matches })
}
}

View file

@ -1,8 +1,8 @@
use clap::ArgMatches;
use ffsend_api::url::Url;
use cmd::arg::{ArgOwner, ArgUrl, CmdArgOption};
use super::Matcher;
use crate::cmd::arg::{ArgOwner, ArgUrl, CmdArgOption};
/// The delete command matcher.
pub struct DeleteMatcher<'a> {
@ -22,18 +22,14 @@ impl<'a: 'b, 'b> DeleteMatcher<'a> {
/// Get the owner token.
pub fn owner(&'a self) -> Option<String> {
// TODO: just return a string reference here?
ArgOwner::value(self.matches)
.map(|token| token.to_owned())
ArgOwner::value(self.matches).map(|token| token.to_owned())
}
}
impl<'a> Matcher<'a> for DeleteMatcher<'a> {
fn with(matches: &'a ArgMatches) -> Option<Self> {
matches.subcommand_matches("delete")
.map(|matches|
DeleteMatcher {
matches,
}
)
matches
.subcommand_matches("delete")
.map(|matches| DeleteMatcher { matches })
}
}

View file

@ -3,10 +3,10 @@ use std::path::PathBuf;
use clap::ArgMatches;
use ffsend_api::url::Url;
use cmd::arg::{ArgPassword, ArgUrl, CmdArgOption};
use super::Matcher;
use crate::cmd::arg::{ArgPassword, ArgUrl, CmdArgOption};
#[cfg(feature = "archive")]
use util::env_var_present;
use crate::util::env_var_present;
/// The download command matcher.
pub struct DownloadMatcher<'a> {
@ -23,6 +23,17 @@ impl<'a: 'b, 'b> DownloadMatcher<'a> {
ArgUrl::value(self.matches)
}
/// Guess the file share host, based on the file share URL.
///
/// See `Self::url`.
pub fn guess_host(&'a self, url: Option<Url>) -> Url {
let mut url = url.unwrap_or(self.url());
url.set_path("");
url.set_query(None);
url.set_fragment(None);
url
}
/// Get the password.
/// `None` is returned if no password was specified.
pub fn password(&'a self) -> Option<String> {
@ -33,7 +44,8 @@ impl<'a: 'b, 'b> DownloadMatcher<'a> {
/// If a directory is given, the file name of the original uploaded file
/// will be used.
pub fn output(&'a self) -> PathBuf {
self.matches.value_of("output")
self.matches
.value_of("output")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("./"))
}
@ -47,11 +59,8 @@ impl<'a: 'b, 'b> DownloadMatcher<'a> {
impl<'a> Matcher<'a> for DownloadMatcher<'a> {
fn with(matches: &'a ArgMatches) -> Option<Self> {
matches.subcommand_matches("download")
.map(|matches|
DownloadMatcher {
matches,
}
)
matches
.subcommand_matches("download")
.map(|matches| DownloadMatcher { matches })
}
}

View file

@ -2,8 +2,8 @@ use ffsend_api::url::Url;
use clap::ArgMatches;
use cmd::arg::{ArgUrl, CmdArgOption};
use super::Matcher;
use crate::cmd::arg::{ArgUrl, CmdArgOption};
/// The exists command matcher.
pub struct ExistsMatcher<'a> {
@ -23,11 +23,8 @@ impl<'a: 'b, 'b> ExistsMatcher<'a> {
impl<'a> Matcher<'a> for ExistsMatcher<'a> {
fn with(matches: &'a ArgMatches) -> Option<Self> {
matches.subcommand_matches("exists")
.map(|matches|
ExistsMatcher {
matches,
}
)
matches
.subcommand_matches("exists")
.map(|matches| ExistsMatcher { matches })
}
}

View file

@ -0,0 +1,64 @@
use std::path::PathBuf;
use std::str::FromStr;
use clap::{ArgMatches, Shell};
use super::Matcher;
/// The completions completions command matcher.
pub struct CompletionsMatcher<'a> {
matches: &'a ArgMatches<'a>,
}
impl<'a: 'b, 'b> CompletionsMatcher<'a> {
/// Get the shells to generate completions for.
pub fn shells(&'a self) -> Vec<Shell> {
// Get the raw list of shells
let raw = self
.matches
.values_of("SHELL")
.expect("no shells were given");
// Parse the list of shell names, deduplicate
let mut shells: Vec<_> = raw
.into_iter()
.map(|name| name.trim().to_lowercase())
.map(|name| {
if name == "all" {
Shell::variants()
.iter()
.map(|name| name.to_string())
.collect()
} else {
vec![name]
}
})
.flatten()
.collect();
shells.sort_unstable();
shells.dedup();
// Parse the shell names
shells
.into_iter()
.map(|name| Shell::from_str(&name).expect("failed to parse shell name"))
.collect()
}
/// The target directory to output the shell completion files to.
pub fn output(&'a self) -> PathBuf {
self.matches
.value_of("output")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("./"))
}
}
impl<'a> Matcher<'a> for CompletionsMatcher<'a> {
fn with(matches: &'a ArgMatches) -> Option<Self> {
matches
.subcommand_matches("generate")?
.subcommand_matches("completions")
.map(|matches| CompletionsMatcher { matches })
}
}

View file

@ -0,0 +1,29 @@
pub mod completions;
use clap::ArgMatches;
use super::Matcher;
use completions::CompletionsMatcher;
/// The generate command matcher.
pub struct GenerateMatcher<'a> {
root: &'a ArgMatches<'a>,
_matches: &'a ArgMatches<'a>,
}
impl<'a: 'b, 'b> GenerateMatcher<'a> {
/// Get the generate completions sub command, if matched.
pub fn matcher_completions(&'a self) -> Option<CompletionsMatcher> {
CompletionsMatcher::with(&self.root)
}
}
impl<'a> Matcher<'a> for GenerateMatcher<'a> {
fn with(root: &'a ArgMatches) -> Option<Self> {
root.subcommand_matches("generate")
.map(|matches| GenerateMatcher {
root,
_matches: matches,
})
}
}

View file

@ -1,6 +1,10 @@
use clap::ArgMatches;
use failure::Fail;
use ffsend_api::url::Url;
use super::Matcher;
use crate::host::parse_host;
use crate::util::{quit_error, ErrorHints};
/// The history command matcher.
pub struct HistoryMatcher<'a> {
@ -8,13 +12,36 @@ pub struct HistoryMatcher<'a> {
matches: &'a ArgMatches<'a>,
}
impl<'a> Matcher<'a> for HistoryMatcher<'a> {
fn with(matches: &'a ArgMatches) -> Option<Self> {
matches.subcommand_matches("history")
.map(|matches|
HistoryMatcher {
matches,
}
)
impl<'a> HistoryMatcher<'a> {
/// Check whether to clear all history.
pub fn clear(&self) -> bool {
self.matches.is_present("clear")
}
/// Check whether to remove a given entry from the history.
///
/// This method parses the URL into an `Url`.
/// If the given URL is invalid,
/// the program will quit with an error message.
pub fn rm(&'a self) -> Option<Url> {
// Get the URL
let url = self.matches.value_of("rm")?;
// Parse the URL
match parse_host(&url) {
Ok(url) => Some(url),
Err(err) => quit_error(
err.context("failed to parse the given share URL"),
ErrorHints::default(),
),
}
}
}
impl<'a> Matcher<'a> for HistoryMatcher<'a> {
fn with(matches: &'a ArgMatches) -> Option<Self> {
matches
.subcommand_matches("history")
.map(|matches| HistoryMatcher { matches })
}
}

View file

@ -2,8 +2,8 @@ use ffsend_api::url::Url;
use clap::ArgMatches;
use cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArgOption};
use super::Matcher;
use crate::cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArgOption};
/// The info command matcher.
pub struct InfoMatcher<'a> {
@ -23,8 +23,7 @@ impl<'a: 'b, 'b> InfoMatcher<'a> {
/// Get the owner token.
pub fn owner(&'a self) -> Option<String> {
// TODO: just return a string reference here?
ArgOwner::value(self.matches)
.map(|token| token.to_owned())
ArgOwner::value(self.matches).map(|token| token.to_owned())
}
/// Get the password.
@ -36,11 +35,8 @@ impl<'a: 'b, 'b> InfoMatcher<'a> {
impl<'a> Matcher<'a> for InfoMatcher<'a> {
fn with(matches: &'a ArgMatches) -> Option<Self> {
matches.subcommand_matches("info")
.map(|matches|
InfoMatcher {
matches,
}
)
matches
.subcommand_matches("info")
.map(|matches| InfoMatcher { matches })
}
}

View file

@ -2,11 +2,13 @@
use std::path::PathBuf;
use clap::ArgMatches;
use ffsend_api::api::DesiredVersion;
use super::Matcher;
use util::env_var_present;
use crate::cmd::arg::{ArgApi, ArgBasicAuth, CmdArgOption};
use crate::util::{env_var_present, parse_duration};
#[cfg(feature = "history")]
use util::{ErrorHintsBuilder, quit_error_msg};
use crate::util::{quit_error_msg, ErrorHintsBuilder};
/// The main command matcher.
pub struct MainMatcher<'a> {
@ -29,12 +31,21 @@ impl<'a: 'b, 'b> MainMatcher<'a> {
self.matches.is_present("yes") || env_var_present("FFSEND_YES")
}
/// Get the desired API version to use.
pub fn api(&'a self) -> DesiredVersion {
ArgApi::value(self.matches)
}
/// Get basic HTTP authentication credentials to use.
pub fn basic_auth(&'a self) -> Option<(String, Option<String>)> {
ArgBasicAuth::value(self.matches)
}
/// Get the history file to use.
#[cfg(feature = "history")]
pub fn history(&self) -> PathBuf {
// Get the path
let path = self.matches.value_of("history")
.map(PathBuf::from);
let path = self.matches.value_of("history").map(PathBuf::from);
// Ensure the path is correct
match path {
@ -50,12 +61,33 @@ impl<'a: 'b, 'b> MainMatcher<'a> {
}
}
/// Get the timeout in seconds
pub fn timeout(&self) -> u64 {
self.matches
.value_of("timeout")
.and_then(|arg| parse_duration(arg).ok())
.expect("invalid timeout value") as u64
}
/// Get the transfer timeout in seconds
pub fn transfer_timeout(&self) -> u64 {
self.matches
.value_of("transfer-timeout")
.and_then(|arg| parse_duration(arg).ok())
.expect("invalid transfer-timeout value") as u64
}
/// Check whether we are incognito from the file history.
#[cfg(feature = "history")]
pub fn incognito(&self) -> bool {
self.matches.is_present("incognito") || env_var_present("FFSEND_INCOGNITO")
}
/// Check whether quiet mode is used.
pub fn quiet(&self) -> bool {
!self.verbose() && (self.matches.is_present("quiet") || env_var_present("FFSEND_QUIET"))
}
/// Check whether verbose mode is used.
pub fn verbose(&self) -> bool {
self.matches.is_present("verbose") || env_var_present("FFSEND_VERBOSE")
@ -64,10 +96,6 @@ impl<'a: 'b, 'b> MainMatcher<'a> {
impl<'a> Matcher<'a> for MainMatcher<'a> {
fn with(matches: &'a ArgMatches) -> Option<Self> {
Some(
MainMatcher {
matches,
}
)
Some(MainMatcher { matches })
}
}

View file

@ -2,6 +2,7 @@ pub mod debug;
pub mod delete;
pub mod download;
pub mod exists;
pub mod generate;
#[cfg(feature = "history")]
pub mod history;
pub mod info;
@ -9,19 +10,22 @@ pub mod main;
pub mod params;
pub mod password;
pub mod upload;
pub mod version;
// Reexport to matcher module
// Re-export to matcher module
pub use self::debug::DebugMatcher;
pub use self::delete::DeleteMatcher;
pub use self::download::DownloadMatcher;
pub use self::exists::ExistsMatcher;
pub use self::generate::GenerateMatcher;
#[cfg(feature = "history")]
pub use self::history::HistoryMatcher;
pub use self::info::InfoMatcher;
pub use self::main::MainMatcher;
pub use self::params::ParamsMatcher;
pub use self::password::PasswordMatcher;
pub use self::upload::UploadMatcher;
pub use self::upload::{CopyMode, UploadMatcher};
pub use self::version::VersionMatcher;
use clap::ArgMatches;

View file

@ -1,8 +1,12 @@
use clap::ArgMatches;
use ffsend_api::api::Version as ApiVersion;
use ffsend_api::url::Url;
use cmd::arg::{ArgDownloadLimit, ArgOwner, ArgUrl, CmdArgOption};
use super::Matcher;
use crate::cmd::{
arg::{ArgDownloadLimit, ArgOwner, ArgUrl, CmdArgOption},
matcher::MainMatcher,
};
/// The params command matcher.
pub struct ParamsMatcher<'a> {
@ -22,23 +26,27 @@ impl<'a: 'b, 'b> ParamsMatcher<'a> {
/// Get the owner token.
pub fn owner(&'a self) -> Option<String> {
// TODO: just return a string reference here?
ArgOwner::value(self.matches)
.map(|token| token.to_owned())
ArgOwner::value(self.matches).map(|token| token.to_owned())
}
/// Get the download limit.
pub fn download_limit(&'a self) -> Option<u8> {
ArgDownloadLimit::value(self.matches)
///
/// If the download limit was the default, `None` is returned to not
/// explicitly set it.
pub fn download_limit(
&'a self,
main_matcher: &MainMatcher,
api_version: ApiVersion,
auth: bool,
) -> Option<usize> {
ArgDownloadLimit::value_checked(self.matches, main_matcher, api_version, auth)
}
}
impl<'a> Matcher<'a> for ParamsMatcher<'a> {
fn with(matches: &'a ArgMatches) -> Option<Self> {
matches.subcommand_matches("parameters")
.map(|matches|
ParamsMatcher {
matches,
}
)
matches
.subcommand_matches("parameters")
.map(|matches| ParamsMatcher { matches })
}
}

View file

@ -2,9 +2,9 @@ use clap::ArgMatches;
use ffsend_api::url::Url;
use rpassword::prompt_password_stderr;
use cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArgOption};
use cmd::matcher::{MainMatcher, Matcher};
use util::check_empty_password;
use crate::cmd::arg::{ArgGenPassphrase, ArgOwner, ArgPassword, ArgUrl, CmdArgFlag, CmdArgOption};
use crate::cmd::matcher::{MainMatcher, Matcher};
use crate::util::check_empty_password;
/// The password command matcher.
pub struct PasswordMatcher<'a> {
@ -24,12 +24,19 @@ impl<'a: 'b, 'b> PasswordMatcher<'a> {
/// Get the owner token.
pub fn owner(&'a self) -> Option<String> {
// TODO: just return a string reference here?
ArgOwner::value(self.matches)
.map(|token| token.to_owned())
ArgOwner::value(self.matches).map(|token| token.to_owned())
}
/// Get the password.
pub fn password(&'a self) -> String {
///
/// The password is returned in the following format:
/// `(password, generated)`
pub fn password(&'a self) -> (String, bool) {
// Generate a passphrase if requested
if ArgGenPassphrase::is_present(self.matches) {
return (ArgGenPassphrase::gen_passphrase(), true);
}
// Get the password, or prompt for it
let password = match ArgPassword::value(self.matches) {
Some(password) => password,
@ -39,7 +46,7 @@ impl<'a: 'b, 'b> PasswordMatcher<'a> {
// TODO: create utility function for this
prompt_password_stderr("New password: ")
.expect("failed to read password from stdin")
},
}
};
// Create a main matcher
@ -48,17 +55,14 @@ impl<'a: 'b, 'b> PasswordMatcher<'a> {
// Check for empty passwords
check_empty_password(&password, &matcher_main);
password
(password, false)
}
}
impl<'a> Matcher<'a> for PasswordMatcher<'a> {
fn with(matches: &'a ArgMatches) -> Option<Self> {
matches.subcommand_matches("password")
.map(|matches|
PasswordMatcher {
matches,
}
)
matches
.subcommand_matches("password")
.map(|matches| PasswordMatcher { matches })
}
}

View file

@ -1,12 +1,15 @@
use clap::ArgMatches;
use ffsend_api::action::params::{
PARAMS_DEFAULT_DOWNLOAD as DOWNLOAD_DEFAULT,
};
use ffsend_api::url::Url;
use ffsend_api::{api::Version as ApiVersion, config, url::Url};
use cmd::arg::{ArgDownloadLimit, ArgHost, ArgPassword, CmdArgOption};
use super::Matcher;
use util::{env_var_present, ErrorHintsBuilder, quit_error_msg};
use crate::cmd::{
arg::{
ArgDownloadLimit, ArgExpiryTime, ArgGenPassphrase, ArgHost, ArgPassword, CmdArgFlag,
CmdArgOption,
},
matcher::MainMatcher,
};
use crate::util::{bin_name, env_var_present, quit_error_msg, ErrorHintsBuilder};
/// The upload command matcher.
pub struct UploadMatcher<'a> {
@ -16,9 +19,11 @@ pub struct UploadMatcher<'a> {
impl<'a: 'b, 'b> UploadMatcher<'a> {
/// Get the selected file to upload.
// TODO: maybe return a file or path instance here
pub fn file(&'a self) -> &'a str {
self.matches.value_of("FILE")
pub fn files(&'a self) -> Vec<&'a str> {
self.matches
.values_of("FILE")
.expect("no file specified to upload")
.collect()
}
/// The the name to use for the uploaded file.
@ -30,7 +35,7 @@ impl<'a: 'b, 'b> UploadMatcher<'a> {
let name = self.matches.value_of("name")?;
// The file name must not be empty
// TODO: allow to force an empty name here, and process emtpy names on downloading
// TODO: allow to force an empty name here, and process empty names on downloading
if name.trim().is_empty() {
quit_error_msg(
"the file name must not be empty",
@ -55,20 +60,50 @@ impl<'a: 'b, 'b> UploadMatcher<'a> {
}
/// Get the password.
/// `None` is returned if no password was specified.
pub fn password(&'a self) -> Option<String> {
ArgPassword::value(self.matches)
/// A generated passphrase will be returned if the user requested so,
/// otherwise the specified password is returned.
/// If no password was set, `None` is returned instead.
///
/// The password is returned in the following format:
/// `(password, generated)`
pub fn password(&'a self) -> Option<(String, bool)> {
// Generate a passphrase if requested
if ArgGenPassphrase::is_present(self.matches) {
return Some((ArgGenPassphrase::gen_passphrase(), true));
}
// Use a specified password or use nothing
ArgPassword::value(self.matches).map(|password| (password, false))
}
/// Get the download limit.
///
/// If the download limit was the default, `None` is returned to not
/// explicitly set it.
pub fn download_limit(&'a self) -> Option<u8> {
ArgDownloadLimit::value(self.matches)
.and_then(|d| match d {
DOWNLOAD_DEFAULT => None,
pub fn download_limit(
&'a self,
main_matcher: &MainMatcher,
api_version: ApiVersion,
auth: bool,
) -> Option<usize> {
ArgDownloadLimit::value_checked(self.matches, main_matcher, api_version, auth).and_then(
|d| match d {
d if d == config::downloads_default(api_version, auth) => None,
d => Some(d),
})
},
)
}
/// Get the expiry time in seconds.
///
/// If the expiry time was not set, `None` is returned.
pub fn expiry_time(
&'a self,
main_matcher: &MainMatcher,
api_version: ApiVersion,
auth: bool,
) -> Option<usize> {
ArgExpiryTime::value_checked(self.matches, main_matcher, api_version, auth)
}
/// Check whether to archive the file to upload.
@ -82,20 +117,65 @@ impl<'a: 'b, 'b> UploadMatcher<'a> {
self.matches.is_present("open") || env_var_present("FFSEND_OPEN")
}
/// Check whether to copy the file URL in the user's clipboard.
/// Check whether to to delete local files after uploading.
pub fn delete(&self) -> bool {
self.matches.is_present("delete")
}
/// Check whether to copy the file URL in the user's clipboard, get the copy mode.
#[cfg(feature = "clipboard")]
pub fn copy(&self) -> bool {
self.matches.is_present("copy") || env_var_present("FFSEND_COPY")
pub fn copy(&self) -> Option<CopyMode> {
// Get the options
let copy = self.matches.is_present("copy") || env_var_present("FFSEND_COPY");
let copy_cmd = self.matches.is_present("copy-cmd") || env_var_present("FFSEND_COPY_CMD");
// Return the corresponding copy mode
if copy_cmd {
Some(CopyMode::DownloadCmd)
} else if copy {
Some(CopyMode::Url)
} else {
None
}
}
/// Check whether to shorten a share URL
#[cfg(feature = "urlshorten")]
pub fn shorten(&self) -> bool {
self.matches.is_present("shorten")
}
/// Check whether to print a QR code for the share URL.
#[cfg(feature = "qrcode")]
pub fn qrcode(&self) -> bool {
self.matches.is_present("qrcode")
}
}
impl<'a> Matcher<'a> for UploadMatcher<'a> {
fn with(matches: &'a ArgMatches) -> Option<Self> {
matches.subcommand_matches("upload")
.map(|matches|
UploadMatcher {
matches,
}
)
matches
.subcommand_matches("upload")
.map(|matches| UploadMatcher { matches })
}
}
/// The copy mode.
#[derive(Debug, Copy, Clone)]
pub enum CopyMode {
/// Copy the public share link.
Url,
/// Copy an ffsend download command.
DownloadCmd,
}
impl CopyMode {
/// Build the string to copy, based on the given `url` and current mode.
pub fn build(&self, url: &str) -> String {
match self {
CopyMode::Url => url.into(),
CopyMode::DownloadCmd => format!("{} download {}", bin_name(), url),
}
}
}

View file

@ -0,0 +1,30 @@
use ffsend_api::url::Url;
use clap::ArgMatches;
use super::Matcher;
use crate::cmd::arg::{ArgHost, CmdArgOption};
/// The version command matcher.
pub struct VersionMatcher<'a> {
matches: &'a ArgMatches<'a>,
}
impl<'a: 'b, 'b> VersionMatcher<'a> {
/// Get the host to probe.
///
/// This method parses the host into an `Url`.
/// If the given host is invalid,
/// the program will quit with an error message.
pub fn host(&'a self) -> Url {
ArgHost::value(self.matches)
}
}
impl<'a> Matcher<'a> for VersionMatcher<'a> {
fn with(matches: &'a ArgMatches) -> Option<Self> {
matches
.subcommand_matches("version")
.map(|matches| VersionMatcher { matches })
}
}

View file

@ -1,7 +1,7 @@
pub mod arg;
pub mod subcmd;
pub mod handler;
pub mod matcher;
pub mod subcmd;
// Reexport modules
pub use self::handler::Handler;

View file

@ -1,6 +1,6 @@
use clap::{App, SubCommand};
use cmd::arg::{ArgHost, CmdArg};
use crate::cmd::arg::{ArgHost, CmdArg};
/// The debug command definition.
pub struct CmdDebug;

View file

@ -1,6 +1,6 @@
use clap::{App, SubCommand};
use cmd::arg::{ArgOwner, ArgUrl, CmdArg};
use crate::cmd::arg::{ArgOwner, ArgUrl, CmdArg};
/// The delete command definition.
pub struct CmdDelete;
@ -10,6 +10,7 @@ impl CmdDelete {
SubCommand::with_name("delete")
.about("Delete a shared file")
.visible_alias("del")
.visible_alias("rm")
.arg(ArgUrl::build())
.arg(ArgOwner::build())
}

View file

@ -1,6 +1,6 @@
use clap::{App, Arg, SubCommand};
use cmd::arg::{ArgPassword, ArgUrl, CmdArg};
use crate::cmd::arg::{ArgPassword, ArgUrl, CmdArg};
/// The download command definition.
pub struct CmdDownload;
@ -15,24 +15,29 @@ impl CmdDownload {
.visible_alias("down")
.arg(ArgUrl::build())
.arg(ArgPassword::build())
.arg(Arg::with_name("output")
.long("output")
.short("o")
.alias("output-file")
.alias("out")
.alias("file")
.value_name("PATH")
.help("The output file or directory"));
.arg(
Arg::with_name("output")
.long("output")
.short("o")
.alias("output-file")
.alias("out")
.alias("file")
.value_name("PATH")
.help("Output file or directory"),
);
// Optional archive support
#[cfg(feature = "archive")] {
cmd = cmd.arg(Arg::with_name("extract")
.long("extract")
.short("e")
.alias("archive")
.alias("arch")
.alias("a")
.help("Extract an archived file"))
#[cfg(feature = "archive")]
{
cmd = cmd.arg(
Arg::with_name("extract")
.long("extract")
.short("e")
.alias("archive")
.alias("arch")
.alias("a")
.help("Extract an archived file"),
)
}
cmd

View file

@ -1,6 +1,6 @@
use clap::{App, SubCommand};
use cmd::arg::{ArgUrl, CmdArg};
use crate::cmd::arg::{ArgUrl, CmdArg};
/// The exists command definition.
pub struct CmdExists;

View file

@ -0,0 +1,33 @@
use clap::{App, Arg, Shell, SubCommand};
/// The generate completions command definition.
pub struct CmdCompletions;
impl CmdCompletions {
pub fn build<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("completions")
.about("Shell completions")
.alias("completion")
.alias("complete")
.arg(
Arg::with_name("SHELL")
.help("Shell type to generate completions for")
.required(true)
.multiple(true)
.takes_value(true)
.possible_value("all")
.possible_values(&Shell::variants())
.case_insensitive(true),
)
.arg(
Arg::with_name("output")
.long("output")
.short("o")
.alias("output-dir")
.alias("out")
.alias("dir")
.value_name("DIR")
.help("Shell completion files output directory"),
)
}
}

View file

@ -0,0 +1,18 @@
pub mod completions;
use clap::{App, AppSettings, SubCommand};
use completions::CmdCompletions;
/// The generate command definition.
pub struct CmdGenerate;
impl CmdGenerate {
pub fn build<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("generate")
.about("Generate assets")
.visible_alias("gen")
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(CmdCompletions::build())
}
}

View file

@ -1,4 +1,4 @@
use clap::{App, SubCommand};
use clap::{App, Arg, SubCommand};
/// The history command definition.
pub struct CmdHistory;
@ -9,5 +9,20 @@ impl CmdHistory {
.about("View file history")
.visible_alias("h")
.alias("ls")
.arg(
Arg::with_name("rm")
.long("rm")
.short("R")
.alias("remove")
.value_name("URL")
.help("Remove history entry"),
)
.arg(
Arg::with_name("clear")
.long("clear")
.short("C")
.alias("flush")
.help("Clear all history"),
)
}
}

View file

@ -1,6 +1,6 @@
use clap::{App, SubCommand};
use cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArg};
use crate::cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArg};
/// The info command definition.
pub struct CmdInfo;

View file

@ -2,21 +2,25 @@ pub mod debug;
pub mod delete;
pub mod download;
pub mod exists;
pub mod generate;
#[cfg(feature = "history")]
pub mod history;
pub mod info;
pub mod params;
pub mod password;
pub mod upload;
pub mod version;
// Reexport to cmd module
// Re-export to cmd module
pub use self::debug::CmdDebug;
pub use self::delete::CmdDelete;
pub use self::download::CmdDownload;
pub use self::exists::CmdExists;
pub use self::generate::CmdGenerate;
#[cfg(feature = "history")]
pub use self::history::CmdHistory;
pub use self::info::CmdInfo;
pub use self::params::CmdParams;
pub use self::password::CmdPassword;
pub use self::upload::CmdUpload;
pub use self::version::CmdVersion;

View file

@ -1,6 +1,6 @@
use clap::{App, SubCommand};
use cmd::arg::{ArgDownloadLimit, ArgOwner, ArgUrl, CmdArg};
use crate::cmd::arg::{ArgDownloadLimit, ArgOwner, ArgUrl, CmdArg};
/// The params command definition.
pub struct CmdParams;
@ -8,9 +8,7 @@ pub struct CmdParams;
impl CmdParams {
pub fn build<'a, 'b>() -> App<'a, 'b> {
// Create a list of parameter arguments, of which one is required
let param_args = [
ArgDownloadLimit::name(),
];
let param_args = [ArgDownloadLimit::name()];
SubCommand::with_name("parameters")
.about("Change parameters of a shared file")

View file

@ -1,6 +1,6 @@
use clap::{App, SubCommand};
use cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArg};
use crate::cmd::arg::{ArgGenPassphrase, ArgOwner, ArgPassword, ArgUrl, CmdArg};
/// The password command definition.
pub struct CmdPassword;
@ -12,8 +12,8 @@ impl CmdPassword {
.visible_alias("pass")
.visible_alias("p")
.arg(ArgUrl::build())
.arg(ArgPassword::build()
.help("Specify a password, do not prompt"))
.arg(ArgPassword::build().help("Specify a password, do not prompt"))
.arg(ArgGenPassphrase::build())
.arg(ArgOwner::build())
}
}

View file

@ -1,11 +1,10 @@
use clap::{App, Arg, SubCommand};
use ffsend_api::action::params::{
PARAMS_DEFAULT_DOWNLOAD_STR as DOWNLOAD_DEFAULT,
use crate::cmd::arg::{
ArgDownloadLimit, ArgExpiryTime, ArgGenPassphrase, ArgHost, ArgPassword, CmdArg,
};
use cmd::arg::{ArgDownloadLimit, ArgHost, ArgPassword, CmdArg};
/// The uplaod command definition.
/// The upload command definition.
pub struct CmdUpload;
impl CmdUpload {
@ -16,42 +15,96 @@ impl CmdUpload {
.about("Upload files")
.visible_alias("u")
.visible_alias("up")
.arg(Arg::with_name("FILE")
.help("The file to upload")
.required(true)
.multiple(false))
.arg(ArgPassword::build()
.help("Protect the file with a password"))
.arg(ArgDownloadLimit::build()
.default_value(DOWNLOAD_DEFAULT))
.arg(
Arg::with_name("FILE")
.help("The file(s) to upload")
.required(true)
.multiple(true),
)
.arg(ArgPassword::build().help("Protect the file with a password"))
.arg(ArgGenPassphrase::build())
.arg(ArgDownloadLimit::build())
.arg(ArgExpiryTime::build())
.arg(ArgHost::build())
.arg(Arg::with_name("name")
.long("name")
.short("n")
.alias("file")
.alias("f")
.value_name("NAME")
.help("Rename the file being uploaded"))
.arg(Arg::with_name("open")
.long("open")
.short("o")
.help("Open the share link in your browser"));
.arg(
Arg::with_name("name")
.long("name")
.short("n")
.alias("file")
.alias("f")
.value_name("NAME")
.help("Rename the file being uploaded"),
)
.arg(
Arg::with_name("open")
.long("open")
.short("o")
.help("Open the share link in your browser"),
)
.arg(
Arg::with_name("delete")
.long("delete")
.alias("rm")
.short("D")
.help("Delete local file after upload"),
);
// Optional archive support
#[cfg(feature = "archive")] {
cmd = cmd.arg(Arg::with_name("archive")
.long("archive")
.short("a")
.alias("arch")
.help("Archive the upload in a single file"))
#[cfg(feature = "archive")]
{
cmd = cmd.arg(
Arg::with_name("archive")
.long("archive")
.short("a")
.alias("arch")
.help("Archive the upload in a single file"),
)
}
// Optional clipboard support
#[cfg(feature = "clipboard")] {
cmd = cmd.arg(Arg::with_name("copy")
.long("copy")
.short("c")
.help("Copy the share link to your clipboard"));
#[cfg(feature = "clipboard")]
{
cmd = cmd
.arg(
Arg::with_name("copy")
.long("copy")
.short("c")
.help("Copy the share link to your clipboard")
.conflicts_with("copy-cmd"),
)
.arg(
Arg::with_name("copy-cmd")
.long("copy-cmd")
.alias("copy-command")
.short("C")
.help("Copy the ffsend download command to your clipboard")
.conflicts_with("copy"),
);
}
// Optional url shortening support
#[cfg(feature = "urlshorten")]
{
cmd = cmd.arg(
Arg::with_name("shorten")
.long("shorten")
.alias("short")
.alias("url-shorten")
.short("S")
.help("Shorten share URLs with a public service"),
)
}
// Optional qrcode support
#[cfg(feature = "qrcode")]
{
cmd = cmd.arg(
Arg::with_name("qrcode")
.long("qrcode")
.alias("qr")
.short("Q")
.help("Print a QR code for the share URL"),
)
}
cmd

16
src/cmd/subcmd/version.rs Normal file
View file

@ -0,0 +1,16 @@
use clap::{App, SubCommand};
use crate::cmd::arg::{ArgHost, CmdArg};
/// The version command definition.
pub struct CmdVersion;
impl CmdVersion {
pub fn build<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("version")
.about("Determine the Send server version")
.alias("ver")
.visible_alias("v")
.arg(ArgHost::build())
}
}

36
src/config.rs Normal file
View file

@ -0,0 +1,36 @@
#[cfg(feature = "infer-command")]
use std::collections::HashMap;
use ffsend_api::api::{DesiredVersion, Version};
/// The timeout for the Send client for generic requests, `0` to disable.
pub const CLIENT_TIMEOUT: u64 = 30;
/// The timeout for the Send client used to transfer (upload/download) files.
/// Make sure this is big enough, or file uploads will be dropped. `0` to disable.
pub const CLIENT_TRANSFER_TIMEOUT: u64 = 24 * 60 * 60;
/// The default desired version to select for the server API.
pub const API_VERSION_DESIRED_DEFAULT: DesiredVersion = DesiredVersion::Assume(API_VERSION_ASSUME);
/// The default server API version to assume when it could not be determined.
#[cfg(feature = "send3")]
pub const API_VERSION_ASSUME: Version = Version::V3;
#[cfg(not(feature = "send3"))]
pub const API_VERSION_ASSUME: Version = Version::V2;
#[cfg(feature = "infer-command")]
lazy_static! {
/// Hashmap holding binary names to infer subcommands for.
///
/// When the `ffsend` binary is called with such a name, the corresponding subcommand is
/// automatically inserted as argument. This also works when calling binaries through symbolic
/// or hard links.
pub static ref INFER_COMMANDS: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert("ffput", "upload");
m.insert("ffget", "download");
m.insert("ffdel", "delete");
m
};
}

View file

@ -2,13 +2,15 @@ use ffsend_api::action::delete::Error as DeleteError;
use ffsend_api::action::exists::Error as ExistsError;
use ffsend_api::action::params::Error as ParamsError;
use ffsend_api::action::password::Error as PasswordError;
use ffsend_api::action::version::Error as VersionError;
use ffsend_api::file::remote_file::FileParseError;
use action::download::Error as CliDownloadError;
use crate::action::download::Error as CliDownloadError;
use crate::action::generate::completions::Error as CliGenerateCompletionsError;
#[cfg(feature = "history")]
use action::history::Error as CliHistoryError;
use action::info::Error as CliInfoError;
use action::upload::Error as CliUploadError;
use crate::action::history::Error as CliHistoryError;
use crate::action::info::Error as CliInfoError;
use crate::action::upload::Error as CliUploadError;
#[derive(Fail, Debug)]
pub enum Error {
@ -55,6 +57,10 @@ pub enum ActionError {
#[fail(display = "failed to check whether the file exists")]
Exists(#[cause] ExistsError),
/// An error occurred while generating completions.
#[fail(display = "failed to generate shell completions")]
GenerateCompletions(#[cause] CliGenerateCompletionsError),
/// An error occurred while processing the file history.
#[cfg(feature = "history")]
#[fail(display = "failed to process the history")]
@ -72,6 +78,10 @@ pub enum ActionError {
#[fail(display = "failed to change the password")]
Password(#[cause] PasswordError),
/// An error occurred while invoking the version action.
#[fail(display = "failed to determine server version")]
Version(#[cause] VersionError),
/// An error occurred while invoking the upload action.
#[fail(display = "failed to upload the specified file")]
Upload(#[cause] CliUploadError),
@ -94,6 +104,12 @@ impl From<ExistsError> for ActionError {
}
}
impl From<CliGenerateCompletionsError> for ActionError {
fn from(err: CliGenerateCompletionsError) -> ActionError {
ActionError::GenerateCompletions(err)
}
}
#[cfg(feature = "history")]
impl From<CliHistoryError> for ActionError {
fn from(err: CliHistoryError) -> ActionError {
@ -113,6 +129,12 @@ impl From<PasswordError> for ActionError {
}
}
impl From<VersionError> for ActionError {
fn from(err: VersionError) -> ActionError {
ActionError::Version(err)
}
}
impl From<FileParseError> for ActionError {
fn from(err: FileParseError) -> ActionError {
ActionError::InvalidUrl(err)

View file

@ -1,20 +1,16 @@
extern crate toml;
extern crate version_compare;
use std::fs;
use std::io::Error as IoError;
use std::path::PathBuf;
use failure::Fail;
use ffsend_api::file::remote_file::RemoteFile;
use self::toml::de::Error as DeError;
use self::toml::ser::Error as SerError;
use self::version_compare::{
CompOp,
VersionCompare,
use ffsend_api::{
file::remote_file::{FileParseError, RemoteFile},
url::Url,
};
use toml::{de::Error as DeError, ser::Error as SerError};
use version_compare::Cmp;
use util::{print_error, print_warning};
use crate::util::{print_error, print_warning};
/// The minimum supported history file version.
const VERSION_MIN: &str = "0.0.1";
@ -25,7 +21,7 @@ const VERSION_MAX: &str = crate_version!();
#[derive(Serialize, Deserialize)]
pub struct History {
/// The application version the history file was built with.
/// Used for compatability checking.
/// Used for compatibility checking.
version: Option<String>,
/// The file history.
@ -59,16 +55,16 @@ impl History {
history.autosave = Some(path);
// Make sure the file version is supported
if history.version.is_none() {
if history.version.is_none() {
print_warning("History file has no version, ignoring");
history.version = Some(crate_version!().into());
} else {
// Get the version number from the file
let version = history.version.as_ref().unwrap();
if let Ok(true) = VersionCompare::compare_to(version, VERSION_MIN, &CompOp::Lt) {
if let Ok(true) = version_compare::compare_to(version, VERSION_MIN, Cmp::Lt) {
print_warning("history file version is too old, ignoring");
} else if let Ok(true) = VersionCompare::compare_to(version, VERSION_MAX, &CompOp::Gt) {
} else if let Ok(true) = version_compare::compare_to(version, VERSION_MAX, Cmp::Gt) {
print_warning("history file has an unknown version, ignoring");
}
}
@ -97,24 +93,36 @@ impl History {
self.gc();
// Get the path
let path = self.autosave
.as_ref()
.ok_or(SaveError::NoPath)?;
let path = self.autosave.as_ref().ok_or(SaveError::NoPath)?;
// If we have no files, remove the history file if it exists
if self.files.is_empty() {
if path.is_file() {
fs::remove_file(&path)
.map_err(SaveError::Delete)?;
fs::remove_file(&path).map_err(SaveError::Delete)?;
}
return Ok(());
}
// Ensure the file parnet directories are available
// Ensure the file parent directories are available
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
// Set file permissions on unix based systems
#[cfg(unix)]
{
use std::fs::Permissions;
use std::os::unix::fs::PermissionsExt;
if !path.exists() {
let file = fs::File::create(path).map_err(SaveError::Write)?;
// Set Read/Write permissions for the user
file.set_permissions(Permissions::from_mode(0o600))
.map_err(SaveError::SetPermissions)?;
}
}
// Build the data and write to a file
let data = toml::to_string(self)?;
fs::write(&path, data)?;
@ -135,7 +143,9 @@ impl History {
// Merge any existing file with the same ID
{
// Find anything to merge
let merge_info: Vec<bool> = self.files.iter_mut()
let merge_info: Vec<bool> = self
.files
.iter_mut()
.filter(|f| f.id() == file.id())
.map(|ref mut f| f.merge(&file, overwrite))
.collect();
@ -156,14 +166,16 @@ impl History {
self.changed = true;
}
/// Remove the given remote file, matched by it's file ID.
/// Remove a file, matched by it's file ID.
///
/// If any file was removed, true is returned.
pub fn remove(&mut self, file: &RemoteFile) -> bool {
pub fn remove(&mut self, id: &str) -> bool {
// Get the indices of files that have expired
let expired_indices: Vec<usize> = self.files.iter()
let expired_indices: Vec<usize> = self
.files
.iter()
.enumerate()
.filter(|&(_, f)| f.id() == file.id())
.filter(|&(_, f)| f.id() == id)
.map(|(i, _)| i)
.collect();
@ -179,6 +191,13 @@ impl History {
!expired_indices.is_empty()
}
/// Remove a file by the given URL.
///
/// If any file was removed, true is returned.
pub fn remove_url(&mut self, url: Url) -> Result<bool, FileParseError> {
Ok(self.remove(RemoteFile::parse_url(url, None)?.id()))
}
/// Get all files.
pub fn files(&self) -> &Vec<RemoteFile> {
&self.files
@ -189,7 +208,15 @@ impl History {
/// If multiple files exist within the history that are equal, only one is returned.
/// If no matching file was found, `None` is returned.
pub fn get_file(&self, file: &RemoteFile) -> Option<&RemoteFile> {
self.files.iter().find(|f| f.id() == file.id() && f.host() == file.host())
self.files
.iter()
.find(|f| f.id() == file.id() && f.host() == file.host())
}
/// Clear all history.
pub fn clear(&mut self) {
self.changed = !self.files.is_empty();
self.files.clear();
}
/// Garbage collect (remove) all files that have been expired,
@ -197,10 +224,11 @@ impl History {
///
/// If the expiry property is None (thus unknown), the file will be kept.
///
/// The number of exired files is returned.
/// The number of expired files is returned.
pub fn gc(&mut self) -> usize {
// Get a list of expired files
let expired: Vec<RemoteFile> = self.files
let expired: Vec<RemoteFile> = self
.files
.iter()
.filter(|f| f.has_expired())
.cloned()
@ -208,7 +236,7 @@ impl History {
// Remove the files
for f in &expired {
self.remove(f);
self.remove(f.id());
}
// Set the changed flag
@ -227,9 +255,7 @@ impl Drop for History {
if self.autosave.is_some() && self.changed {
// Save and report errors
if let Err(err) = self.save() {
print_error(
err.context("failed to auto save history, ignoring"),
);
print_error(err.context("failed to auto save history, ignoring"));
}
}
}
@ -272,11 +298,11 @@ impl From<SaveError> for Error {
#[derive(Debug, Fail)]
pub enum LoadError {
/// Failed to read the file contents from the given file.
#[fail(display = "Failed to read from the history file")]
#[fail(display = "failed to read from the history file")]
Read(#[cause] IoError),
/// Failed to parse the loaded file.
#[fail(display = "Failed to parse the file contents")]
#[fail(display = "failed to parse the file contents")]
Parse(#[cause] DeError),
}
@ -306,6 +332,10 @@ pub enum SaveError {
#[fail(display = "failed to write to the history file")]
Write(#[cause] IoError),
/// Failed to set file permissions to the history file.
#[fail(display = "failed to set permissions to the history file")]
SetPermissions(#[cause] IoError),
/// Failed to delete the history file, which was tried because there
/// are no history items to save.
#[fail(display = "failed to delete history file, because history is empty")]

View file

@ -1,12 +1,9 @@
use failure::Fail;
use ffsend_api::file::remote_file::RemoteFile;
use cmd::matcher::MainMatcher;
use history::{
Error as HistoryError,
History,
};
use util::print_error;
use crate::cmd::matcher::MainMatcher;
use crate::history::{Error as HistoryError, History};
use crate::util::print_error;
/// Load the history from the given path, add the given file, and save it
/// again.
@ -16,9 +13,11 @@ use util::print_error;
/// overwrite properties in the already existing file when merging.
///
/// If there is no file at the given path, new history will be created.
fn add_error(matcher_main: &MainMatcher, file: RemoteFile, overwrite: bool)
-> Result<(), HistoryError>
{
fn add_error(
matcher_main: &MainMatcher,
file: RemoteFile,
overwrite: bool,
) -> Result<(), HistoryError> {
// Ignore if incognito
if matcher_main.incognito() {
return Ok(());
@ -41,18 +40,14 @@ fn add_error(matcher_main: &MainMatcher, file: RemoteFile, overwrite: bool)
/// If an error occurred, the error is printed and ignored.
pub fn add(matcher_main: &MainMatcher, file: RemoteFile, overwrite: bool) {
if let Err(err) = add_error(matcher_main, file, overwrite) {
print_error(err.context(
"failed to add file to local history, ignoring",
));
print_error(err.context("failed to add file to local history, ignoring"));
}
}
/// Load the history from the given path, remove the given file by it's
/// ID, and save it again.
/// True is returned if any file was removed.
fn remove_error(matcher_main: &MainMatcher, file: &RemoteFile)
-> Result<bool, HistoryError>
{
fn remove_error(matcher_main: &MainMatcher, file: &RemoteFile) -> Result<bool, HistoryError> {
// Ignore if incognito
if matcher_main.incognito() {
return Ok(false);
@ -60,7 +55,7 @@ fn remove_error(matcher_main: &MainMatcher, file: &RemoteFile)
// Load the history, remove the file, and save
let mut history = History::load_or_new(matcher_main.history())?;
let removed = history.remove(file);
let removed = history.remove(file.id());
history.save()?;
Ok(removed)
}
@ -72,9 +67,7 @@ pub fn remove(matcher_main: &MainMatcher, file: &RemoteFile) -> bool {
let result = remove_error(matcher_main, file);
let ok = result.is_ok();
if let Err(err) = result {
print_error(err.context(
"failed to remove file from local history, ignoring",
));
print_error(err.context("failed to remove file from local history, ignoring"));
}
ok
}
@ -104,9 +97,7 @@ pub fn derive_file_properties(matcher_main: &MainMatcher, file: &mut RemoteFile)
let history = match History::load_or_new(matcher_main.history()) {
Ok(history) => history,
Err(err) => {
print_error(err.context(
"failed to derive file properties from history, ignoring",
));
print_error(err.context("failed to derive file properties from history, ignoring"));
return false;
}
};
@ -126,7 +117,7 @@ pub fn derive_file_properties(matcher_main: &MainMatcher, file: &mut RemoteFile)
// Return whether any property was derived
f.has_secret() || f.has_owner_token()
},
}
None => false,
}
}

Some files were not shown because too many files have changed in this diff Show more