diff --git a/Cargo.lock b/Cargo.lock index e2a9ab7..62c4770 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,19 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "anstream" version = "0.6.14" @@ -249,6 +262,12 @@ version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.6.0" @@ -441,6 +460,29 @@ dependencies = [ "subtle", ] +[[package]] +name = "cssparser" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c66d1cd8ed61bf80b38432613a7a2f09401ab8d0501110655f8b341484a3e3" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.57", +] + [[package]] name = "ctr" version = "0.8.0" @@ -494,6 +536,17 @@ dependencies = [ "matches", ] +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + [[package]] name = "dialoguer" version = "0.9.0" @@ -536,6 +589,27 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "ego-tree" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c6ba7d4eec39eaa9ab24d44a0e73a7949a1095a8b3f3abb11eddf27dbb56a53" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -612,6 +686,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -662,6 +746,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -672,6 +765,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.2.12" @@ -790,6 +892,20 @@ dependencies = [ "digest", ] +[[package]] +name = "html5ever" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e15626aaf9c351bc696217cbe29cb9b5e86c43f8a46b5e2f5c6c5cf7cb904ce" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 2.0.57", +] + [[package]] name = "http" version = "1.1.0" @@ -977,12 +1093,42 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "markup5ever" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c88c6129bd24319e62a0359cb6b958fa7e8be6e19bb1663bc396b90883aca5" +dependencies = [ + "log", + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", + "tendril", +] + [[package]] name = "matches" version = "0.1.10" @@ -1064,6 +1210,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nom" version = "7.1.3" @@ -1171,6 +1323,29 @@ version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.4", +] + [[package]] name = "parse-size" version = "1.0.0" @@ -1218,6 +1393,7 @@ dependencies = [ "rand_core", "reqwest", "ring", + "scraper", "serde", "serde_json", "serde_with", @@ -1246,6 +1422,77 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.5" @@ -1308,6 +1555,12 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1350,6 +1603,17 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -1369,6 +1633,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "redox_users" version = "0.4.6" @@ -1481,6 +1754,28 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scraper" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0e749d29b2064585327af5038a5a8eb73aeebad4a3472e83531a436563f7208" +dependencies = [ + "ahash", + "cssparser", + "ego-tree", + "getopts", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", +] + [[package]] name = "scroll" version = "0.12.0" @@ -1524,6 +1819,25 @@ dependencies = [ "libc", ] +[[package]] +name = "selectors" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8" +dependencies = [ + "bitflags 2.5.0", + "cssparser", + "derive_more", + "fxhash", + "log", + "new_debug_unreachable", + "phf", + "phf_codegen", + "precomputed-hash", + "servo_arc", + "smallvec", +] + [[package]] name = "semver" version = "1.0.22" @@ -1598,6 +1912,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "servo_arc" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae65c4249478a2647db249fb43e23cec56a2c8974a427e7bd8cb5a1d0964921a" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "sha2" version = "0.9.9" @@ -1654,12 +1977,44 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + [[package]] name = "strsim" version = "0.10.0" @@ -1739,6 +2094,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -2094,6 +2460,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.1" @@ -2385,6 +2757,26 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + [[package]] name = "zeroize" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index 64cbe65..7ff2860 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,4 +48,5 @@ parse-size = { version = "1.0.0", features = ["std"] } uniffi = { version = "0.28.0", optional = true, features = ["cli"] } openssl = { version = "0.10", features = ["vendored"] } directories = "5.0.1" -log = "0.4.22" \ No newline at end of file +log = "0.4.22" +scraper = "0.21.0" diff --git a/README.md b/README.md index dd029b4..21194fa 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ OPTIONS: --oidc-username username to send to the token endpoint --overwrite overwrite the file given with --download if it already exists --password + --scrape-expiries attempt scraping supported expiries of given host and exit --size-limit Prompt if the paste exceeds the given size. Fail in non-interactive environments. --upload -V, --version Print version information diff --git a/src/api.rs b/src/api.rs index f544429..1967541 100644 --- a/src/api.rs +++ b/src/api.rs @@ -6,6 +6,7 @@ use crate::util::check_filesize; use crate::DecryptedPaste; use rand_chacha::rand_core::{RngCore, SeedableRng}; use reqwest::{Method, Url}; +use scraper::{Html, Selector}; use std::str::FromStr; #[cfg_attr(feature = "uniffi", derive(uniffi::Object))] @@ -75,11 +76,14 @@ impl API { &self, method: &str, url: Url, + json_request: bool, ) -> PbResult { let client = reqwest::blocking::Client::builder().build()?; let mut request = client.request(Method::from_str(method).unwrap(), url); - request = request.header("X-Requested-With", "JSONHttpRequest"); + if json_request { + request = request.header("X-Requested-With", "JSONHttpRequest"); + } if self.opts.oidc_token_url.is_some() { let access_token = self.get_oidc_access_token()?; @@ -96,7 +100,7 @@ impl API { pub fn get_paste(&self, paste_id: &str) -> PbResult { let url = reqwest::Url::parse_with_params(self.base.as_str(), [("pasteid", paste_id)])?; let value: serde_json::Value = self - .preconfigured_privatebin_request_builder("GET", url)? + .preconfigured_privatebin_request_builder("GET", url, true)? .send()? .json()?; let status: u32 = value.get("status").unwrap().as_u64().unwrap() as u32; @@ -131,7 +135,7 @@ impl API { let encrypted_content = encrypt( &serde_json::to_string(content)?, - &paste_passphrase.into(), + &paste_passphrase, password, &cipher.vec_kdf_salt()?, &cipher.vec_cipher_iv()?, @@ -145,7 +149,7 @@ impl API { let url = self.base.clone(); let response = self - .preconfigured_privatebin_request_builder("POST", url)? + .preconfigured_privatebin_request_builder("POST", url, true)? .body::(serde_json::to_string(&paste).unwrap()) .send()?; let mut rsv: serde_json::Value = response.json()?; @@ -194,7 +198,7 @@ impl API { let url = self.base.clone(); let response = self - .preconfigured_privatebin_request_builder("POST", url)? + .preconfigured_privatebin_request_builder("POST", url, true)? .body::(serde_json::to_string(&comment).unwrap()) .send()?; let rsv: serde_json::Value = response.json()?; @@ -207,6 +211,24 @@ impl API { } } + pub fn scrape_expiries(&self) -> PbResult> { + let url = self.base.clone(); + let response = self + .preconfigured_privatebin_request_builder("GET", url, false)? + .send()?; + response.error_for_status_ref()?; + let html = response.text()?; + let document = Html::parse_document(&html); + let expiries_selector = Selector::parse("#expiration + ul > li > a").unwrap(); + let mut expiries = Vec::new(); + for expiry_anchor in document.select(&expiries_selector) { + if let Some(expiry) = expiry_anchor.attr("data-expiration") { + expiries.push(expiry.to_string()); + } + } + Ok(expiries) + } + pub fn base(&self) -> Url { self.base.clone() } diff --git a/src/crypto.rs b/src/crypto.rs index 68c71fc..2fb1ee2 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -53,7 +53,7 @@ pub fn decrypt_with_password( pub fn encrypt( content: &str, - key: &Vec, + key: &[u8], password: &str, salt: &[u8], nonce: &[u8], diff --git a/src/main.rs b/src/main.rs index 0bf9d84..640fc6c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -171,6 +171,15 @@ fn handle_comment(opts: &Opts) -> PbResult<()> { Ok(()) } +fn handle_scrape(opts: &Opts) -> PbResult<()> { + let url = opts.get_url(); + let api = API::new(url.clone(), opts.clone()); + let expiries = api.scrape_expiries()?; + std::io::stdout().write_all(format!("{:?}", expiries).as_bytes())?; + writeln!(std::io::stdout())?; + Ok(()) +} + fn main() -> PbResult<()> { crate::logger::SimpleLogger::init()?; @@ -187,6 +196,10 @@ fn main() -> PbResult<()> { let opts: Opts = Opts::parse_from(&merged_args); + if opts.scrape_expiries { + return handle_scrape(&opts); + } + let url_has_query = opts.get_url().query().is_some(); if url_has_query { if opts.comment { diff --git a/src/opts.rs b/src/opts.rs index 30eec0d..bf7c1ac 100644 --- a/src/opts.rs +++ b/src/opts.rs @@ -107,6 +107,10 @@ pub struct Opts { #[clap(long)] #[clap(help("do not look for config in default locations"))] pub no_default_config: bool, + + #[clap(long)] + #[clap(help("attempt scraping supported expiries of given host and exit"))] + pub scrape_expiries: bool, } impl Opts {