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
29#[allow(dead_code)]
30pub mod cloud_save;
32pub mod egs;
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 async fn send(request: RequestBuilder) -> Result<reqwest::Response, EpicAPIError> {
114 request.send().await.map_err(|e| {
115 error!("{:?}", e);
116 EpicAPIError::NetworkError(e)
117 })
118 }
119
120 fn require_ok(response: &reqwest::Response) -> Result<(), ()> {
121 if response.status() == reqwest::StatusCode::OK {
122 Ok(())
123 } else {
124 Err(())
125 }
126 }
127
128 async fn error_response(response: reqwest::Response) -> EpicAPIError {
129 let status = response.status();
130 let body = response.text().await.unwrap_or_default();
131 warn!("{} result: {}", status, body);
132 EpicAPIError::HttpError { status, body }
133 }
134
135 async fn read_json<T: DeserializeOwned>(
136 response: reqwest::Response,
137 url: &str,
138 ) -> Result<T, EpicAPIError> {
139 let body = response.text().await.map_err(|e| {
140 error!("Failed to read response body from {}: {:?}", url, e);
141 EpicAPIError::DeserializationError(format!("{}", e))
142 })?;
143 serde_json::from_str::<T>(&body).map_err(|e| {
144 error!("Deserialization failed for {}: {:?}", url, e);
145 error!("Response body: {}", &body[..body.len().min(2048)]);
146 EpicAPIError::DeserializationError(format!("{}", e))
147 })
148 }
149
150 async fn send_and_deserialize<T: DeserializeOwned>(
151 request: RequestBuilder,
152 url: &str,
153 ) -> Result<T, EpicAPIError> {
154 let response = Self::send(request).await?;
155 if Self::require_ok(&response).is_ok() {
156 Self::read_json(response, url).await
157 } else {
158 Err(Self::error_response(response).await)
159 }
160 }
161
162 pub(crate) async fn authorized_get_json<T: DeserializeOwned>(
163 &self,
164 url: &str,
165 ) -> Result<T, EpicAPIError> {
166 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
167 debug!("authorized_get_json: {}", url);
168 Self::send_and_deserialize(self.authorized_get_client(parsed_url), url).await
169 }
170
171 pub(crate) async fn authorized_post_form_json<T: DeserializeOwned>(
172 &self,
173 url: &str,
174 form: &[(String, String)],
175 ) -> Result<T, EpicAPIError> {
176 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
177 debug!("authorized_post_form_json: {}", url);
178 Self::send_and_deserialize(self.authorized_post_client(parsed_url).form(form), url).await
179 }
180
181 pub(crate) async fn authorized_post_json<T: DeserializeOwned, B: serde::Serialize>(
182 &self,
183 url: &str,
184 body: &B,
185 ) -> Result<T, EpicAPIError> {
186 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
187 debug!("authorized_post_json: {}", url);
188 Self::send_and_deserialize(self.authorized_post_client(parsed_url).json(body), url).await
189 }
190
191 pub(crate) async fn get_bytes(&self, url: &str) -> Result<Vec<u8>, EpicAPIError> {
192 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
193 let response = Self::send(self.client.get(parsed_url)).await?;
194 if Self::require_ok(&response).is_ok() {
195 response.bytes().await.map(|b| b.to_vec()).map_err(|e| {
196 error!("{:?}", e);
197 EpicAPIError::DeserializationError(format!("{}", e))
198 })
199 } else {
200 Err(Self::error_response(response).await)
201 }
202 }
203
204 pub(crate) async fn get_json<T: DeserializeOwned>(&self, url: &str) -> Result<T, EpicAPIError> {
205 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
206 debug!("get_json: {}", url);
207 Self::send_and_deserialize(self.client.get(parsed_url), url).await
208 }
209
210 #[allow(dead_code)]
211 pub(crate) async fn authorized_delete(&self, url: &str) -> Result<(), EpicAPIError> {
212 let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
213 let response =
214 Self::send(self.set_authorization_header(self.client.delete(parsed_url))).await?;
215 if response.status() == reqwest::StatusCode::OK
216 || response.status() == reqwest::StatusCode::NO_CONTENT
217 {
218 Ok(())
219 } else {
220 Err(Self::error_response(response).await)
221 }
222 }
223}