1use bytes::Buf;
19use http::header;
20use http::Request;
21use http::Response;
22use http::StatusCode;
23use serde::Deserialize;
24use std::fmt::Debug;
25use std::fmt::Formatter;
26use std::sync::Arc;
27
28use super::error::parse_error;
29use super::error::PcloudError;
30use crate::raw::*;
31use crate::*;
32
33#[derive(Clone)]
34pub struct PcloudCore {
35 pub info: Arc<AccessorInfo>,
36
37 pub root: String,
39 pub endpoint: String,
41 pub username: String,
43 pub password: String,
45}
46
47impl Debug for PcloudCore {
48 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
49 f.debug_struct("Backend")
50 .field("root", &self.root)
51 .field("endpoint", &self.endpoint)
52 .field("username", &self.username)
53 .finish_non_exhaustive()
54 }
55}
56
57impl PcloudCore {
58 #[inline]
59 pub async fn send(&self, req: Request<Buffer>) -> Result<Response<Buffer>> {
60 self.info.http_client().send(req).await
61 }
62}
63
64impl PcloudCore {
65 pub async fn get_file_link(&self, path: &str) -> Result<String> {
66 let path = build_abs_path(&self.root, path);
67
68 let url = format!(
69 "{}/getfilelink?path=/{}&username={}&password={}",
70 self.endpoint,
71 percent_encode_path(&path),
72 self.username,
73 self.password
74 );
75
76 let req = Request::get(url);
77
78 let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
80
81 let resp = self.send(req).await?;
82
83 let status = resp.status();
84 match status {
85 StatusCode::OK => {
86 let bs = resp.into_body();
87 let resp: GetFileLinkResponse =
88 serde_json::from_reader(bs.reader()).map_err(new_json_deserialize_error)?;
89 let result = resp.result;
90 if result == 2010 || result == 2055 || result == 2002 {
91 return Err(Error::new(ErrorKind::NotFound, format!("{resp:?}")));
92 }
93 if result != 0 {
94 return Err(Error::new(ErrorKind::Unexpected, format!("{resp:?}")));
95 }
96
97 if let Some(hosts) = resp.hosts {
98 if let Some(path) = resp.path {
99 if !hosts.is_empty() {
100 return Ok(format!("https://{}{}", hosts[0], path));
101 }
102 }
103 }
104 Err(Error::new(ErrorKind::Unexpected, "hosts is empty"))
105 }
106 _ => Err(parse_error(resp)),
107 }
108 }
109
110 pub async fn download(&self, url: &str, range: BytesRange) -> Result<Response<HttpBody>> {
111 let req = Request::get(url);
112
113 let req = req
115 .header(header::RANGE, range.to_header())
116 .body(Buffer::new())
117 .map_err(new_request_build_error)?;
118
119 self.info.http_client().fetch(req).await
120 }
121
122 pub async fn ensure_dir_exists(&self, path: &str) -> Result<()> {
123 let path = build_abs_path(&self.root, path);
124
125 let paths = path.split('/').collect::<Vec<&str>>();
126
127 for i in 0..paths.len() - 1 {
128 let path = paths[..i + 1].join("/");
129 let resp = self.create_folder_if_not_exists(&path).await?;
130
131 let status = resp.status();
132
133 match status {
134 StatusCode::OK => {
135 let bs = resp.into_body();
136 let resp: PcloudError =
137 serde_json::from_reader(bs.reader()).map_err(new_json_deserialize_error)?;
138 let result = resp.result;
139 if result == 2010 || result == 2055 || result == 2002 {
140 return Err(Error::new(ErrorKind::NotFound, format!("{resp:?}")));
141 }
142 if result != 0 {
143 return Err(Error::new(ErrorKind::Unexpected, format!("{resp:?}")));
144 }
145
146 if result != 0 {
147 return Err(Error::new(ErrorKind::Unexpected, format!("{resp:?}")));
148 }
149 }
150 _ => return Err(parse_error(resp)),
151 }
152 }
153 Ok(())
154 }
155
156 pub async fn create_folder_if_not_exists(&self, path: &str) -> Result<Response<Buffer>> {
157 let url = format!(
158 "{}/createfolderifnotexists?path=/{}&username={}&password={}",
159 self.endpoint,
160 percent_encode_path(path),
161 self.username,
162 self.password
163 );
164
165 let req = Request::post(url);
166
167 let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
169
170 self.send(req).await
171 }
172
173 pub async fn rename_file(&self, from: &str, to: &str) -> Result<Response<Buffer>> {
174 let from = build_abs_path(&self.root, from);
175 let to = build_abs_path(&self.root, to);
176
177 let url = format!(
178 "{}/renamefile?path=/{}&topath=/{}&username={}&password={}",
179 self.endpoint,
180 percent_encode_path(&from),
181 percent_encode_path(&to),
182 self.username,
183 self.password
184 );
185
186 let req = Request::post(url);
187
188 let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
190
191 self.send(req).await
192 }
193
194 pub async fn rename_folder(&self, from: &str, to: &str) -> Result<Response<Buffer>> {
195 let from = build_abs_path(&self.root, from);
196 let to = build_abs_path(&self.root, to);
197 let url = format!(
198 "{}/renamefolder?path=/{}&topath=/{}&username={}&password={}",
199 self.endpoint,
200 percent_encode_path(&from),
201 percent_encode_path(&to),
202 self.username,
203 self.password
204 );
205
206 let req = Request::post(url);
207
208 let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
210
211 self.send(req).await
212 }
213
214 pub async fn delete_folder(&self, path: &str) -> Result<Response<Buffer>> {
215 let path = build_abs_path(&self.root, path);
216
217 let url = format!(
218 "{}/deletefolder?path=/{}&username={}&password={}",
219 self.endpoint,
220 percent_encode_path(&path),
221 self.username,
222 self.password
223 );
224
225 let req = Request::post(url);
226
227 let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
229
230 self.send(req).await
231 }
232
233 pub async fn delete_file(&self, path: &str) -> Result<Response<Buffer>> {
234 let path = build_abs_path(&self.root, path);
235
236 let url = format!(
237 "{}/deletefile?path=/{}&username={}&password={}",
238 self.endpoint,
239 percent_encode_path(&path),
240 self.username,
241 self.password
242 );
243
244 let req = Request::post(url);
245
246 let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
248
249 self.send(req).await
250 }
251
252 pub async fn copy_file(&self, from: &str, to: &str) -> Result<Response<Buffer>> {
253 let from = build_abs_path(&self.root, from);
254 let to = build_abs_path(&self.root, to);
255
256 let url = format!(
257 "{}/copyfile?path=/{}&topath=/{}&username={}&password={}",
258 self.endpoint,
259 percent_encode_path(&from),
260 percent_encode_path(&to),
261 self.username,
262 self.password
263 );
264
265 let req = Request::post(url);
266
267 let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
269
270 self.send(req).await
271 }
272
273 pub async fn copy_folder(&self, from: &str, to: &str) -> Result<Response<Buffer>> {
274 let from = build_abs_path(&self.root, from);
275 let to = build_abs_path(&self.root, to);
276
277 let url = format!(
278 "{}/copyfolder?path=/{}&topath=/{}&username={}&password={}",
279 self.endpoint,
280 percent_encode_path(&from),
281 percent_encode_path(&to),
282 self.username,
283 self.password
284 );
285
286 let req = Request::post(url);
287
288 let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
290
291 self.send(req).await
292 }
293
294 pub async fn stat(&self, path: &str) -> Result<Response<Buffer>> {
295 let path = build_abs_path(&self.root, path);
296
297 let path = path.trim_end_matches('/');
298
299 let url = format!(
300 "{}/stat?path=/{}&username={}&password={}",
301 self.endpoint,
302 percent_encode_path(path),
303 self.username,
304 self.password
305 );
306
307 let req = Request::post(url);
308
309 let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
311
312 self.send(req).await
313 }
314
315 pub async fn upload_file(&self, path: &str, bs: Buffer) -> Result<Response<Buffer>> {
316 let path = build_abs_path(&self.root, path);
317
318 let (name, path) = (get_basename(&path), get_parent(&path).trim_end_matches('/'));
319
320 let url = format!(
321 "{}/uploadfile?path=/{}&filename={}&username={}&password={}",
322 self.endpoint,
323 percent_encode_path(path),
324 percent_encode_path(name),
325 self.username,
326 self.password
327 );
328
329 let req = Request::put(url);
330
331 let req = req.body(bs).map_err(new_request_build_error)?;
333
334 self.send(req).await
335 }
336
337 pub async fn list_folder(&self, path: &str) -> Result<Response<Buffer>> {
338 let path = build_abs_path(&self.root, path);
339
340 let path = normalize_root(&path);
341
342 let path = path.trim_end_matches('/');
343
344 let url = format!(
345 "{}/listfolder?path={}&username={}&password={}",
346 self.endpoint,
347 percent_encode_path(path),
348 self.username,
349 self.password
350 );
351
352 let req = Request::get(url);
353
354 let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
356
357 self.send(req).await
358 }
359}
360
361pub(super) fn parse_stat_metadata(content: StatMetadata) -> Result<Metadata> {
362 let mut md = if content.isfolder {
363 Metadata::new(EntryMode::DIR)
364 } else {
365 Metadata::new(EntryMode::FILE)
366 };
367
368 if let Some(size) = content.size {
369 md.set_content_length(size);
370 }
371
372 md.set_last_modified(parse_datetime_from_rfc2822(&content.modified)?);
373
374 Ok(md)
375}
376
377pub(super) fn parse_list_metadata(content: ListMetadata) -> Result<Metadata> {
378 let mut md = if content.isfolder {
379 Metadata::new(EntryMode::DIR)
380 } else {
381 Metadata::new(EntryMode::FILE)
382 };
383
384 if let Some(size) = content.size {
385 md.set_content_length(size);
386 }
387
388 md.set_last_modified(parse_datetime_from_rfc2822(&content.modified)?);
389
390 Ok(md)
391}
392
393#[derive(Debug, Deserialize)]
394pub struct GetFileLinkResponse {
395 pub result: u64,
396 pub path: Option<String>,
397 pub hosts: Option<Vec<String>>,
398}
399
400#[derive(Debug, Deserialize)]
401pub struct StatResponse {
402 pub result: u64,
403 pub metadata: Option<StatMetadata>,
404}
405
406#[derive(Debug, Deserialize)]
407pub struct StatMetadata {
408 pub modified: String,
409 pub isfolder: bool,
410 pub size: Option<u64>,
411}
412
413#[derive(Debug, Deserialize)]
414pub struct ListFolderResponse {
415 pub result: u64,
416 pub metadata: Option<ListMetadata>,
417}
418
419#[derive(Debug, Deserialize)]
420pub struct ListMetadata {
421 pub path: String,
422 pub modified: String,
423 pub isfolder: bool,
424 pub size: Option<u64>,
425 pub contents: Option<Vec<ListMetadata>>,
426}