Przeglądaj źródła

Implement first URL shortening logic using is.gd

timvisee 6 lat temu
rodzic
commit
cca5889e39
8 zmienionych plików z 155 dodań i 17 usunięć
  1. 33 12
      Cargo.lock
  2. 5 1
      Cargo.toml
  3. 2 0
      README.md
  4. 38 4
      src/action/upload.rs
  5. 6 0
      src/cmd/matcher/upload.rs
  6. 13 0
      src/cmd/subcmd/upload.rs
  7. 2 0
      src/main.rs
  8. 56 0
      src/urlshorten.rs

+ 33 - 12
Cargo.lock

@@ -547,9 +547,10 @@ dependencies = [
  "rpassword 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
- "tar 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tar 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "urlshortener 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "version-compare 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -747,7 +748,7 @@ dependencies = [
  "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1406,7 +1407,7 @@ dependencies = [
  "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-threadpool 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1616,7 +1617,7 @@ dependencies = [
 
 [[package]]
 name = "tar"
-version = "0.4.21"
+version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1693,7 +1694,7 @@ dependencies = [
 
 [[package]]
 name = "tokio"
-version = "0.1.16"
+version = "0.1.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1706,10 +1707,11 @@ dependencies = [
  "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-sync 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-sync 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-threadpool 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -1777,12 +1779,12 @@ dependencies = [
  "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-sync 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-sync 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "tokio-sync"
-version = "0.1.3"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1839,6 +1841,14 @@ dependencies = [
  "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "tokio-trace-core"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "tokio-udp"
 version = "0.1.3"
@@ -1964,6 +1974,15 @@ dependencies = [
  "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "urlshortener"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "utf8-ranges"
 version = "1.0.2"
@@ -2021,7 +2040,7 @@ dependencies = [
  "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2272,7 +2291,7 @@ dependencies = [
 "checksum syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)" = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b"
 "checksum syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1825685f977249735d510a242a6727b46efe914bb67e38d30c071b1b72b1d5c2"
 "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
-"checksum tar 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)" = "904b43da53c99b929c4484fa281e5535f2eb86b3040de3e3e5b69708e2a8bd65"
+"checksum tar 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2167ff53da2a661702b3299f71a91b61b1dffef36b4b2884b1f9c67254c0133"
 "checksum tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b86c784c88d98c801132806dadd3819ed29d8600836c4088e855cdf3e178ed8a"
 "checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561"
 "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
@@ -2280,18 +2299,19 @@ dependencies = [
 "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
 "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
 "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
-"checksum tokio 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fcaabb3cec70485d0df6e9454fe514393ad1c4070dee8915f11041e95630b230"
+"checksum tokio 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "1021bb1f4150435ab8f222eb7ed37c60b2d57037def63ba43085a79f387512d7"
 "checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f"
 "checksum tokio-current-thread 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c756b04680eea21902a46fca4e9f410a2332c04995af590e07ff262e2193a9a3"
 "checksum tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30c6dbf2d1ad1de300b393910e8a3aa272b724a400b6531da03eed99e329fbf0"
 "checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af"
 "checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926"
 "checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce"
-"checksum tokio-sync 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1bf2b9dac2a0509b5cfd1df5aa25eafacb616a42a491a13604d6bbeab4486363"
+"checksum tokio-sync 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fda385df506bf7546e70872767f71e81640f1f251bdf2fd8eb81a0eaec5fe022"
 "checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119"
 "checksum tokio-threadpool 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "742e511f6ce2298aeb86fc9ea0d8df81c2388c6ebae3dc8a7316e8c9df0df801"
 "checksum tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2910970404ba6fa78c5539126a9ae2045d62e3713041e447f695f41405a120c6"
 "checksum tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c"
+"checksum tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "350c9edade9830dc185ae48ba45667a445ab59f6167ef6d0254ec9d2430d9dd3"
 "checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92"
 "checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445"
 "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
@@ -2308,6 +2328,7 @@ dependencies = [
 "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
 "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
 "checksum url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74e7d099f1ee52f823d4bdd60c93c3602043c728f5db3b97bdb548467f7bddea"
+"checksum urlshortener 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f16270e97b5ed9c2e8affcbc8a2e694860fda13a43c3e551d0d5eb29618a308"
 "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
 "checksum uuid 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0238db0c5b605dd1cf51de0f21766f97fba2645897024461d6a00c036819a768"
 "checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d"

+ 5 - 1
Cargo.toml

@@ -50,7 +50,7 @@ name = "ffsend"
 path = "src/main.rs"
 
 [features]
-default = ["archive", "clipboard", "history", "send2", "send3", "qrcode"]
+default = ["archive", "clipboard", "history", "send2", "send3", "qrcode", "urlshorten"]
 
 # Compile with file archiving support
 archive = ["tar"]
@@ -70,6 +70,9 @@ send3 = ["ffsend-api/send3"]
 # Support for generating QR codes for share URLs
 qrcode = ["qr2term"]
 
+# Support for shortening share URLs
+urlshorten = ["urlshortener"]
+
 [dependencies]
 chbs = "0.0.8"
 chrono = "0.4"
@@ -93,6 +96,7 @@ tar = { version = "0.4", optional = true }
 tempfile = "3"
 toml = "0.4"
 version-compare = "0.0.6"
+urlshortener = { version = "0.9", default-features = false, optional = true }
 
 [target.'cfg(not(target_os = "linux"))'.dependencies]
 clipboard = { version = "0.5", optional = true }

+ 2 - 0
README.md

@@ -394,6 +394,8 @@ The following features are available, some of which are enabled by default:
 | `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                          |
 | `no-color`  |         | Compile without color support in error and help messages   |
 
 To enable features during building or installation, specify them with

+ 38 - 4
src/action/upload.rs

@@ -20,11 +20,13 @@ use tempfile::{Builder as TempBuilder, NamedTempFile};
 use super::select_api_version;
 #[cfg(feature = "archive")]
 use crate::archive::archiver::Archiver;
-use crate::client::create_transfer_client;
+use crate::client::{create_client, create_transfer_client};
 use crate::cmd::matcher::{MainMatcher, Matcher, UploadMatcher};
 #[cfg(feature = "history")]
 use crate::history_tool;
 use crate::progress::ProgressBar;
+#[cfg(feature = "urlshorten")]
+use crate::urlshorten;
 #[cfg(feature = "clipboard")]
 use crate::util::set_clipboard;
 use crate::util::{
@@ -56,7 +58,7 @@ impl<'a> Upload<'a> {
         let host = matcher_upload.host();
 
         // Create a reqwest client capable for uploading files
-        let client = create_transfer_client(&matcher_main);
+        let client = create_client(&matcher_main);
 
         // Determine the API version to use
         let mut desired_version = matcher_main.api();
@@ -228,23 +230,55 @@ impl<'a> Upload<'a> {
             params,
         )
         .invoke(&transfer_client, reporter)?;
-        let url = file.download_url(true);
+        #[allow(unused_mut)]
+        let mut url = file.download_url(true);
+
+        // Shorten the share URL if requested
+        #[cfg(feature = "urlshorten")]
+        {
+            if matcher_upload.shorten() {
+                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() {
-            // Show a table
+            // Create a table
             let mut table = Table::new();
             table.set_format(FormatBuilder::new().padding(0, 2).build());
+
+            // Show the original URL when shortening and verbose
+            #[cfg(feature = "urlshorten")]
+            {
+                if matcher_main.verbose() && matcher_upload.shorten() {
+                    table.add_row(Row::new(vec![
+                        Cell::new("Full share link:"),
+                        Cell::new(file.download_url(true).as_str()),
+                    ]));
+                }
+            }
+
+            // 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
             if matcher_main.verbose() {
                 table.add_row(Row::new(vec![
                     Cell::new("Owner token:"),

+ 6 - 0
src/cmd/matcher/upload.rs

@@ -110,6 +110,12 @@ impl<'a: 'b, 'b> UploadMatcher<'a> {
         }
     }
 
+    /// 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 {

+ 13 - 0
src/cmd/subcmd/upload.rs

@@ -73,6 +73,19 @@ impl CmdUpload {
                 );
         }
 
+        // 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")]
         {

+ 2 - 0
src/main.rs

@@ -23,6 +23,8 @@ mod history;
 mod history_tool;
 mod host;
 mod progress;
+#[cfg(feature = "urlshorten")]
+mod urlshorten;
 mod util;
 
 use crate::action::debug::Debug;

+ 56 - 0
src/urlshorten.rs

@@ -0,0 +1,56 @@
+//! URL shortening mechanics.
+
+use ffsend_api::{
+    url::{self, Url},
+    reqwest::Client,
+};
+use urlshortener::{
+    providers::{Provider, self},
+    request::{Method, Request},
+};
+
+/// Shorten the given URL.
+pub fn shorten(client: &Client, url: &str) -> String {
+    // TODO: allow selecting other shorteners
+    request(client, providers::request(url, &Provider::IsGd))
+}
+
+/// Shorten the given URL.
+pub fn shorten_url(client: &Client, url: &Url) -> Result<Url, url::ParseError> {
+    Url::parse(&shorten(client, url.as_str()))
+}
+
+/// Do the request as given, return the response.
+fn request(client: &Client, req: Request) -> String {
+    // Start the request builder
+    let mut builder = match req.method {
+        Method::Get => client.get(&req.url),
+        Method::Post => client.post(&req.url),
+    };
+
+    // Define the custom user agent
+    if let Some(_agent) = req.user_agent.clone() {
+        // TODO: implement this
+        // builder.header(header::UserAgent::new(agent.0));
+        panic!("Custom UserAgent for URL shortener not yet implemented");
+    }
+
+    // Define the custom content type
+    if let Some(_content_type) = req.content_type {
+        // TODO: implement this
+        // match content_type {
+        //     ContentType::Json => builder.header(header::ContentType::json()),
+        //     ContentType::FormUrlEncoded => {
+        //         builder.header(header::ContentType::form_url_encoded())
+        //     }
+        // };
+        panic!("Custom UserAgent for URL shortener not yet implemented");
+    }
+
+    // Define the custom body
+    if let Some(body) = req.body.clone() {
+        builder = builder.body(body);
+    }
+
+    builder.send().expect("failed to send shorten request").text().expect("failed to get text")
+}