1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4
5use std::ffi::{CStr, CString};
6use std::os::raw::c_char;
7use std::ptr;
8
9use thiserror::Error;
10
11include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
12
13#[derive(Debug, Default)]
14pub struct ZSignOptions {
15 pub input_path: String,
16
17 pub cert_file: Option<String>,
18 pub pkey_file: Option<String>,
19 pub prov_file: Option<String>,
20 pub password: Option<String>,
21 pub adhoc: bool,
22 pub sha256_only: bool,
23
24 pub bundle_id: Option<String>,
25 pub bundle_name: Option<String>,
26 pub bundle_version: Option<String>,
27 pub entitlements_file: Option<String>,
28
29 pub dylib_files: Vec<String>,
30 pub weak_inject: bool,
31
32 pub force: bool,
33 pub check_signature: bool,
34 pub temp_folder: Option<String>,
35
36 pub debug: bool,
37 pub quiet: bool,
38}
39
40#[derive(Debug, Clone, Error)]
41pub enum ZSignError {
42 #[error("Invalid input parameters")]
43 InvalidInput,
44 #[error("Signing operation failed")]
45 SigningFailed,
46 #[error("File is not signed")]
47 NotSigned,
48 #[error("Unknown error, code {0}")]
49 Unknown(i32),
50}
51
52impl ZSignOptions {
53 pub fn new<S: Into<String>>(input_path: S) -> Self {
54 Self {
55 input_path: input_path.into(),
56 ..Default::default()
57 }
58 }
59
60 pub fn with_adhoc_signing(mut self) -> Self {
61 self.adhoc = true;
62 self
63 }
64
65 pub fn with_cert_file<S: Into<String>>(mut self, cert_file: S) -> Self {
66 self.cert_file = Some(cert_file.into());
67 self
68 }
69
70 pub fn with_pkey_file<S: Into<String>>(mut self, pkey_file: S) -> Self {
71 self.pkey_file = Some(pkey_file.into());
72 self
73 }
74
75 pub fn with_prov_file<S: Into<String>>(mut self, prov_file: S) -> Self {
76 self.prov_file = Some(prov_file.into());
77 self
78 }
79
80 pub fn with_password<S: Into<String>>(mut self, password: S) -> Self {
81 self.password = Some(password.into());
82 self
83 }
84
85 pub fn with_bundle_id<S: Into<String>>(mut self, bundle_id: S) -> Self {
86 self.bundle_id = Some(bundle_id.into());
87 self
88 }
89
90 pub fn with_bundle_name<S: Into<String>>(mut self, bundle_name: S) -> Self {
91 self.bundle_name = Some(bundle_name.into());
92 self
93 }
94
95 pub fn with_bundle_version<S: Into<String>>(mut self, bundle_version: S) -> Self {
96 self.bundle_version = Some(bundle_version.into());
97 self
98 }
99
100 pub fn with_entitlements_file<S: Into<String>>(mut self, entitlements_file: S) -> Self {
101 self.entitlements_file = Some(entitlements_file.into());
102 self
103 }
104
105 pub fn with_temp_folder<S: Into<String>>(mut self, temp_folder: S) -> Self {
106 self.temp_folder = Some(temp_folder.into());
107 self
108 }
109
110 pub fn with_weak_inject(mut self) -> Self {
111 self.weak_inject = true;
112 self
113 }
114
115 pub fn with_force(mut self) -> Self {
116 self.force = true;
117 self
118 }
119
120 pub fn with_check_signature(mut self) -> Self {
121 self.check_signature = true;
122 self
123 }
124
125 pub fn with_quiet(mut self) -> Self {
126 self.quiet = true;
127 self
128 }
129
130 pub fn with_debug(mut self) -> Self {
131 self.debug = true;
132 self
133 }
134
135 pub fn add_dylib<S: Into<String>>(mut self, dylib_path: S) -> Self {
136 self.dylib_files.push(dylib_path.into());
137 self
138 }
139
140 pub fn sign(&self) -> Result<(), ZSignError> {
141 let input_path = CString::new(self.input_path.clone()).unwrap();
142 let cert_file = self
143 .cert_file
144 .as_ref()
145 .map(|s| CString::new(s.clone()).unwrap());
146 let pkey_file = self
147 .pkey_file
148 .as_ref()
149 .map(|s| CString::new(s.clone()).unwrap());
150 let prov_file = self
151 .prov_file
152 .as_ref()
153 .map(|s| CString::new(s.clone()).unwrap());
154 let password = self
155 .password
156 .as_ref()
157 .map(|s| CString::new(s.clone()).unwrap());
158 let bundle_id = self
159 .bundle_id
160 .as_ref()
161 .map(|s| CString::new(s.clone()).unwrap());
162 let bundle_name = self
163 .bundle_name
164 .as_ref()
165 .map(|s| CString::new(s.clone()).unwrap());
166 let bundle_version = self
167 .bundle_version
168 .as_ref()
169 .map(|s| CString::new(s.clone()).unwrap());
170 let entitlements_file = self
171 .entitlements_file
172 .as_ref()
173 .map(|s| CString::new(s.clone()).unwrap());
174 let temp_folder = self
175 .temp_folder
176 .as_ref()
177 .map(|s| CString::new(s.clone()).unwrap());
178
179 let dylib_cstrings: Vec<CString> = self
180 .dylib_files
181 .iter()
182 .map(|s| CString::new(s.clone()).unwrap())
183 .collect();
184 let mut dylib_ptrs: Vec<*const c_char> =
185 dylib_cstrings.iter().map(|cs| cs.as_ptr()).collect();
186
187 unsafe {
188 let result = sign_ipa(
189 input_path.as_ptr(),
190 cert_file.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
191 pkey_file.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
192 prov_file.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
193 password.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
194 if self.adhoc { 1 } else { 0 },
195 if self.sha256_only { 1 } else { 0 },
196 bundle_id.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
197 bundle_name.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
198 bundle_version.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
199 entitlements_file
200 .as_ref()
201 .map_or(ptr::null(), |s| s.as_ptr()),
202 if dylib_ptrs.is_empty() {
203 ptr::null_mut()
204 } else {
205 dylib_ptrs.as_mut_ptr()
206 },
207 dylib_ptrs.len() as i32,
208 if self.weak_inject { 1 } else { 0 },
209 if self.force { 1 } else { 0 },
210 if self.check_signature { 1 } else { 0 },
211 temp_folder.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
212 if self.debug { 1 } else { 0 },
213 if self.quiet { 1 } else { 0 },
214 );
215
216 match result {
217 0 => Ok(()),
218 -1 => Err(ZSignError::SigningFailed),
219 -2 => Err(ZSignError::NotSigned),
220 code => Err(ZSignError::Unknown(code)),
221 }
222 }
223 }
224}
225
226pub fn get_version() -> String {
227 unsafe {
228 let version_ptr = get_zsign_version();
229 let version_cstr = CStr::from_ptr(version_ptr);
230 version_cstr.to_str().unwrap().to_string()
231 }
232}