zsign-rust 0.1.7

Rust bindings for zsign
Documentation
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::ptr;

use thiserror::Error;

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

#[derive(Debug, Default)]
pub struct ZSignOptions {
    pub input_path: String,

    pub cert_file: Option<String>,
    pub pkey_file: Option<String>,
    pub prov_file: Option<String>,
    pub password: Option<String>,
    pub adhoc: bool,
    pub sha256_only: bool,

    pub bundle_id: Option<String>,
    pub bundle_name: Option<String>,
    pub bundle_version: Option<String>,
    pub entitlements_file: Option<String>,

    pub dylib_files: Vec<String>,
    pub weak_inject: bool,

    pub force: bool,
    pub check_signature: bool,
    pub temp_folder: Option<String>,

    pub debug: bool,
    pub quiet: bool,
    pub enable_cache: bool,
}

#[derive(Debug, Clone, Error)]
pub enum ZSignError {
    #[error("Invalid input parameters")]
    InvalidInput,
    #[error("Signing operation failed")]
    SigningFailed,
    #[error("File is not signed")]
    NotSigned,
    #[error("Unknown error, code {0}")]
    Unknown(i32),
}

impl ZSignOptions {
    pub fn new<S: Into<String>>(input_path: S) -> Self {
        Self {
            input_path: input_path.into(),
            enable_cache: true,
            ..Default::default()
        }
    }

    pub fn with_adhoc_signing(mut self) -> Self {
        self.adhoc = true;
        self
    }

    pub fn with_cert_file<S: Into<String>>(mut self, cert_file: S) -> Self {
        self.cert_file = Some(cert_file.into());
        self
    }

    pub fn with_pkey_file<S: Into<String>>(mut self, pkey_file: S) -> Self {
        self.pkey_file = Some(pkey_file.into());
        self
    }

    pub fn with_prov_file<S: Into<String>>(mut self, prov_file: S) -> Self {
        self.prov_file = Some(prov_file.into());
        self
    }

    pub fn with_password<S: Into<String>>(mut self, password: S) -> Self {
        self.password = Some(password.into());
        self
    }

    pub fn with_bundle_id<S: Into<String>>(mut self, bundle_id: S) -> Self {
        self.bundle_id = Some(bundle_id.into());
        self
    }

    pub fn with_bundle_name<S: Into<String>>(mut self, bundle_name: S) -> Self {
        self.bundle_name = Some(bundle_name.into());
        self
    }

    pub fn with_bundle_version<S: Into<String>>(mut self, bundle_version: S) -> Self {
        self.bundle_version = Some(bundle_version.into());
        self
    }

    pub fn with_entitlements_file<S: Into<String>>(mut self, entitlements_file: S) -> Self {
        self.entitlements_file = Some(entitlements_file.into());
        self
    }

    pub fn with_temp_folder<S: Into<String>>(mut self, temp_folder: S) -> Self {
        self.temp_folder = Some(temp_folder.into());
        self
    }

    pub fn with_weak_inject(mut self) -> Self {
        self.weak_inject = true;
        self
    }

    pub fn with_force(mut self) -> Self {
        self.force = true;
        self
    }

    pub fn with_check_signature(mut self) -> Self {
        self.check_signature = true;
        self
    }

    pub fn with_quiet(mut self) -> Self {
        self.quiet = true;
        self
    }

    pub fn with_debug(mut self) -> Self {
        self.debug = true;
        self
    }

    pub fn with_disable_cache(mut self) -> Self {
        self.enable_cache = false;
        self
    }

    pub fn add_dylib<S: Into<String>>(mut self, dylib_path: S) -> Self {
        self.dylib_files.push(dylib_path.into());
        self
    }

    pub fn sign(&self) -> Result<(), ZSignError> {
        let input_path = CString::new(self.input_path.clone()).unwrap();
        let cert_file = self
            .cert_file
            .as_ref()
            .map(|s| CString::new(s.clone()).unwrap());
        let pkey_file = self
            .pkey_file
            .as_ref()
            .map(|s| CString::new(s.clone()).unwrap());
        let prov_file = self
            .prov_file
            .as_ref()
            .map(|s| CString::new(s.clone()).unwrap());
        let password = self
            .password
            .as_ref()
            .map(|s| CString::new(s.clone()).unwrap());
        let bundle_id = self
            .bundle_id
            .as_ref()
            .map(|s| CString::new(s.clone()).unwrap());
        let bundle_name = self
            .bundle_name
            .as_ref()
            .map(|s| CString::new(s.clone()).unwrap());
        let bundle_version = self
            .bundle_version
            .as_ref()
            .map(|s| CString::new(s.clone()).unwrap());
        let entitlements_file = self
            .entitlements_file
            .as_ref()
            .map(|s| CString::new(s.clone()).unwrap());
        let temp_folder = self
            .temp_folder
            .as_ref()
            .map(|s| CString::new(s.clone()).unwrap());

        let dylib_cstrings: Vec<CString> = self
            .dylib_files
            .iter()
            .map(|s| CString::new(s.clone()).unwrap())
            .collect();
        let mut dylib_ptrs: Vec<*const c_char> =
            dylib_cstrings.iter().map(|cs| cs.as_ptr()).collect();

        unsafe {
            let result = sign_ipa(
                input_path.as_ptr(),
                cert_file.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
                pkey_file.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
                prov_file.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
                password.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
                if self.adhoc { 1 } else { 0 },
                if self.sha256_only { 1 } else { 0 },
                bundle_id.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
                bundle_name.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
                bundle_version.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
                entitlements_file
                    .as_ref()
                    .map_or(ptr::null(), |s| s.as_ptr()),
                if dylib_ptrs.is_empty() {
                    ptr::null_mut()
                } else {
                    dylib_ptrs.as_mut_ptr()
                },
                dylib_ptrs.len() as i32,
                if self.weak_inject { 1 } else { 0 },
                if self.force { 1 } else { 0 },
                if self.check_signature { 1 } else { 0 },
                temp_folder.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
                if self.debug { 1 } else { 0 },
                if self.quiet { 1 } else { 0 },
                if self.enable_cache { 1 } else { 0 },
            );

            match result {
                0 => Ok(()),
                -1 => Err(ZSignError::SigningFailed),
                -2 => Err(ZSignError::NotSigned),
                code => Err(ZSignError::Unknown(code)),
            }
        }
    }
}

pub fn get_version() -> String {
    unsafe {
        let version_ptr = get_zsign_version();
        let version_cstr = CStr::from_ptr(version_ptr);
        version_cstr.to_str().unwrap().to_string()
    }
}