librepcb_rust_core/ffi/
zip_archive_ffi.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! FFI for
//! [`zip::ZipArchive`](https://docs.rs/zip/0.6.6/zip/read/struct.ZipArchive.html)

use super::cpp_ffi::*;
use std::fs::File;
use std::io::{Cursor, Read};

/// Proxy type for in-memory and file-based
/// [`zip::ZipArchive`](https://docs.rs/zip/0.6.6/zip/read/struct.ZipArchive.html)
enum Archive {
  /// File-based Zip archive
  File(zip::ZipArchive<File>),
  /// In-memory Zip archive
  Mem(zip::ZipArchive<Cursor<Vec<u8>>>),
}

/// Wrapper type for [Archive]
struct ZipArchive(Archive);

/// Create a new [ZipArchive] object from file path
#[no_mangle]
extern "C" fn ffi_ziparchive_new_from_file(
  path: &QString,
  err: &mut QString,
) -> *mut ZipArchive {
  let file = match File::open(std::path::Path::new(&from_qstring(path))) {
    Ok(file) => file,
    Err(e) => {
      qstring_set(err, e.to_string().as_str());
      return std::ptr::null_mut();
    }
  };
  match zip::ZipArchive::new(file) {
    Ok(zip) => Box::into_raw(Box::new(ZipArchive(Archive::File(zip)))),
    Err(e) => {
      qstring_set(err, e.to_string().as_str());
      std::ptr::null_mut()
    }
  }
}

/// Create a new [ZipArchive] object from memory
#[no_mangle]
extern "C" fn ffi_ziparchive_new_from_mem(
  data: &QByteArray,
  err: &mut QString,
) -> *mut ZipArchive {
  let reader = Cursor::new(from_qbytearray(data).to_vec());
  match zip::ZipArchive::new(reader) {
    Ok(zip) => Box::into_raw(Box::new(ZipArchive(Archive::Mem(zip)))),
    Err(e) => {
      qstring_set(err, e.to_string().as_str());
      std::ptr::null_mut()
    }
  }
}

/// Delete [ZipArchive] object
#[no_mangle]
extern "C" fn ffi_ziparchive_delete(obj: *mut ZipArchive) {
  assert!(!obj.is_null());
  unsafe { drop(Box::from_raw(obj)) };
}

/// Get number of files in [ZipArchive]
#[no_mangle]
extern "C" fn ffi_ziparchive_len(obj: &ZipArchive) -> usize {
  match &obj.0 {
    Archive::File(zip) => zip.len(),
    Archive::Mem(zip) => zip.len(),
  }
}

/// Get name of a file in [ZipArchive]
#[no_mangle]
extern "C" fn ffi_ziparchive_name_for_index(
  obj: &mut ZipArchive,
  index: usize,
  name: &mut QString,
  err: &mut QString,
) -> bool {
  let res = match &mut obj.0 {
    Archive::File(zip) => zip.by_index(index),
    Archive::Mem(zip) => zip.by_index(index),
  };
  match res {
    Ok(f) => match f.enclosed_name() {
      Some(path) if path.to_str().is_some() => {
        qstring_set(name, path.to_str().unwrap());
        true
      }
      _ => {
        qstring_set(err, "Zip contains invalid or unsafe paths.");
        false
      }
    },
    Err(e) => {
      qstring_set(err, e.to_string().as_str());
      false
    }
  }
}

/// Read a file from [ZipArchive]
#[no_mangle]
extern "C" fn ffi_ziparchive_read_by_index(
  obj: &mut ZipArchive,
  index: usize,
  buf: &mut QByteArray,
  err: &mut QString,
) -> bool {
  let res = match &mut obj.0 {
    Archive::File(zip) => zip.by_index(index),
    Archive::Mem(zip) => zip.by_index(index),
  };
  match res {
    Ok(mut f) => {
      qbytearray_resize(buf, f.size() as usize, 0);
      if let Err(e) = f.read_exact(qbytearray_to_slice_mut(buf)) {
        qstring_set(err, e.to_string().as_str());
        false
      } else {
        true
      }
    }
    Err(e) => {
      qstring_set(err, e.to_string().as_str());
      false
    }
  }
}

/// Extract [ZipArchive] to directory
#[no_mangle]
extern "C" fn ffi_ziparchive_extract(
  obj: &mut ZipArchive,
  dir: &QString,
) -> bool {
  let dir_str = &from_qstring(dir);
  let path = std::path::Path::new(dir_str);
  match &mut obj.0 {
    Archive::File(zip) => zip.extract(path),
    Archive::Mem(zip) => zip.extract(path),
  }
  .is_ok()
}