mirror of
https://github.com/nab138/isideload.git
synced 2026-03-02 14:36:16 +01:00
709 lines
23 KiB
Rust
709 lines
23 KiB
Rust
// This file was made using https://github.com/Dadoum/Sideloader as a reference for the apple private endpoints
|
|
|
|
use crate::Error;
|
|
use icloud_auth::{AppleAccount, Error as ICloudError};
|
|
use plist::{Date, Dictionary, Value};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::sync::Arc;
|
|
use uuid::Uuid;
|
|
|
|
pub struct DeveloperSession {
|
|
pub account: Arc<AppleAccount>,
|
|
team: Option<DeveloperTeam>,
|
|
}
|
|
|
|
impl DeveloperSession {
|
|
pub fn new(account: Arc<AppleAccount>) -> Self {
|
|
DeveloperSession {
|
|
account,
|
|
team: None,
|
|
}
|
|
}
|
|
|
|
pub async fn send_developer_request(
|
|
&self,
|
|
url: &str,
|
|
body: Option<Dictionary>,
|
|
) -> Result<Dictionary, Error> {
|
|
let mut request = Dictionary::new();
|
|
request.insert(
|
|
"clientId".to_string(),
|
|
Value::String("XABBG36SBA".to_string()),
|
|
);
|
|
request.insert(
|
|
"protocolVersion".to_string(),
|
|
Value::String("QH65B2".to_string()),
|
|
);
|
|
request.insert(
|
|
"requestId".to_string(),
|
|
Value::String(Uuid::new_v4().to_string().to_uppercase()),
|
|
);
|
|
request.insert(
|
|
"userLocale".to_string(),
|
|
Value::Array(vec![Value::String("en_US".to_string())]),
|
|
);
|
|
if let Some(body) = body {
|
|
for (key, value) in body {
|
|
request.insert(key, value);
|
|
}
|
|
}
|
|
|
|
let response = self
|
|
.account
|
|
.send_request(url, Some(request))
|
|
.await
|
|
.map_err(|e| {
|
|
if let ICloudError::AuthSrpWithMessage(code, message) = e {
|
|
Error::DeveloperSession(code, format!("Developer request failed: {}", message))
|
|
} else {
|
|
Error::Generic("Failed to send developer request".to_string())
|
|
}
|
|
})?;
|
|
|
|
let status_code = response
|
|
.get("resultCode")
|
|
.and_then(|v| v.as_unsigned_integer())
|
|
.unwrap_or(0);
|
|
if status_code != 0 {
|
|
let description = response
|
|
.get("userString")
|
|
.and_then(|v| v.as_string())
|
|
.or_else(|| response.get("resultString").and_then(|v| v.as_string()))
|
|
.unwrap_or("(null)");
|
|
return Err(Error::DeveloperSession(
|
|
status_code as i64,
|
|
description.to_string(),
|
|
));
|
|
}
|
|
Ok(response)
|
|
}
|
|
|
|
pub async fn list_teams(&self) -> Result<Vec<DeveloperTeam>, Error> {
|
|
let url = "https://developerservices2.apple.com/services/QH65B2/listTeams.action?clientId=XABBG36SBA";
|
|
let response = self.send_developer_request(url, None).await?;
|
|
|
|
let teams = response
|
|
.get("teams")
|
|
.and_then(|v| v.as_array())
|
|
.ok_or(Error::Parse("teams".to_string()))?;
|
|
|
|
let mut result = Vec::new();
|
|
for team in teams {
|
|
let dict = team
|
|
.as_dictionary()
|
|
.ok_or(Error::Parse("team".to_string()))?;
|
|
let name = dict
|
|
.get("name")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("name".to_string()))?
|
|
.to_string();
|
|
let team_id = dict
|
|
.get("teamId")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("teamId".to_string()))?
|
|
.to_string();
|
|
result.push(DeveloperTeam {
|
|
_name: name,
|
|
team_id,
|
|
});
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
pub async fn get_team(&self) -> Result<DeveloperTeam, Error> {
|
|
if let Some(team) = &self.team {
|
|
return Ok(team.clone());
|
|
}
|
|
let teams = self.list_teams().await?;
|
|
if teams.is_empty() {
|
|
return Err(Error::DeveloperSession(
|
|
-1,
|
|
"No developer teams found".to_string(),
|
|
));
|
|
}
|
|
// TODO: Handle multiple teams
|
|
Ok(teams[0].clone())
|
|
}
|
|
|
|
pub fn set_team(&mut self, team: DeveloperTeam) {
|
|
self.team = Some(team);
|
|
}
|
|
|
|
pub async fn list_devices(
|
|
&self,
|
|
device_type: DeveloperDeviceType,
|
|
team: &DeveloperTeam,
|
|
) -> Result<Vec<DeveloperDevice>, Error> {
|
|
let url = dev_url(device_type, "listDevices");
|
|
let mut body = Dictionary::new();
|
|
body.insert("teamId".to_string(), Value::String(team.team_id.clone()));
|
|
let response = self.send_developer_request(&url, Some(body)).await?;
|
|
|
|
let devices = response
|
|
.get("devices")
|
|
.and_then(|v| v.as_array())
|
|
.ok_or(Error::Parse("devices".to_string()))?;
|
|
|
|
let mut result = Vec::new();
|
|
for device in devices {
|
|
let dict = device
|
|
.as_dictionary()
|
|
.ok_or(Error::Parse("device".to_string()))?;
|
|
let device_id = dict
|
|
.get("deviceId")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("deviceId".to_string()))?
|
|
.to_string();
|
|
let name = dict
|
|
.get("name")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("name".to_string()))?
|
|
.to_string();
|
|
let device_number = dict
|
|
.get("deviceNumber")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("deviceNumber".to_string()))?
|
|
.to_string();
|
|
result.push(DeveloperDevice {
|
|
_device_id: device_id,
|
|
_name: name,
|
|
device_number,
|
|
});
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
pub async fn add_device(
|
|
&self,
|
|
device_type: DeveloperDeviceType,
|
|
team: &DeveloperTeam,
|
|
device_name: &str,
|
|
udid: &str,
|
|
) -> Result<DeveloperDevice, Error> {
|
|
let url = dev_url(device_type, "addDevice");
|
|
let mut body = Dictionary::new();
|
|
body.insert("teamId".to_string(), Value::String(team.team_id.clone()));
|
|
body.insert("name".to_string(), Value::String(device_name.to_string()));
|
|
body.insert("deviceNumber".to_string(), Value::String(udid.to_string()));
|
|
|
|
let response = self.send_developer_request(&url, Some(body)).await?;
|
|
|
|
let device_dict = response
|
|
.get("device")
|
|
.and_then(|v| v.as_dictionary())
|
|
.ok_or(Error::Parse("device".to_string()))?;
|
|
|
|
let device_id = device_dict
|
|
.get("deviceId")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("deviceId".to_string()))?
|
|
.to_string();
|
|
let name = device_dict
|
|
.get("name")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("name".to_string()))?
|
|
.to_string();
|
|
let device_number = device_dict
|
|
.get("deviceNumber")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("deviceNumber".to_string()))?
|
|
.to_string();
|
|
|
|
Ok(DeveloperDevice {
|
|
_device_id: device_id,
|
|
_name: name,
|
|
device_number,
|
|
})
|
|
}
|
|
|
|
pub async fn list_all_development_certs(
|
|
&self,
|
|
device_type: DeveloperDeviceType,
|
|
team: &DeveloperTeam,
|
|
) -> Result<Vec<DevelopmentCertificate>, Error> {
|
|
let url = dev_url(device_type, "listAllDevelopmentCerts");
|
|
let mut body = Dictionary::new();
|
|
body.insert("teamId".to_string(), Value::String(team.team_id.clone()));
|
|
|
|
let response = self.send_developer_request(&url, Some(body)).await?;
|
|
|
|
let certs = response
|
|
.get("certificates")
|
|
.and_then(|v| v.as_array())
|
|
.ok_or(Error::Parse("certificates".to_string()))?;
|
|
|
|
let mut result = Vec::new();
|
|
for cert in certs {
|
|
let dict = cert
|
|
.as_dictionary()
|
|
.ok_or(Error::Parse("certificate".to_string()))?;
|
|
let name = dict
|
|
.get("name")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("name".to_string()))?
|
|
.to_string();
|
|
let certificate_id = dict
|
|
.get("certificateId")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("certificateId".to_string()))?
|
|
.to_string();
|
|
let serial_number = dict
|
|
.get("serialNumber")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("serialNumber".to_string()))?
|
|
.to_string();
|
|
let machine_name = dict
|
|
.get("machineName")
|
|
.and_then(|v| v.as_string())
|
|
.unwrap_or("")
|
|
.to_string();
|
|
let cert_content = dict
|
|
.get("certContent")
|
|
.and_then(|v| v.as_data())
|
|
.ok_or(Error::Parse("certContent".to_string()))?
|
|
.to_vec();
|
|
|
|
result.push(DevelopmentCertificate {
|
|
name: name,
|
|
certificate_id,
|
|
serial_number: serial_number,
|
|
machine_name: machine_name,
|
|
cert_content,
|
|
});
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
pub async fn revoke_development_cert(
|
|
&self,
|
|
device_type: DeveloperDeviceType,
|
|
team: &DeveloperTeam,
|
|
serial_number: &str,
|
|
) -> Result<(), Error> {
|
|
let url = dev_url(device_type, "revokeDevelopmentCert");
|
|
let mut body = Dictionary::new();
|
|
body.insert("teamId".to_string(), Value::String(team.team_id.clone()));
|
|
body.insert(
|
|
"serialNumber".to_string(),
|
|
Value::String(serial_number.to_string()),
|
|
);
|
|
|
|
self.send_developer_request(&url, Some(body)).await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn submit_development_csr(
|
|
&self,
|
|
device_type: DeveloperDeviceType,
|
|
team: &DeveloperTeam,
|
|
csr_content: String,
|
|
) -> Result<String, Error> {
|
|
let url = dev_url(device_type, "submitDevelopmentCSR");
|
|
let mut body = Dictionary::new();
|
|
body.insert("teamId".to_string(), Value::String(team.team_id.clone()));
|
|
body.insert("csrContent".to_string(), Value::String(csr_content));
|
|
body.insert(
|
|
"machineId".to_string(),
|
|
Value::String(uuid::Uuid::new_v4().to_string().to_uppercase()),
|
|
);
|
|
body.insert(
|
|
"machineName".to_string(),
|
|
Value::String("YCode".to_string()),
|
|
);
|
|
|
|
let response = self.send_developer_request(&url, Some(body)).await?;
|
|
let cert_dict = response
|
|
.get("certRequest")
|
|
.and_then(|v| v.as_dictionary())
|
|
.ok_or(Error::Parse("certRequest".to_string()))?;
|
|
let id = cert_dict
|
|
.get("certRequestId")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("certRequestId".to_string()))?
|
|
.to_string();
|
|
|
|
Ok(id)
|
|
}
|
|
|
|
pub async fn list_app_ids(
|
|
&self,
|
|
device_type: DeveloperDeviceType,
|
|
team: &DeveloperTeam,
|
|
) -> Result<ListAppIdsResponse, Error> {
|
|
let url = dev_url(device_type, "listAppIds");
|
|
let mut body = Dictionary::new();
|
|
body.insert("teamId".to_string(), Value::String(team.team_id.clone()));
|
|
|
|
let response = self.send_developer_request(&url, Some(body)).await?;
|
|
|
|
let app_ids = response
|
|
.get("appIds")
|
|
.and_then(|v| v.as_array())
|
|
.ok_or(Error::Parse("appIds".to_string()))?;
|
|
|
|
let mut result = Vec::new();
|
|
for app_id in app_ids {
|
|
let dict = app_id
|
|
.as_dictionary()
|
|
.ok_or(Error::Parse("appId".to_string()))?;
|
|
let name = dict
|
|
.get("name")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("name".to_string()))?
|
|
.to_string();
|
|
let app_id_id = dict
|
|
.get("appIdId")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("appIdId".to_string()))?
|
|
.to_string();
|
|
let identifier = dict
|
|
.get("identifier")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("identifier".to_string()))?
|
|
.to_string();
|
|
let features = dict
|
|
.get("features")
|
|
.and_then(|v| v.as_dictionary())
|
|
.ok_or(Error::Parse("features".to_string()))?;
|
|
let expiration_date = if dict.contains_key("expirationDate") {
|
|
Some(
|
|
dict.get("expirationDate")
|
|
.and_then(|v| v.as_date())
|
|
.ok_or(Error::Parse("expirationDate".to_string()))?,
|
|
)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
result.push(AppId {
|
|
name,
|
|
app_id_id,
|
|
identifier,
|
|
features: features.clone(),
|
|
expiration_date,
|
|
});
|
|
}
|
|
|
|
let max_quantity = response
|
|
.get("maxQuantity")
|
|
.and_then(|v| v.as_unsigned_integer())
|
|
.ok_or(Error::Parse("maxQuantity".to_string()))?;
|
|
let available_quantity = response
|
|
.get("availableQuantity")
|
|
.and_then(|v| v.as_unsigned_integer())
|
|
.ok_or(Error::Parse("availableQuantity".to_string()))?;
|
|
|
|
Ok(ListAppIdsResponse {
|
|
app_ids: result,
|
|
max_quantity,
|
|
available_quantity,
|
|
})
|
|
}
|
|
|
|
pub async fn add_app_id(
|
|
&self,
|
|
device_type: DeveloperDeviceType,
|
|
team: &DeveloperTeam,
|
|
name: &str,
|
|
identifier: &str,
|
|
) -> Result<(), Error> {
|
|
let url = dev_url(device_type, "addAppId");
|
|
let mut body = Dictionary::new();
|
|
body.insert("teamId".to_string(), Value::String(team.team_id.clone()));
|
|
body.insert("name".to_string(), Value::String(name.to_string()));
|
|
body.insert(
|
|
"identifier".to_string(),
|
|
Value::String(identifier.to_string()),
|
|
);
|
|
|
|
self.send_developer_request(&url, Some(body)).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn update_app_id(
|
|
&self,
|
|
device_type: DeveloperDeviceType,
|
|
team: &DeveloperTeam,
|
|
app_id: &AppId,
|
|
features: &Dictionary,
|
|
) -> Result<Dictionary, Error> {
|
|
let url = dev_url(device_type, "updateAppId");
|
|
let mut body = Dictionary::new();
|
|
body.insert(
|
|
"appIdId".to_string(),
|
|
Value::String(app_id.app_id_id.clone()),
|
|
);
|
|
body.insert("teamId".to_string(), Value::String(team.team_id.clone()));
|
|
|
|
for (key, value) in features {
|
|
body.insert(key.clone(), value.clone());
|
|
}
|
|
|
|
let response = self.send_developer_request(&url, Some(body)).await?;
|
|
let cert_dict = response
|
|
.get("appId")
|
|
.and_then(|v| v.as_dictionary())
|
|
.ok_or(Error::Parse("appId".to_string()))?;
|
|
let feats = cert_dict
|
|
.get("features")
|
|
.and_then(|v| v.as_dictionary())
|
|
.ok_or(Error::Parse("features".to_string()))?;
|
|
|
|
Ok(feats.clone())
|
|
}
|
|
|
|
pub async fn delete_app_id(
|
|
&self,
|
|
device_type: DeveloperDeviceType,
|
|
team: &DeveloperTeam,
|
|
app_id_id: String,
|
|
) -> Result<(), Error> {
|
|
let url = dev_url(device_type, "deleteAppId");
|
|
let mut body = Dictionary::new();
|
|
body.insert("teamId".to_string(), Value::String(team.team_id.clone()));
|
|
body.insert("appIdId".to_string(), Value::String(app_id_id.clone()));
|
|
|
|
self.send_developer_request(&url, Some(body)).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn list_application_groups(
|
|
&self,
|
|
device_type: DeveloperDeviceType,
|
|
team: &DeveloperTeam,
|
|
) -> Result<Vec<ApplicationGroup>, Error> {
|
|
let url = dev_url(device_type, "listApplicationGroups");
|
|
let mut body = Dictionary::new();
|
|
body.insert("teamId".to_string(), Value::String(team.team_id.clone()));
|
|
|
|
let response = self.send_developer_request(&url, Some(body)).await?;
|
|
|
|
let app_groups = response
|
|
.get("applicationGroupList")
|
|
.and_then(|v| v.as_array())
|
|
.ok_or(Error::Parse("applicationGroupList".to_string()))?;
|
|
|
|
let mut result = Vec::new();
|
|
for app_group in app_groups {
|
|
let dict = app_group
|
|
.as_dictionary()
|
|
.ok_or(Error::Parse("applicationGroup".to_string()))?;
|
|
let application_group = dict
|
|
.get("applicationGroup")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("applicationGroup".to_string()))?
|
|
.to_string();
|
|
let name = dict
|
|
.get("name")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("name".to_string()))?
|
|
.to_string();
|
|
let identifier = dict
|
|
.get("identifier")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("identifier".to_string()))?
|
|
.to_string();
|
|
|
|
result.push(ApplicationGroup {
|
|
application_group,
|
|
_name: name,
|
|
identifier,
|
|
});
|
|
}
|
|
|
|
Ok(result)
|
|
}
|
|
|
|
pub async fn add_application_group(
|
|
&self,
|
|
device_type: DeveloperDeviceType,
|
|
team: &DeveloperTeam,
|
|
group_identifier: &str,
|
|
name: &str,
|
|
) -> Result<ApplicationGroup, Error> {
|
|
let url = dev_url(device_type, "addApplicationGroup");
|
|
let mut body = Dictionary::new();
|
|
body.insert("teamId".to_string(), Value::String(team.team_id.clone()));
|
|
body.insert("name".to_string(), Value::String(name.to_string()));
|
|
body.insert(
|
|
"identifier".to_string(),
|
|
Value::String(group_identifier.to_string()),
|
|
);
|
|
|
|
let response = self.send_developer_request(&url, Some(body)).await?;
|
|
let app_group_dict = response
|
|
.get("applicationGroup")
|
|
.and_then(|v| v.as_dictionary())
|
|
.ok_or(Error::Parse("applicationGroup".to_string()))?;
|
|
let application_group = app_group_dict
|
|
.get("applicationGroup")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("applicationGroup".to_string()))?
|
|
.to_string();
|
|
let name = app_group_dict
|
|
.get("name")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("name".to_string()))?
|
|
.to_string();
|
|
let identifier = app_group_dict
|
|
.get("identifier")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("identifier".to_string()))?
|
|
.to_string();
|
|
|
|
Ok(ApplicationGroup {
|
|
application_group,
|
|
_name: name,
|
|
identifier,
|
|
})
|
|
}
|
|
|
|
pub async fn assign_application_group_to_app_id(
|
|
&self,
|
|
device_type: DeveloperDeviceType,
|
|
team: &DeveloperTeam,
|
|
app_id: &AppId,
|
|
app_group: &ApplicationGroup,
|
|
) -> Result<(), Error> {
|
|
let url = dev_url(device_type, "assignApplicationGroupToAppId");
|
|
let mut body = Dictionary::new();
|
|
body.insert("teamId".to_string(), Value::String(team.team_id.clone()));
|
|
body.insert(
|
|
"appIdId".to_string(),
|
|
Value::String(app_id.app_id_id.clone()),
|
|
);
|
|
body.insert(
|
|
"applicationGroups".to_string(),
|
|
Value::String(app_group.application_group.clone()),
|
|
);
|
|
|
|
self.send_developer_request(&url, Some(body)).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn download_team_provisioning_profile(
|
|
&self,
|
|
device_type: DeveloperDeviceType,
|
|
team: &DeveloperTeam,
|
|
app_id: &AppId,
|
|
) -> Result<ProvisioningProfile, Error> {
|
|
let url = dev_url(device_type, "downloadTeamProvisioningProfile");
|
|
let mut body = Dictionary::new();
|
|
body.insert("teamId".to_string(), Value::String(team.team_id.clone()));
|
|
body.insert(
|
|
"appIdId".to_string(),
|
|
Value::String(app_id.app_id_id.clone()),
|
|
);
|
|
|
|
let response = self.send_developer_request(&url, Some(body)).await?;
|
|
|
|
let profile = response
|
|
.get("provisioningProfile")
|
|
.and_then(|v| v.as_dictionary())
|
|
.ok_or(Error::Parse("provisioningProfile".to_string()))?;
|
|
let name = profile
|
|
.get("name")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("name".to_string()))?
|
|
.to_string();
|
|
let provisioning_profile_id = profile
|
|
.get("provisioningProfileId")
|
|
.and_then(|v| v.as_string())
|
|
.ok_or(Error::Parse("provisioningProfileId".to_string()))?
|
|
.to_string();
|
|
let encoded_profile = profile
|
|
.get("encodedProfile")
|
|
.and_then(|v| v.as_data())
|
|
.ok_or(Error::Parse("encodedProfile".to_string()))?
|
|
.to_vec();
|
|
|
|
Ok(ProvisioningProfile {
|
|
_name: name,
|
|
_provisioning_profile_id: provisioning_profile_id,
|
|
encoded_profile,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum DeveloperDeviceType {
|
|
Any,
|
|
Ios,
|
|
Tvos,
|
|
Watchos,
|
|
}
|
|
|
|
impl DeveloperDeviceType {
|
|
pub fn url_segment(&self) -> &'static str {
|
|
match self {
|
|
DeveloperDeviceType::Any => "",
|
|
DeveloperDeviceType::Ios => "ios/",
|
|
DeveloperDeviceType::Tvos => "tvos/",
|
|
DeveloperDeviceType::Watchos => "watchos/",
|
|
}
|
|
}
|
|
}
|
|
|
|
fn dev_url(device_type: DeveloperDeviceType, endpoint: &str) -> String {
|
|
format!(
|
|
"https://developerservices2.apple.com/services/QH65B2/{}{}.action?clientId=XABBG36SBA",
|
|
device_type.url_segment(),
|
|
endpoint
|
|
)
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DeveloperDevice {
|
|
pub _device_id: String,
|
|
pub _name: String,
|
|
pub device_number: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DeveloperTeam {
|
|
pub _name: String,
|
|
pub team_id: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DevelopmentCertificate {
|
|
pub name: String,
|
|
pub certificate_id: String,
|
|
pub serial_number: String,
|
|
pub machine_name: String,
|
|
pub cert_content: Vec<u8>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct AppId {
|
|
pub app_id_id: String,
|
|
pub identifier: String,
|
|
pub name: String,
|
|
pub features: Dictionary,
|
|
pub expiration_date: Option<Date>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ListAppIdsResponse {
|
|
pub app_ids: Vec<AppId>,
|
|
pub max_quantity: u64,
|
|
pub available_quantity: u64,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ApplicationGroup {
|
|
pub application_group: String,
|
|
pub _name: String,
|
|
pub identifier: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ProvisioningProfile {
|
|
pub _provisioning_profile_id: String,
|
|
pub _name: String,
|
|
pub encoded_profile: Vec<u8>,
|
|
}
|