guerrillamail_client/
client.rs1use crate::{Error, Message, Result};
4use regex::Regex;
5use reqwest::header::{
6 HeaderMap, HeaderValue, ACCEPT, ACCEPT_LANGUAGE, CONTENT_TYPE, HOST, ORIGIN, REFERER,
7 USER_AGENT,
8};
9use std::time::{SystemTime, UNIX_EPOCH};
10
11const BASE_URL: &str = "https://www.guerrillamail.com";
12const AJAX_URL: &str = "https://www.guerrillamail.com/ajax.php";
13const USER_AGENT_VALUE: &str =
14 "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0";
15
16#[derive(Debug)]
18pub struct Client {
19 http: reqwest::Client,
20 api_token: String,
21 domains: Vec<String>,
22 proxy: Option<String>,
23}
24
25impl Client {
26 pub async fn new() -> Result<Self> {
30 Self::with_proxy(None).await
31 }
32
33 pub async fn with_proxy(proxy: Option<&str>) -> Result<Self> {
38 let mut builder = reqwest::Client::builder().danger_accept_invalid_certs(true);
39
40 if let Some(proxy_url) = proxy {
41 builder = builder.proxy(reqwest::Proxy::all(proxy_url)?);
42 }
43
44 let http = builder.cookie_store(true).build()?;
46
47 let response = http.get(BASE_URL).send().await?.text().await?;
49
50 let token_re = Regex::new(r"api_token\s*:\s*'(\w+)'").unwrap();
52 let api_token = token_re
53 .captures(&response)
54 .and_then(|c| c.get(1))
55 .map(|m| m.as_str().to_string())
56 .ok_or(Error::TokenParse)?;
57
58 let domain_re = Regex::new(r#"<option value="([\w.]+)">"#).unwrap();
60 let domains: Vec<String> = domain_re
61 .captures_iter(&response)
62 .filter_map(|c| c.get(1).map(|m| m.as_str().to_string()))
63 .collect();
64
65 if domains.is_empty() {
66 return Err(Error::DomainParse);
67 }
68
69 Ok(Self {
70 http,
71 api_token,
72 domains,
73 proxy: proxy.map(|s| s.to_string()),
74 })
75 }
76
77 pub fn domains(&self) -> &[String] {
79 &self.domains
80 }
81
82 pub fn proxy(&self) -> Option<&str> {
84 self.proxy.as_deref()
85 }
86
87 pub async fn create_email(&self, alias: &str, _domain: Option<&str>) -> Result<String> {
96 let params = [("f", "set_email_user")];
97 let form = [
98 ("email_user", alias),
99 ("lang", "en"),
100 ("site", "guerrillamail.com"),
101 ("in", " Set cancel"),
102 ];
103
104 let response: serde_json::Value = self
105 .http
106 .post(AJAX_URL)
107 .query(¶ms)
108 .form(&form)
109 .headers(self.headers())
110 .send()
111 .await?
112 .error_for_status()?
113 .json()
114 .await?;
115
116 response
117 .get("email_addr")
118 .and_then(|v| v.as_str())
119 .map(|s| s.to_string())
120 .ok_or(Error::TokenParse)
121 }
122
123 pub async fn get_messages(&self, email: &str) -> Result<Vec<Message>> {
131 let response = self.get_api("check_email", email, None).await?;
132
133 let messages = response
134 .get("list")
135 .and_then(|v| v.as_array())
136 .map(|arr| {
137 arr.iter()
138 .filter_map(|v| serde_json::from_value::<Message>(v.clone()).ok())
139 .collect()
140 })
141 .unwrap_or_default();
142
143 Ok(messages)
144 }
145
146 pub async fn fetch_email(&self, email: &str, mail_id: &str) -> Result<crate::EmailDetails> {
155 let response = self.get_api("fetch_email", email, Some(mail_id)).await?;
156 serde_json::from_value(response).map_err(|_| Error::TokenParse)
157 }
158
159 pub async fn delete_email(&self, email: &str) -> Result<bool> {
167 let alias = Self::extract_alias(email);
168 let params = [("f", "forget_me")];
169 let form = [("site", "guerrillamail.com"), ("in", alias)];
170
171 let response = self
172 .http
173 .post(AJAX_URL)
174 .query(¶ms)
175 .form(&form)
176 .headers(self.headers())
177 .send()
178 .await?;
179
180 Ok(response.status().is_success())
181 }
182
183 async fn get_api(
185 &self,
186 function: &str,
187 email: &str,
188 email_id: Option<&str>,
189 ) -> Result<serde_json::Value> {
190 let alias = Self::extract_alias(email);
191 let timestamp = Self::timestamp();
192
193 let mut params = vec![
194 ("f", function.to_string()),
195 ("site", "guerrillamail.com".to_string()),
196 ("in", alias.to_string()),
197 ("_", timestamp),
198 ];
199
200 if let Some(id) = email_id {
201 params.insert(1, ("email_id", id.to_string()));
202 }
203
204 if function == "check_email" {
205 params.insert(1, ("seq", "1".to_string()));
206 }
207
208 let mut headers = self.headers();
209 headers.remove(CONTENT_TYPE);
210
211 self.http
212 .get(AJAX_URL)
213 .query(¶ms)
214 .headers(headers)
215 .send()
216 .await?
217 .error_for_status()?
218 .json()
219 .await
220 .map_err(Into::into)
221 }
222
223 fn extract_alias(email: &str) -> &str {
225 email.split('@').next().unwrap_or(email)
226 }
227
228 fn timestamp() -> String {
230 SystemTime::now()
231 .duration_since(UNIX_EPOCH)
232 .unwrap()
233 .as_millis()
234 .to_string()
235 }
236
237 fn headers(&self) -> HeaderMap {
239 let mut headers = HeaderMap::new();
240 headers.insert(HOST, HeaderValue::from_static("www.guerrillamail.com"));
241 headers.insert(USER_AGENT, HeaderValue::from_static(USER_AGENT_VALUE));
242 headers.insert(
243 ACCEPT,
244 HeaderValue::from_static("application/json, text/javascript, */*; q=0.01"),
245 );
246 headers.insert(ACCEPT_LANGUAGE, HeaderValue::from_static("en-US,en;q=0.5"));
247 headers.insert(
248 CONTENT_TYPE,
249 HeaderValue::from_static("application/x-www-form-urlencoded; charset=UTF-8"),
250 );
251 headers.insert(
252 "Authorization",
253 HeaderValue::from_str(&format!("ApiToken {}", self.api_token)).unwrap(),
254 );
255 headers.insert(
256 "X-Requested-With",
257 HeaderValue::from_static("XMLHttpRequest"),
258 );
259 headers.insert(
260 ORIGIN,
261 HeaderValue::from_static("https://www.guerrillamail.com"),
262 );
263 headers.insert(
264 REFERER,
265 HeaderValue::from_static("https://www.guerrillamail.com/"),
266 );
267 headers.insert("Sec-Fetch-Dest", HeaderValue::from_static("empty"));
268 headers.insert("Sec-Fetch-Mode", HeaderValue::from_static("cors"));
269 headers.insert("Sec-Fetch-Site", HeaderValue::from_static("same-origin"));
270 headers.insert("Priority", HeaderValue::from_static("u=0"));
271 headers
272 }
273}