1#![doc = include_str!("../../README.md")]
2#![cfg_attr(feature = "nightly", feature(doc_cfg))]
3
4mod types;
5pub use types::{Attachment, Cipher, CipherInfo, CompressionType};
6
7mod crypto;
8pub use crypto::decrypt_with_key;
9
10mod error;
11pub use error::Error;
12
13#[allow(unused)]
15#[derive(Debug, Clone)]
16pub struct Paste<'a> {
17 key: Vec<u8>,
18 key_base58: &'a str,
19 pasteid: &'a str,
20 base_url: &'a str,
21}
22
23pub type Result<T> = std::result::Result<T, Error>;
25
26impl Paste<'_> {
27 pub fn parse_url<'a>(url: &'a str) -> Result<Paste<'a>> {
39 let (base_url, pasteinfo) = url.split_once('?').ok_or(Error::IllFormedURL)?;
40 let (pasteid, key_base58) = pasteinfo.split_once("#").ok_or(Error::IllFormedURL)?;
41
42 let paste = Self::try_from_key_and_pasteid(key_base58, pasteid)?;
43 Ok(Paste { base_url, ..paste })
44 }
45
46 pub fn try_from_key_and_pasteid<'a>(
69 key_base58: &'a str,
70 pasteid: &'a str,
71 ) -> Result<Paste<'a>> {
72 let key = bs58::decode(key_base58).into_vec()?;
73
74 if key.len() != 32 {
75 return Err(Error::KeyLengthMismatch(key.len()));
76 }
77
78 let base_url = "https://paste.fitgirl-repacks.site/";
79
80 Ok(Paste {
81 key,
82 key_base58,
83 pasteid,
84 base_url,
85 })
86 }
87
88 pub fn decrypt(&self, cipher: impl AsRef<CipherInfo>) -> Result<Attachment> {
90 decrypt_with_key(&self.master_key(), cipher.as_ref())
91 }
92
93 pub fn master_key(&self) -> &Vec<u8> {
94 &self.key
95 }
96
97 #[cfg_attr(feature = "nightly", doc(cfg(feature = "ureq")))]
99 #[cfg(feature = "ureq")]
100 pub fn request(&self) -> Result<CipherInfo> {
101 use ureq::{http::header::ACCEPT, Agent};
102
103 let pasteid = self.pasteid;
104 let key_base58 = self.key_base58;
105
106 let base = self.base_url;
107 let init_cookies = format!("{base}?{pasteid}#{key_base58}");
108 let cipher_info = format!("{base}?pasteid={pasteid}");
109
110 let agent = Agent::new_with_defaults();
111
112 agent.get(init_cookies).call()?;
113
114 let resp = agent
115 .get(cipher_info)
116 .header(ACCEPT, "application/json")
117 .call()?
118 .body_mut()
119 .read_json()?;
120 Ok(resp)
121 }
122
123 #[cfg_attr(feature = "nightly", doc(cfg(feature = "reqwest")))]
133 #[cfg(feature = "reqwest")]
134 pub async fn request_async(&self) -> Result<CipherInfo> {
135 use reqwest::{header::ACCEPT, ClientBuilder};
136
137 let pasteid = self.pasteid;
138 let key_base58 = self.key_base58;
139
140 let base = self.base_url;
141 let init_cookies = format!("{base}?{pasteid}#{key_base58}");
142 let cipher_info = format!("{base}?pasteid={pasteid}");
143
144 let client = ClientBuilder::new().gzip(true).build()?;
145 client.get(init_cookies).send().await?;
146
147 let resp = client
148 .get(cipher_info)
149 .header(ACCEPT, "application/json")
150 .send()
151 .await?
152 .json()
153 .await?;
154 Ok(resp)
155 }
156
157 #[cfg_attr(feature = "nightly", doc(cfg(feature = "nyquest")))]
161 #[cfg(feature = "nyquest")]
162 pub async fn request_async_ny(&self) -> Result<CipherInfo> {
163 use nyquest::{r#async::Request, ClientBuilder};
164
165 let pasteid = self.pasteid;
166 let key_base58 = self.key_base58;
167
168 let base = self.base_url;
169 let init_cookies = format!("/?{pasteid}#{key_base58}");
170 let cipher_info = format!("/?pasteid={pasteid}");
171
172 let client = ClientBuilder::default()
173 .base_url(base)
174 .build_async()
175 .await?;
176 client.request(Request::get(init_cookies)).await?;
177
178 let resp = client
179 .request(Request::get(cipher_info).with_header("Accept", "application/json"))
180 .await?
181 .json()
182 .await?;
183 Ok(resp)
184 }
185}
186
187pub use base64;