1use log::{debug, error, warn};
2use reqwest::header::HeaderMap;
3use reqwest::{Client, RequestBuilder};
4use serde::de::DeserializeOwned;
5use types::account::UserData;
6use url::Url;
7
8use crate::api::error::EpicAPIError;
9
10pub mod types;
12
13pub mod utils;
15
16#[allow(dead_code)]
18pub(crate) mod binary_rw;
19
20pub mod error;
22
23pub mod fab;
25
26pub mod account;
28
29pub mod egs;
31#[allow(dead_code)]
32pub mod cloud_save;
34pub mod login;
36
37pub mod commerce;
39
40pub mod status;
42
43pub mod presence;
45
46pub mod store;
48
49pub mod cosmos;
51
52#[derive(Debug, Clone)]
53pub(crate) struct EpicAPI {
54 client: Client,
55 pub(crate) user_data: UserData,
56}
57
58impl Default for EpicAPI {
59 fn default() -> Self {
60 Self::new()
61 }
62}
63
64impl EpicAPI {
65 pub fn new() -> Self {
66 let mut headers = HeaderMap::new();
67 headers.insert(
68 "User-Agent",
69 "UELauncher/17.0.1-37584233+++Portal+Release-Live Windows/10.0.19043.1.0.64bit"
70 .parse()
71 .unwrap(),
72 );
73 headers.insert(
74 "X-Epic-Correlation-ID",
75 "UE4-c176f7154c2cda1061cc43ab52598e2b-93AFB486488A22FDF70486BD1D883628-BFCD88F649E997BA203FF69F07CE578C".parse().unwrap()
76 );
77 let client = reqwest::Client::builder()
78 .default_headers(headers)
79 .cookie_store(true)
80 .build()
81 .unwrap();
82 EpicAPI {
83 client,
84 user_data: Default::default(),
85 }
86 }
87
88 fn authorized_get_client(&self, url: Url) -> RequestBuilder {
89 self.set_authorization_header(self.client.get(url))
90 }
91
92 fn authorized_post_client(&self, url: Url) -> RequestBuilder {
93 self.set_authorization_header(self.client.post(url))
94 }
95
96 fn set_authorization_header(&self, rb: RequestBuilder) -> RequestBuilder {
97 rb.header(
98 "Authorization",
99 format!(
100 "{} {}",
101 self.user_data
102 .token_type
103 .as_ref()
104 .unwrap_or(&"bearer".to_string()),
105 self.user_data
106 .access_token
107 .as_ref()
108 .unwrap_or(&"".to_string())
109 ),
110 )
111 }
112
113 pub(crate) async fn authorized_get_json<T: DeserializeOwned>(
115 &self,
116 url: &str,
117 ) -> Result<T, EpicAPIError> {
118 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
119 debug!("authorized_get_json: {}", url);
120 let response = self
121 .authorized_get_client(parsed_url)
122 .send()
123 .await
124 .map_err(|e| {
125 error!("{:?}", e);
126 EpicAPIError::NetworkError(e)
127 })?;
128 if response.status() == reqwest::StatusCode::OK {
129 let body = response.text().await.map_err(|e| {
130 error!("Failed to read response body from {}: {:?}", url, e);
131 EpicAPIError::DeserializationError(format!("{}", e))
132 })?;
133 serde_json::from_str::<T>(&body).map_err(|e| {
134 error!("Deserialization failed for {}: {:?}", url, e);
135 error!("Response body: {}", &body[..body.len().min(2048)]);
136 EpicAPIError::DeserializationError(format!("{}", e))
137 })
138 } else {
139 let status = response.status();
140 let body = response.text().await.unwrap_or_default();
141 warn!("{} result: {}", status, body);
142 Err(EpicAPIError::HttpError { status, body })
143 }
144 }
145
146 pub(crate) async fn authorized_post_form_json<T: DeserializeOwned>(
148 &self,
149 url: &str,
150 form: &[(String, String)],
151 ) -> Result<T, EpicAPIError> {
152 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
153 debug!("authorized_post_form_json: {}", url);
154 let response = self
155 .authorized_post_client(parsed_url)
156 .form(form)
157 .send()
158 .await
159 .map_err(|e| {
160 error!("{:?}", e);
161 EpicAPIError::NetworkError(e)
162 })?;
163 if response.status() == reqwest::StatusCode::OK {
164 let body = response.text().await.map_err(|e| {
165 error!("Failed to read response body from {}: {:?}", url, e);
166 EpicAPIError::DeserializationError(format!("{}", e))
167 })?;
168 serde_json::from_str::<T>(&body).map_err(|e| {
169 error!("Deserialization failed for {}: {:?}", url, e);
170 error!("Response body: {}", &body[..body.len().min(2048)]);
171 EpicAPIError::DeserializationError(format!("{}", e))
172 })
173 } else {
174 let status = response.status();
175 let body = response.text().await.unwrap_or_default();
176 warn!("{} result: {}", status, body);
177 Err(EpicAPIError::HttpError { status, body })
178 }
179 }
180
181 pub(crate) async fn authorized_post_json<T: DeserializeOwned, B: serde::Serialize>(
183 &self,
184 url: &str,
185 body: &B,
186 ) -> Result<T, EpicAPIError> {
187 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
188 debug!("authorized_post_json: {}", url);
189 let response = self
190 .authorized_post_client(parsed_url)
191 .json(body)
192 .send()
193 .await
194 .map_err(|e| {
195 error!("{:?}", e);
196 EpicAPIError::NetworkError(e)
197 })?;
198 if response.status() == reqwest::StatusCode::OK {
199 let resp_body = response.text().await.map_err(|e| {
200 error!("Failed to read response body from {}: {:?}", url, e);
201 EpicAPIError::DeserializationError(format!("{}", e))
202 })?;
203 serde_json::from_str::<T>(&resp_body).map_err(|e| {
204 error!("Deserialization failed for {}: {:?}", url, e);
205 error!("Response body: {}", &resp_body[..resp_body.len().min(2048)]);
206 EpicAPIError::DeserializationError(format!("{}", e))
207 })
208 } else {
209 let status = response.status();
210 let body = response.text().await.unwrap_or_default();
211 warn!("{} result: {}", status, body);
212 Err(EpicAPIError::HttpError { status, body })
213 }
214 }
215
216 pub(crate) async fn get_bytes(&self, url: &str) -> Result<Vec<u8>, EpicAPIError> {
218 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
219 let response = self
220 .client
221 .get(parsed_url)
222 .send()
223 .await
224 .map_err(|e| {
225 error!("{:?}", e);
226 EpicAPIError::NetworkError(e)
227 })?;
228 if response.status() == reqwest::StatusCode::OK {
229 response.bytes().await.map(|b| b.to_vec()).map_err(|e| {
230 error!("{:?}", e);
231 EpicAPIError::DeserializationError(format!("{}", e))
232 })
233 } else {
234 let status = response.status();
235 let body = response.text().await.unwrap_or_default();
236 warn!("{} result: {}", status, body);
237 Err(EpicAPIError::HttpError { status, body })
238 }
239 }
240
241 pub(crate) async fn get_json<T: DeserializeOwned>(
243 &self,
244 url: &str,
245 ) -> Result<T, EpicAPIError> {
246 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
247 debug!("get_json: {}", url);
248 let response = self.client.get(parsed_url).send().await.map_err(|e| {
249 error!("{:?}", e);
250 EpicAPIError::NetworkError(e)
251 })?;
252 if response.status() == reqwest::StatusCode::OK {
253 let body = response.text().await.map_err(|e| {
254 error!("Failed to read response body from {}: {:?}", url, e);
255 EpicAPIError::DeserializationError(format!("{}", e))
256 })?;
257 serde_json::from_str::<T>(&body).map_err(|e| {
258 error!("Deserialization failed for {}: {:?}", url, e);
259 error!("Response body: {}", &body[..body.len().min(2048)]);
260 EpicAPIError::DeserializationError(format!("{}", e))
261 })
262 } else {
263 let status = response.status();
264 let body = response.text().await.unwrap_or_default();
265 warn!("{} result: {}", status, body);
266 Err(EpicAPIError::HttpError { status, body })
267 }
268 }
269
270 #[allow(dead_code)]
271 pub(crate) async fn authorized_delete(&self, url: &str) -> Result<(), EpicAPIError> {
273 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
274 let response = self
275 .set_authorization_header(self.client.delete(parsed_url))
276 .send()
277 .await
278 .map_err(|e| {
279 error!("{:?}", e);
280 EpicAPIError::NetworkError(e)
281 })?;
282 if response.status() == reqwest::StatusCode::OK
283 || response.status() == reqwest::StatusCode::NO_CONTENT
284 {
285 Ok(())
286 } else {
287 let status = response.status();
288 let body = response.text().await.unwrap_or_default();
289 warn!("{} result: {}", status, body);
290 Err(EpicAPIError::HttpError { status, body })
291 }
292 }
293}