libmtp_rs/storage/
files.rs

1//! Contains relevant items to handle file objects in the device.
2
3use chrono::{DateTime, TimeZone, Utc};
4use libmtp_sys as ffi;
5use num_traits::FromPrimitive;
6use std::ffi::{CStr, CString};
7use std::fmt::{self, Debug};
8use std::path::Path;
9
10#[cfg(unix)]
11use std::os::unix::io::AsRawFd;
12
13use crate::device::MtpDevice;
14use crate::object::filetypes::Filetype;
15use crate::object::{AsObjectId, Object};
16use crate::storage::Parent;
17use crate::util::data_get_func_handler;
18use crate::util::data_put_func_handler;
19use crate::util::progress_func_handler;
20use crate::util::{CallbackReturn, HandlerReturn};
21use crate::Result;
22
23/// Abstraction of a file object, it implements `Object`, you may want to use
24/// this struct to create a tree representation of one storage.
25pub struct File<'a> {
26    pub(crate) inner: *mut ffi::LIBMTP_file_t,
27    pub(crate) owner: &'a MtpDevice,
28}
29
30impl Drop for File<'_> {
31    fn drop(&mut self) {
32        unsafe {
33            ffi::LIBMTP_destroy_file_t(self.inner);
34        }
35    }
36}
37
38impl Object for File<'_> {
39    fn id(&self) -> u32 {
40        unsafe { (*self.inner).item_id }
41    }
42
43    fn device(&self) -> &MtpDevice {
44        self.owner
45    }
46}
47
48impl Object for &File<'_> {
49    fn id(&self) -> u32 {
50        unsafe { (*self.inner).item_id }
51    }
52
53    fn device(&self) -> &MtpDevice {
54        self.owner
55    }
56}
57
58impl Debug for File<'_> {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        f.debug_struct("File")
61            .field("id", &self.id())
62            .field("parent_id", &self.parent_id())
63            .field("storage_id", &self.storage_id())
64            .field("size", &self.size())
65            .field("name", &self.name())
66            .field("ftype", &self.ftype())
67            .field("modification_date", &self.modification_date())
68            .finish()
69    }
70}
71
72impl File<'_> {
73    /// Returns the id of the storage it belongs to.
74    pub fn storage_id(&self) -> u32 {
75        unsafe { (*self.inner).storage_id }
76    }
77
78    /// Returns the id of its parent.
79    pub fn parent_id(&self) -> Parent {
80        let id = unsafe { (*self.inner).parent_id };
81
82        if id == ffi::LIBMTP_FILES_AND_FOLDERS_ROOT {
83            Parent::Root
84        } else {
85            Parent::Folder(id)
86        }
87    }
88
89    /// Returns the size of this file.
90    pub fn size(&self) -> u64 {
91        unsafe { (*self.inner).filesize }
92    }
93
94    /// Returns the name of this file.
95    pub fn name(&self) -> &str {
96        unsafe {
97            let cstr = CStr::from_ptr((*self.inner).filename);
98            cstr.to_str().expect("Invalid UTF-8 on file name")
99        }
100    }
101
102    /// Returns the type of this file.
103    pub fn ftype(&self) -> Filetype {
104        let ftype = unsafe { (*self.inner).filetype };
105        Filetype::from_u32(ftype).expect("Unexpected raw variant of Filetype")
106    }
107
108    /// Returns the latest modification date in UTC.
109    pub fn modification_date(&self) -> DateTime<Utc> {
110        let epoch = unsafe { (*self.inner).modificationdate };
111        Utc.timestamp(epoch, 0)
112    }
113
114    /// Rename this file in-place.
115    pub fn rename(&mut self, new_name: &str) -> Result<()> {
116        let new_name = CString::new(new_name).expect("Nul byte");
117
118        let res = unsafe {
119            ffi::LIBMTP_Set_File_Name(self.owner.inner, self.inner, new_name.as_ptr() as *const _)
120        };
121
122        if res != 0 {
123            Err(self.owner.latest_error().unwrap_or_default())
124        } else {
125            Ok(())
126        }
127    }
128}
129
130/// Convenience struct used as a parameter to send local files to an MTP device.
131#[derive(Debug, Clone)]
132pub struct FileMetadata<'a> {
133    pub file_size: u64,
134    pub file_name: &'a str,
135    pub file_type: Filetype,
136    pub modification_date: DateTime<Utc>,
137}
138
139pub(crate) fn get_file_to_path(
140    mtpdev: &MtpDevice,
141    file: impl AsObjectId,
142    path: impl AsRef<Path>,
143) -> Result<()> {
144    let path = path.as_ref();
145    let path = path_to_cvec!(path);
146
147    let res = unsafe {
148        ffi::LIBMTP_Get_File_To_File(
149            mtpdev.inner,
150            file.as_id(),
151            path.as_ptr() as *const _,
152            None,
153            std::ptr::null(),
154        )
155    };
156
157    if res != 0 {
158        Err(mtpdev.latest_error().unwrap_or_default())
159    } else {
160        Ok(())
161    }
162}
163
164pub(crate) fn get_file_to_path_with_callback<C>(
165    mtpdev: &MtpDevice,
166    file: impl AsObjectId,
167    path: impl AsRef<Path>,
168    mut callback: C,
169) -> Result<()>
170where
171    C: FnMut(u64, u64) -> CallbackReturn,
172{
173    let path = path.as_ref();
174    let path = path_to_cvec!(path);
175
176    let mut callback: &mut dyn FnMut(u64, u64) -> CallbackReturn = &mut callback;
177    let callback = &mut callback as *mut _ as *mut libc::c_void as *const _;
178
179    let res = unsafe {
180        ffi::LIBMTP_Get_File_To_File(
181            mtpdev.inner,
182            file.as_id(),
183            path.as_ptr() as *const _,
184            Some(progress_func_handler),
185            callback,
186        )
187    };
188
189    if res != 0 {
190        Err(mtpdev.latest_error().unwrap_or_default())
191    } else {
192        Ok(())
193    }
194}
195
196#[cfg(unix)]
197pub(crate) fn get_file_to_descriptor(
198    mtpdev: &MtpDevice,
199    file: impl AsObjectId,
200    descriptor: impl AsRawFd,
201) -> Result<()> {
202    let res = unsafe {
203        ffi::LIBMTP_Get_File_To_File_Descriptor(
204            mtpdev.inner,
205            file.as_id(),
206            descriptor.as_raw_fd(),
207            None,
208            std::ptr::null(),
209        )
210    };
211
212    if res != 0 {
213        Err(mtpdev.latest_error().unwrap_or_default())
214    } else {
215        Ok(())
216    }
217}
218
219#[cfg(unix)]
220pub(crate) fn get_file_to_descriptor_with_callback<C>(
221    mtpdev: &MtpDevice,
222    file: impl AsObjectId,
223    descriptor: impl AsRawFd,
224    mut callback: C,
225) -> Result<()>
226where
227    C: FnMut(u64, u64) -> CallbackReturn,
228{
229    let mut callback: &mut dyn FnMut(u64, u64) -> CallbackReturn = &mut callback;
230    let callback = &mut callback as *mut _ as *mut libc::c_void as *const _;
231
232    let res = unsafe {
233        ffi::LIBMTP_Get_File_To_File_Descriptor(
234            mtpdev.inner,
235            file.as_id(),
236            descriptor.as_raw_fd(),
237            Some(progress_func_handler),
238            callback,
239        )
240    };
241
242    if res != 0 {
243        Err(mtpdev.latest_error().unwrap_or_default())
244    } else {
245        Ok(())
246    }
247}
248
249pub(crate) fn get_file_to_handler<H>(
250    mtpdev: &MtpDevice,
251    file: impl AsObjectId,
252    mut handler: H,
253) -> Result<()>
254where
255    H: FnMut(&[u8]) -> HandlerReturn,
256{
257    let handler: &mut dyn FnMut(&[u8]) -> HandlerReturn = &mut handler;
258    let mut handler_return = HandlerReturn::Ok(0);
259
260    let private = &mut (&mut handler_return, handler) as *mut _ as *mut libc::c_void;
261
262    let res = unsafe {
263        ffi::LIBMTP_Get_File_To_Handler(
264            mtpdev.inner,
265            file.as_id(),
266            Some(data_put_func_handler),
267            private,
268            None,
269            std::ptr::null(),
270        )
271    };
272
273    if res != 0 && handler_return.is_error() {
274        Err(mtpdev.latest_error().unwrap_or_default())
275    } else {
276        if handler_return.is_cancel() {
277            let _ = mtpdev.latest_error();
278        }
279
280        Ok(())
281    }
282}
283
284pub(crate) fn get_file_to_handler_with_callback<H, C>(
285    mtpdev: &MtpDevice,
286    file: impl AsObjectId,
287    mut handler: H,
288    mut callback: C,
289) -> Result<()>
290where
291    H: FnMut(&[u8]) -> HandlerReturn,
292    C: FnMut(u64, u64) -> CallbackReturn,
293{
294    let handler: &mut dyn FnMut(&[u8]) -> HandlerReturn = &mut handler;
295    let mut handler_return = HandlerReturn::Ok(0);
296
297    let private = &mut (&mut handler_return, handler) as *mut _ as *mut libc::c_void;
298
299    let mut callback: &mut dyn FnMut(u64, u64) -> CallbackReturn = &mut callback;
300    let callback = &mut callback as *mut _ as *mut libc::c_void as *const _;
301
302    let res = unsafe {
303        ffi::LIBMTP_Get_File_To_Handler(
304            mtpdev.inner,
305            file.as_id(),
306            Some(data_put_func_handler),
307            private,
308            Some(progress_func_handler),
309            callback,
310        )
311    };
312
313    if res != 0 && handler_return.is_error() {
314        Err(mtpdev.latest_error().unwrap_or_default())
315    } else {
316        if handler_return.is_cancel() {
317            let _ = mtpdev.latest_error();
318        }
319
320        Ok(())
321    }
322}
323
324pub(crate) fn send_file_from_path<'a>(
325    mtpdev: &'a MtpDevice,
326    storage_id: u32,
327    path: impl AsRef<Path>,
328    parent: Parent,
329    metadata: FileMetadata<'_>,
330) -> Result<File<'a>> {
331    let path = path.as_ref();
332    let path = path_to_cvec!(path);
333
334    let file_t = unsafe { ffi::LIBMTP_new_file_t() };
335    unsafe { fill_file_t!(metadata, parent.to_id(), storage_id, file_t) };
336
337    let res = unsafe {
338        ffi::LIBMTP_Send_File_From_File(
339            mtpdev.inner,
340            path.as_ptr() as *const _,
341            file_t,
342            None,
343            std::ptr::null(),
344        )
345    };
346
347    if res != 0 {
348        Err(mtpdev.latest_error().unwrap_or_default())
349    } else {
350        Ok(File {
351            inner: file_t,
352            owner: mtpdev,
353        })
354    }
355}
356
357pub(crate) fn send_file_from_path_with_callback<'a, C>(
358    mtpdev: &'a MtpDevice,
359    storage_id: u32,
360    path: impl AsRef<Path>,
361    parent: Parent,
362    metadata: FileMetadata<'_>,
363    mut callback: C,
364) -> Result<File<'a>>
365where
366    C: FnMut(u64, u64) -> CallbackReturn,
367{
368    let path = path.as_ref();
369    let path = path_to_cvec!(path);
370
371    let file_t = unsafe { ffi::LIBMTP_new_file_t() };
372    unsafe { fill_file_t!(metadata, parent.to_id(), storage_id, file_t) };
373
374    let mut callback: &mut dyn FnMut(u64, u64) -> CallbackReturn = &mut callback;
375    let callback = &mut callback as *mut _ as *mut libc::c_void as *const _;
376
377    let res = unsafe {
378        ffi::LIBMTP_Send_File_From_File(
379            mtpdev.inner,
380            path.as_ptr() as *const _,
381            file_t,
382            Some(progress_func_handler),
383            callback,
384        )
385    };
386
387    if res != 0 {
388        Err(mtpdev.latest_error().unwrap_or_default())
389    } else {
390        Ok(File {
391            inner: file_t,
392            owner: mtpdev,
393        })
394    }
395}
396
397#[cfg(unix)]
398pub(crate) fn send_file_from_descriptor<'a>(
399    mtpdev: &'a MtpDevice,
400    storage_id: u32,
401    descriptor: impl AsRawFd,
402    parent: Parent,
403    metadata: FileMetadata<'_>,
404) -> Result<File<'a>> {
405    let file_t = unsafe { ffi::LIBMTP_new_file_t() };
406    unsafe { fill_file_t!(metadata, parent.to_id(), storage_id, file_t) };
407
408    let res = unsafe {
409        ffi::LIBMTP_Send_File_From_File_Descriptor(
410            mtpdev.inner,
411            descriptor.as_raw_fd(),
412            file_t,
413            None,
414            std::ptr::null(),
415        )
416    };
417
418    if res != 0 {
419        Err(mtpdev.latest_error().unwrap_or_default())
420    } else {
421        Ok(File {
422            inner: file_t,
423            owner: mtpdev,
424        })
425    }
426}
427
428#[cfg(unix)]
429pub(crate) fn send_file_from_descriptor_with_callback<'a, C>(
430    mtpdev: &'a MtpDevice,
431    storage_id: u32,
432    descriptor: impl AsRawFd,
433    parent: Parent,
434    metadata: FileMetadata<'_>,
435    mut callback: C,
436) -> Result<File<'a>>
437where
438    C: FnMut(u64, u64) -> CallbackReturn,
439{
440    let file_t = unsafe { ffi::LIBMTP_new_file_t() };
441    unsafe { fill_file_t!(metadata, parent.to_id(), storage_id, file_t) };
442
443    let mut callback: &mut dyn FnMut(u64, u64) -> CallbackReturn = &mut callback;
444    let callback = &mut callback as *mut _ as *mut libc::c_void as *const _;
445
446    let res = unsafe {
447        ffi::LIBMTP_Send_File_From_File_Descriptor(
448            mtpdev.inner,
449            descriptor.as_raw_fd(),
450            file_t,
451            Some(progress_func_handler),
452            callback,
453        )
454    };
455
456    if res != 0 {
457        Err(mtpdev.latest_error().unwrap_or_default())
458    } else {
459        Ok(File {
460            inner: file_t,
461            owner: mtpdev,
462        })
463    }
464}
465
466pub(crate) fn send_file_from_handler<'a, H>(
467    mtpdev: &'a MtpDevice,
468    storage_id: u32,
469    parent: Parent,
470    metadata: FileMetadata<'_>,
471    mut handler: H,
472) -> Result<File<'a>>
473where
474    H: FnMut(&mut [u8]) -> HandlerReturn,
475{
476    let handler: &mut dyn FnMut(&mut [u8]) -> HandlerReturn = &mut handler;
477    let mut handler_return = HandlerReturn::Ok(0);
478
479    let private = &mut (&mut handler_return, handler) as *mut _ as *mut libc::c_void;
480
481    let file_t = unsafe { ffi::LIBMTP_new_file_t() };
482    unsafe { fill_file_t!(metadata, parent.to_id(), storage_id, file_t) };
483
484    let res = unsafe {
485        ffi::LIBMTP_Send_File_From_Handler(
486            mtpdev.inner,
487            Some(data_get_func_handler),
488            private,
489            file_t,
490            None,
491            std::ptr::null(),
492        )
493    };
494
495    if res != 0 && handler_return.is_error() {
496        Err(mtpdev.latest_error().unwrap_or_default())
497    } else {
498        if handler_return.is_cancel() {
499            let _ = mtpdev.latest_error();
500        }
501
502        Ok(File {
503            inner: file_t,
504            owner: mtpdev,
505        })
506    }
507}
508
509pub(crate) fn send_file_from_handler_with_callback<'a, H, C>(
510    mtpdev: &'a MtpDevice,
511    storage_id: u32,
512    parent: Parent,
513    metadata: FileMetadata<'_>,
514    mut handler: H,
515    mut callback: C,
516) -> Result<File<'a>>
517where
518    H: FnMut(&mut [u8]) -> HandlerReturn,
519    C: FnMut(u64, u64) -> CallbackReturn,
520{
521    let handler: &mut dyn FnMut(&mut [u8]) -> HandlerReturn = &mut handler;
522    let mut handler_return = HandlerReturn::Ok(0);
523
524    let private = &mut (&mut handler_return, handler) as *mut _ as *mut libc::c_void;
525
526    let file_t = unsafe { ffi::LIBMTP_new_file_t() };
527    unsafe { fill_file_t!(metadata, parent.to_id(), storage_id, file_t) };
528
529    let mut callback: &mut dyn FnMut(u64, u64) -> CallbackReturn = &mut callback;
530    let callback = &mut callback as *mut _ as *mut libc::c_void as *const _;
531
532    let res = unsafe {
533        ffi::LIBMTP_Send_File_From_Handler(
534            mtpdev.inner,
535            Some(data_get_func_handler),
536            private,
537            file_t,
538            Some(progress_func_handler),
539            callback,
540        )
541    };
542
543    if res != 0 && handler_return.is_error() {
544        Err(mtpdev.latest_error().unwrap_or_default())
545    } else {
546        if handler_return.is_cancel() {
547            let _ = mtpdev.latest_error();
548        }
549
550        Ok(File {
551            inner: file_t,
552            owner: mtpdev,
553        })
554    }
555}