Add support for listing, downloading, installing, and uninstalling provisioning profiles.
diff --git a/src/ios-deploy/ios-deploy.m b/src/ios-deploy/ios-deploy.m
index 06eb792..af74ca8 100644
--- a/src/ios-deploy/ios-deploy.m
+++ b/src/ios-deploy/ios-deploy.m
@@ -96,6 +96,13 @@
int AMDServiceConnectionReceive(ServiceConnRef con, void * data, size_t size);
uint64_t AMDServiceConnectionReceiveMessage(ServiceConnRef con, CFPropertyListRef message, CFPropertyListFormat *format);
uint64_t AMDServiceConnectionSendMessage(ServiceConnRef con, CFPropertyListRef message, CFPropertyListFormat format);
+CFArrayRef AMDeviceCopyProvisioningProfiles(AMDeviceRef device);
+int AMDeviceInstallProvisioningProfile(AMDeviceRef device, void *profile);
+int AMDeviceRemoveProvisioningProfile(AMDeviceRef device, CFStringRef uuid);
+CFStringRef MISProvisioningProfileGetValue(void *profile, CFStringRef key);
+CFDictionaryRef MISProfileCopyPayload(void *profile);
+void *MISProfileCreateWithData(int zero, CFDataRef data);
+int MISProfileWriteToFile(void *profile, CFStringRef dest_path);
bool found_device = false, debug = false, verbose = false, unbuffered = false, nostart = false, debugserver_only = false, detect_only = false, install = true, uninstall = false, no_wifi = false;
bool faster_path_search = false;
@@ -117,6 +124,8 @@
char *list_root = NULL;
const char * custom_script_path = NULL;
char *symbols_download_directory = NULL;
+char *profile_uuid = NULL;
+char *profile_path = NULL;
int _timeout = 0;
int _detectDeadlockTimeout = 0;
bool _json_output = false;
@@ -1844,6 +1853,146 @@
check_error(AMDeviceDisconnect(device));
}
+void replace_dict_date_with_absolute_time(CFMutableDictionaryRef dict, CFStringRef key) {
+ CFDateRef date = CFDictionaryGetValue(dict, key);
+ CFAbsoluteTime absolute_date = CFDateGetAbsoluteTime(date);
+ CFNumberRef absolute_date_ref = CFNumberCreate(NULL, kCFNumberDoubleType, &absolute_date);
+ CFDictionaryReplaceValue(dict, key, absolute_date_ref);
+ CFRelease(absolute_date_ref);
+}
+
+void list_provisioning_profiles(AMDeviceRef device) {
+ connect_and_start_session(device);
+ CFArrayRef device_provisioning_profiles = AMDeviceCopyProvisioningProfiles(device);
+
+ CFIndex provisioning_profiles_count = CFArrayGetCount(device_provisioning_profiles);
+ CFMutableArrayRef serializable_provisioning_profiles =
+ CFArrayCreateMutable(NULL, provisioning_profiles_count, &kCFTypeArrayCallBacks);
+
+ for (CFIndex i = 0; i < provisioning_profiles_count; i++) {
+ void *device_provisioning_profile =
+ (void *)CFArrayGetValueAtIndex(device_provisioning_profiles, i);
+ CFDictionaryRef serializable_provisioning_profile;
+
+ if (verbose) {
+ // Fetch the entire provisioning profile and copy it into a mutable dictionary so we can
+ // process it as needed.
+ CFDictionaryRef immutable_profile_dict =
+ MISProfileCopyPayload(device_provisioning_profile);
+ CFMutableDictionaryRef mutable_profile_dict =
+ CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, immutable_profile_dict);
+ CFRelease(immutable_profile_dict);
+
+ // Remove binary values from the output since they aren't readable and add a whole lot
+ // of noise to the output.
+ CFDictionaryRemoveValue(mutable_profile_dict, CFSTR("DER-Encoded-Profile"));
+ CFDictionaryRemoveValue(mutable_profile_dict, CFSTR("DeveloperCertificates"));
+ if (_json_output) {
+ // JSON output can't have certain CFDate objects so convert dates into stringified
+ // CFAbsoluteTime's respectively.
+ replace_dict_date_with_absolute_time(mutable_profile_dict, CFSTR("ExpirationDate"));
+ replace_dict_date_with_absolute_time(mutable_profile_dict, CFSTR("CreationDate"));
+ }
+ serializable_provisioning_profile = mutable_profile_dict;
+ } else {
+ // We're not in verbose mode so pluck out only particularly interesting keys.
+ CFStringRef uuid = MISProvisioningProfileGetValue(device_provisioning_profile, "UUID");
+ CFStringRef name = MISProvisioningProfileGetValue(device_provisioning_profile, "Name");
+ CFDateRef date =
+ MISProvisioningProfileGetValue(device_provisioning_profile, "ExpirationDate");
+ CFAbsoluteTime absolute_date = CFDateGetAbsoluteTime(date);
+ CFNumberRef absolute_date_ref =
+ CFNumberCreate(NULL, kCFNumberDoubleType, &absolute_date);
+
+ CFStringRef keys[] = {CFSTR("Name"), CFSTR("UUID"), CFSTR("ExpirationDate")};
+ CFTypeRef values[] = {name, uuid, absolute_date_ref};
+ CFIndex size = sizeof(keys) / sizeof(CFStringRef);
+ serializable_provisioning_profile = CFDictionaryCreate(
+ NULL, (const void **)&keys, (const void **)&values, size,
+ &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFRelease(absolute_date_ref);
+ }
+
+ CFArrayAppendValue(serializable_provisioning_profiles, serializable_provisioning_profile);
+ CFRelease(serializable_provisioning_profile);
+ }
+ CFRelease(device_provisioning_profiles);
+
+ if (_json_output) {
+ NSLogJSON(@{
+ @"Event" : @"ListProvisioningProfiles",
+ @"Profiles" : (NSArray *)serializable_provisioning_profiles
+ });
+ } else {
+ NSLogOut(@"%@", serializable_provisioning_profiles);
+ }
+ CFRelease(serializable_provisioning_profiles);
+}
+
+void install_provisioning_profile(AMDeviceRef device) {
+ if (!profile_path) {
+ on_error(@"no path to provisioning profile specified");
+ }
+
+ size_t file_size = 0;
+ void *file_content = read_file_to_memory(profile_path, &file_size);
+ CFDataRef profile_data = CFDataCreate(NULL, file_content, file_size);
+ void *profile = MISProfileCreateWithData(0, profile_data);
+ connect_and_start_session(device);
+ check_error(AMDeviceInstallProvisioningProfile(device, profile));
+
+ free(file_content);
+ CFRelease(profile_data);
+ CFRelease(profile);
+}
+
+void uninstall_provisioning_profile(AMDeviceRef device) {
+ if (!profile_uuid) {
+ on_error(@"no profile UUID specified via --profile-uuid");
+ }
+
+ CFStringRef uuid = CFStringCreateWithCString(NULL, profile_uuid, kCFStringEncodingUTF8);
+ connect_and_start_session(device);
+ check_error(AMDeviceRemoveProvisioningProfile(device, uuid));
+ CFRelease(uuid);
+}
+
+void download_provisioning_profile(AMDeviceRef device) {
+ if (!profile_uuid) {
+ on_error(@"no profile UUID specified via --profile-uuid");
+ } else if (!profile_path) {
+ on_error(@"no download path specified");
+ }
+
+ connect_and_start_session(device);
+ CFArrayRef device_provisioning_profiles = AMDeviceCopyProvisioningProfiles(device);
+ CFIndex provisioning_profiles_count = CFArrayGetCount(device_provisioning_profiles);
+ CFStringRef uuid = CFStringCreateWithCString(NULL, profile_uuid, kCFStringEncodingUTF8);
+ bool found_matching_uuid = false;
+
+ for (CFIndex i = 0; i < provisioning_profiles_count; i++) {
+ void *profile = (void *)CFArrayGetValueAtIndex(device_provisioning_profiles, i);
+ CFStringRef other_uuid = MISProvisioningProfileGetValue(profile, "UUID");
+ found_matching_uuid = CFStringCompare(uuid, other_uuid, 0) == kCFCompareEqualTo;
+
+ if (found_matching_uuid) {
+ NSLogVerbose(@"Writing %@ to %s", MISProvisioningProfileGetValue(profile, "Name"),
+ profile_path);
+ CFStringRef dst_path =
+ CFStringCreateWithCString(NULL, profile_path, kCFStringEncodingUTF8);
+ check_error(MISProfileWriteToFile(profile, dst_path));
+ CFRelease(dst_path);
+ break;
+ }
+ }
+
+ CFRelease(uuid);
+ CFRelease(device_provisioning_profiles);
+ if (!found_matching_uuid) {
+ on_error(@"Did not find provisioning profile with UUID %x on device", profile_uuid);
+ }
+}
+
void list_bundle_id(AMDeviceRef device)
{
connect_and_start_session(device);
@@ -2446,6 +2595,14 @@
get_battery_level(device);
} else if (strcmp("symbols", command) == 0) {
download_device_symbols(device);
+ } else if (strcmp("list_profiles", command) == 0) {
+ list_provisioning_profiles(device);
+ } else if (strcmp("install_profile", command) == 0) {
+ install_provisioning_profile(device);
+ } else if (strcmp("uninstall_profile", command) == 0) {
+ uninstall_provisioning_profile(device);
+ } else if (strcmp("download_profile", command) == 0) {
+ download_provisioning_profile(device);
}
exit(0);
}
@@ -2707,6 +2864,11 @@
@" --custom-script <script> path to custom python script to execute in lldb\n"
@" --custom-command <command> specify additional lldb commands to execute\n"
@" --faster-path-search use alternative logic to find the device support paths faster\n",
+ @" -P, --list_profiles list all provisioning profiles on device\n"
+ @" --profile-uuid <uuid> the UUID of the provisioning profile to target, use with other profile commands\n"
+ @" --profile-download <path> download a provisioning profile (requires --profile-uuid)\n"
+ @" --profile-install <file> install a provisioning profile\n"
+ @" --profile-uninstall uninstall a provisioning profile (requires --profile-uuid)\n",
[NSString stringWithUTF8String:app]);
}
@@ -2764,9 +2926,14 @@
{ "non-recursively", no_argument, NULL, 'F'},
{ "key", optional_argument, NULL, 'k' },
{ "symbols", required_argument, NULL, 'S' },
+ { "list_profiles", no_argument, NULL, 'P' },
{ "custom-script", required_argument, NULL, 1001},
{ "custom-command", required_argument, NULL, 1002},
{ "faster-path-search", no_argument, NULL, 1003},
+ { "profile-install", required_argument, NULL, 1004},
+ { "profile-uninstall", no_argument, NULL, 1005},
+ { "profile-download", required_argument, NULL, 1006},
+ { "profile-uuid", required_argument, NULL, 1007},
{ NULL, 0, NULL, 0 },
};
int ch;
@@ -2925,6 +3092,27 @@
case 1003:
faster_path_search = true;
break;
+ case 1004:
+ command_only = true;
+ command = "install_profile";
+ profile_path = optarg;
+ break;
+ case 1005:
+ command_only = true;
+ command = "uninstall_profile";
+ break;
+ case 1006:
+ command_only = true;
+ command = "download_profile";
+ profile_path = optarg;
+ break;
+ case 1007:
+ profile_uuid = optarg;
+ break;
+ case 'P':
+ command_only = true;
+ command = "list_profiles";
+ break;
case 'k':
if (!keys) keys = [[NSMutableArray alloc] init];
[keys addObject: [NSString stringWithUTF8String:optarg]];