From a46bc063ffcd93d8fb95d706ff4cdadee5b2d571 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Fri, 1 Apr 2022 00:40:42 +0200 Subject: [PATCH] Add support for sendcm - Change the special url resolver code to better (still not optimally) support different services besides zippyshare - Implement support for sendcm --- Cargo.toml | 2 +- src/integrations.rs | 26 ++++++++++ src/integrations/sendcm.rs | 84 +++++++++++++++++++++++++++++++++ src/{ => integrations}/zippy.rs | 0 src/main.rs | 29 ++++++------ 5 files changed, 126 insertions(+), 15 deletions(-) create mode 100644 src/integrations.rs create mode 100644 src/integrations/sendcm.rs rename src/{ => integrations}/zippy.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 6641a92..5a62ccb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "ffdl" version = "0.1.2" authors = ["daniel m "] -edition = "2018" +edition = "2021" description = "Download files fast" [dependencies] diff --git a/src/integrations.rs b/src/integrations.rs new file mode 100644 index 0000000..ca635c7 --- /dev/null +++ b/src/integrations.rs @@ -0,0 +1,26 @@ +mod zippy; +mod sendcm; + +use anyhow::Result; + +pub enum IntegratedService { + ZippyShare, + SendCm, +} + +pub fn is_integrated_url(url: &str) -> Option { + if zippy::is_zippyshare_url(url) { + Some(IntegratedService::ZippyShare) + } else if sendcm::is_sendcm_url(url) { + Some(IntegratedService::SendCm) + } else { + None + } +} + +pub async fn resolve_integrated_url(url: &str, service: IntegratedService) -> Result { + match service { + IntegratedService::ZippyShare => zippy::resolve_link(url).await, + IntegratedService::SendCm => sendcm::resolve_link(url).await, + } +} diff --git a/src/integrations/sendcm.rs b/src/integrations/sendcm.rs new file mode 100644 index 0000000..714f0a3 --- /dev/null +++ b/src/integrations/sendcm.rs @@ -0,0 +1,84 @@ +use std::io::{Error, ErrorKind}; + +use anyhow::Result; +use regex::Regex; + +pub fn is_sendcm_url(url: &str) -> bool { + Regex::new(r"^https?://send\.cm/(?:d/)?[0-9a-zA-Z]+$") + .unwrap() + .is_match(url) +} + +/* +Updated: 01.04.2022 +Link generation code: +- A post request is sent to the server using the form described below +- The id field is the value which is used to generate the link +- If the id is not found, the link is not generated +- The id is the same as the url suffix when NOT using a /d/ prefix url +- The reponse to the post request is a 302 redirect to the generated link + +``` +
+ + + + + + +...... +``` +*/ +pub async fn resolve_link(url: &str) -> Result { + let user_agent = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:77.0) Gecko/20100101 Firefox/77.0"; + let accept = + "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"; + + // Add a few extra headers to the request in order to be less suspicious + let body = reqwest::Client::new() + .get(url) + .header("User-Agent", user_agent) + .header("Accept", accept) + .send() + .await? + .text() + .await?; + + let re_link = Regex::new(r#""#)?; + + let cap_link = match re_link.captures(&body) { + Some(cap) => cap, + None => return Err(Error::new(ErrorKind::Other, "Link not found").into()), + }; + + let id = &match cap_link.get(1) { + Some(id) => id.as_str(), + None => return Err(Error::new(ErrorKind::Other, "Link not found").into()), + }; + + let resp = reqwest::ClientBuilder::new() + .redirect(reqwest::redirect::Policy::none()) + .build()? + .post("https://send.cm") + .header("User-Agent", user_agent) + .header("Accept", accept) + .form(&[ + ("op", "download2"), + ("id", id), + ("rand", ""), + ("referer", ""), + ("method_free", ""), + ("method_premium", ""), + ]) + .send() + .await?; + + if resp.status().is_redirection() { + match resp.headers().get(reqwest::header::LOCATION) { + Some(location) => Ok(location.to_str()?.to_string()), + None => Err(Error::new(ErrorKind::Other, "Location header not found").into()), + } + } else { + Err(Error::new(ErrorKind::Other, "Link not found").into()) + } +} diff --git a/src/zippy.rs b/src/integrations/zippy.rs similarity index 100% rename from src/zippy.rs rename to src/integrations/zippy.rs diff --git a/src/main.rs b/src/main.rs index 8a628ae..16f2c66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,15 +15,15 @@ use crate::args::CLIArgs; use crate::clireporter::cli_print_reports; use crate::dlreport::{DlReport, DlReporter}; use crate::download::{download_feedback, download_feedback_multi, http_file_info}; -use crate::zippy::is_zippyshare_url; +use crate::integrations::{is_integrated_url, resolve_integrated_url}; mod args; mod clireporter; mod dlreport; mod download; mod errors; +mod integrations; mod misc; -mod zippy; struct DlRequest { id: usize, @@ -113,20 +113,21 @@ async fn download_job(urls: SyncQueue, reporter: UnboundedSender, cli_ let reporter = DlReporter::new(dlreq.id as u32, reporter.clone()); // Resolve the zippy url to the direct download url if necessary - let url = if is_zippyshare_url(&dlreq.url) { - match zippy::resolve_link(&dlreq.url).await { - Ok(url) => url, - Err(_e) => { - report_msg!( - reporter, - "Zippyshare link could not be resolved, skipping: {}", - dlreq.url - ); - continue; + let url = match is_integrated_url(&dlreq.url) { + Some(service) => { + match resolve_integrated_url(&dlreq.url, service).await { + Ok(url) => url, + Err(_e) => { + report_msg!( + reporter, + "Zippyshare link could not be resolved, skipping: {}", + dlreq.url + ); + continue; + } } } - } else { - dlreq.url.to_string() + None => dlreq.url, }; let info = match http_file_info(&url).await {