Skip to content

Commit f381d03

Browse files
Add support for listing, downloading, installing, and uninstalling provisioning profiles. (#549)
Co-authored-by: Gabe Giosia <[email protected]>
1 parent 13ccbb0 commit f381d03

File tree

1 file changed

+186
-1
lines changed

1 file changed

+186
-1
lines changed

src/ios-deploy/ios-deploy.m

Lines changed: 186 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@
9696
int AMDServiceConnectionReceive(ServiceConnRef con, void * data, size_t size);
9797
uint64_t AMDServiceConnectionReceiveMessage(ServiceConnRef con, CFPropertyListRef message, CFPropertyListFormat *format);
9898
uint64_t AMDServiceConnectionSendMessage(ServiceConnRef con, CFPropertyListRef message, CFPropertyListFormat format);
99+
CFArrayRef AMDeviceCopyProvisioningProfiles(AMDeviceRef device);
100+
int AMDeviceInstallProvisioningProfile(AMDeviceRef device, void *profile);
101+
int AMDeviceRemoveProvisioningProfile(AMDeviceRef device, CFStringRef uuid);
102+
CFStringRef MISProfileGetValue(void *profile, CFStringRef key);
103+
CFDictionaryRef MISProfileCopyPayload(void *profile);
104+
void *MISProfileCreateWithData(int zero, CFDataRef data);
105+
int MISProfileWriteToFile(void *profile, CFStringRef dest_path);
99106

100107
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;
101108
bool faster_path_search = false;
@@ -117,6 +124,8 @@
117124
char *list_root = NULL;
118125
const char * custom_script_path = NULL;
119126
char *symbols_download_directory = NULL;
127+
char *profile_uuid = NULL;
128+
char *profile_path = NULL;
120129
int _timeout = 0;
121130
int _detectDeadlockTimeout = 0;
122131
bool _json_output = false;
@@ -1844,6 +1853,143 @@ void get_battery_level(AMDeviceRef device)
18441853
check_error(AMDeviceDisconnect(device));
18451854
}
18461855

1856+
void replace_dict_date_with_absolute_time(CFMutableDictionaryRef dict, CFStringRef key) {
1857+
CFDateRef date = CFDictionaryGetValue(dict, key);
1858+
CFAbsoluteTime absolute_date = CFDateGetAbsoluteTime(date);
1859+
CFNumberRef absolute_date_ref = CFNumberCreate(NULL, kCFNumberDoubleType, &absolute_date);
1860+
CFDictionaryReplaceValue(dict, key, absolute_date_ref);
1861+
CFRelease(absolute_date_ref);
1862+
}
1863+
1864+
void list_provisioning_profiles(AMDeviceRef device) {
1865+
connect_and_start_session(device);
1866+
CFArrayRef device_provisioning_profiles = AMDeviceCopyProvisioningProfiles(device);
1867+
1868+
CFIndex provisioning_profiles_count = CFArrayGetCount(device_provisioning_profiles);
1869+
CFMutableArrayRef serializable_provisioning_profiles =
1870+
CFArrayCreateMutable(NULL, provisioning_profiles_count, &kCFTypeArrayCallBacks);
1871+
1872+
for (CFIndex i = 0; i < provisioning_profiles_count; i++) {
1873+
void *device_provisioning_profile =
1874+
(void *)CFArrayGetValueAtIndex(device_provisioning_profiles, i);
1875+
CFMutableDictionaryRef serializable_provisioning_profile;
1876+
1877+
if (verbose) {
1878+
// Verbose output; We selectively omit keys from profile.
1879+
CFDictionaryRef immutable_profile_dict =
1880+
MISProfileCopyPayload(device_provisioning_profile);
1881+
serializable_provisioning_profile =
1882+
CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, immutable_profile_dict);
1883+
CFRelease(immutable_profile_dict);
1884+
1885+
// Remove binary values from the output since they aren't readable and add a whole lot
1886+
// of noise to the output.
1887+
CFDictionaryRemoveValue(serializable_provisioning_profile,
1888+
CFSTR("DER-Encoded-Profile"));
1889+
CFDictionaryRemoveValue(serializable_provisioning_profile,
1890+
CFSTR("DeveloperCertificates"));
1891+
} else {
1892+
// Normal output; We selectively include keys from profile.
1893+
CFStringRef keys[] = {CFSTR("Name"), CFSTR("UUID"), CFSTR("ExpirationDate")};
1894+
CFIndex size = sizeof(keys) / sizeof(CFStringRef);
1895+
serializable_provisioning_profile =
1896+
CFDictionaryCreateMutable(kCFAllocatorDefault, size, &kCFTypeDictionaryKeyCallBacks,
1897+
&kCFTypeDictionaryValueCallBacks);
1898+
for (CFIndex i = 0; i < size; i++) {
1899+
CFStringRef key = keys[i];
1900+
CFStringRef value = MISProfileGetValue(device_provisioning_profile, key);
1901+
CFDictionaryAddValue(serializable_provisioning_profile, key, value);
1902+
}
1903+
}
1904+
1905+
if (_json_output) {
1906+
// JSON output can't have CFDate objects so convert dates into CFAbsoluteTime's.
1907+
replace_dict_date_with_absolute_time(serializable_provisioning_profile,
1908+
CFSTR("ExpirationDate"));
1909+
replace_dict_date_with_absolute_time(serializable_provisioning_profile,
1910+
CFSTR("CreationDate"));
1911+
}
1912+
1913+
CFArrayAppendValue(serializable_provisioning_profiles, serializable_provisioning_profile);
1914+
CFRelease(serializable_provisioning_profile);
1915+
}
1916+
CFRelease(device_provisioning_profiles);
1917+
1918+
if (_json_output) {
1919+
NSLogJSON(@{
1920+
@"Event" : @"ListProvisioningProfiles",
1921+
@"Profiles" : (NSArray *)serializable_provisioning_profiles
1922+
});
1923+
} else {
1924+
NSLogOut(@"%@", serializable_provisioning_profiles);
1925+
}
1926+
CFRelease(serializable_provisioning_profiles);
1927+
}
1928+
1929+
void install_provisioning_profile(AMDeviceRef device) {
1930+
if (!profile_path) {
1931+
on_error(@"no path to provisioning profile specified");
1932+
}
1933+
1934+
size_t file_size = 0;
1935+
void *file_content = read_file_to_memory(profile_path, &file_size);
1936+
CFDataRef profile_data = CFDataCreate(NULL, file_content, file_size);
1937+
void *profile = MISProfileCreateWithData(0, profile_data);
1938+
connect_and_start_session(device);
1939+
check_error(AMDeviceInstallProvisioningProfile(device, profile));
1940+
1941+
free(file_content);
1942+
CFRelease(profile_data);
1943+
CFRelease(profile);
1944+
}
1945+
1946+
void uninstall_provisioning_profile(AMDeviceRef device) {
1947+
if (!profile_uuid) {
1948+
on_error(@"no profile UUID specified via --profile-uuid");
1949+
}
1950+
1951+
CFStringRef uuid = CFStringCreateWithCString(NULL, profile_uuid, kCFStringEncodingUTF8);
1952+
connect_and_start_session(device);
1953+
check_error(AMDeviceRemoveProvisioningProfile(device, uuid));
1954+
CFRelease(uuid);
1955+
}
1956+
1957+
void download_provisioning_profile(AMDeviceRef device) {
1958+
if (!profile_uuid) {
1959+
on_error(@"no profile UUID specified via --profile-uuid");
1960+
} else if (!profile_path) {
1961+
on_error(@"no download path specified");
1962+
}
1963+
1964+
connect_and_start_session(device);
1965+
CFArrayRef device_provisioning_profiles = AMDeviceCopyProvisioningProfiles(device);
1966+
CFIndex provisioning_profiles_count = CFArrayGetCount(device_provisioning_profiles);
1967+
CFStringRef uuid = CFStringCreateWithCString(NULL, profile_uuid, kCFStringEncodingUTF8);
1968+
bool found_matching_uuid = false;
1969+
1970+
for (CFIndex i = 0; i < provisioning_profiles_count; i++) {
1971+
void *profile = (void *)CFArrayGetValueAtIndex(device_provisioning_profiles, i);
1972+
CFStringRef other_uuid = MISProfileGetValue(profile, CFSTR("UUID"));
1973+
found_matching_uuid = CFStringCompare(uuid, other_uuid, 0) == kCFCompareEqualTo;
1974+
1975+
if (found_matching_uuid) {
1976+
NSLogVerbose(@"Writing %@ to %s", MISProfileGetValue(profile, CFSTR("Name")),
1977+
profile_path);
1978+
CFStringRef dst_path =
1979+
CFStringCreateWithCString(NULL, profile_path, kCFStringEncodingUTF8);
1980+
check_error(MISProfileWriteToFile(profile, dst_path));
1981+
CFRelease(dst_path);
1982+
break;
1983+
}
1984+
}
1985+
1986+
CFRelease(uuid);
1987+
CFRelease(device_provisioning_profiles);
1988+
if (!found_matching_uuid) {
1989+
on_error(@"Did not find provisioning profile with UUID %x on device", profile_uuid);
1990+
}
1991+
}
1992+
18471993
void list_bundle_id(AMDeviceRef device)
18481994
{
18491995
connect_and_start_session(device);
@@ -2446,6 +2592,14 @@ void handle_device(AMDeviceRef device) {
24462592
get_battery_level(device);
24472593
} else if (strcmp("symbols", command) == 0) {
24482594
download_device_symbols(device);
2595+
} else if (strcmp("list_profiles", command) == 0) {
2596+
list_provisioning_profiles(device);
2597+
} else if (strcmp("install_profile", command) == 0) {
2598+
install_provisioning_profile(device);
2599+
} else if (strcmp("uninstall_profile", command) == 0) {
2600+
uninstall_provisioning_profile(device);
2601+
} else if (strcmp("download_profile", command) == 0) {
2602+
download_provisioning_profile(device);
24492603
}
24502604
exit(0);
24512605
}
@@ -2707,6 +2861,11 @@ void usage(const char* app) {
27072861
@" --custom-script <script> path to custom python script to execute in lldb\n"
27082862
@" --custom-command <command> specify additional lldb commands to execute\n"
27092863
@" --faster-path-search use alternative logic to find the device support paths faster\n",
2864+
@" -P, --list_profiles list all provisioning profiles on device\n"
2865+
@" --profile-uuid <uuid> the UUID of the provisioning profile to target, use with other profile commands\n"
2866+
@" --profile-download <path> download a provisioning profile (requires --profile-uuid)\n"
2867+
@" --profile-install <file> install a provisioning profile\n"
2868+
@" --profile-uninstall uninstall a provisioning profile (requires --profile-uuid <UUID>)\n",
27102869
[NSString stringWithUTF8String:app]);
27112870
}
27122871

@@ -2764,14 +2923,19 @@ int main(int argc, char *argv[]) {
27642923
{ "non-recursively", no_argument, NULL, 'F'},
27652924
{ "key", optional_argument, NULL, 'k' },
27662925
{ "symbols", required_argument, NULL, 'S' },
2926+
{ "list_profiles", no_argument, NULL, 'P' },
27672927
{ "custom-script", required_argument, NULL, 1001},
27682928
{ "custom-command", required_argument, NULL, 1002},
27692929
{ "faster-path-search", no_argument, NULL, 1003},
2930+
{ "profile-install", required_argument, NULL, 1004},
2931+
{ "profile-uninstall", no_argument, NULL, 1005},
2932+
{ "profile-download", required_argument, NULL, 1006},
2933+
{ "profile-uuid", required_argument, NULL, 1007},
27702934
{ NULL, 0, NULL, 0 },
27712935
};
27722936
int ch;
27732937

2774-
while ((ch = getopt_long(argc, argv, "VmcdvunrILefFD:R:X:i:b:a:t:p:1:2:o:l:w:9BWjNs:OE:CA:k:S:", longopts, NULL)) != -1)
2938+
while ((ch = getopt_long(argc, argv, "VmcdvunrILefFD:R:X:i:b:a:t:p:1:2:o:l:w:9BWjNs:OE:CA:k:S:P", longopts, NULL)) != -1)
27752939
{
27762940
switch (ch) {
27772941
case 'm':
@@ -2925,6 +3089,27 @@ int main(int argc, char *argv[]) {
29253089
case 1003:
29263090
faster_path_search = true;
29273091
break;
3092+
case 1004:
3093+
command_only = true;
3094+
command = "install_profile";
3095+
profile_path = optarg;
3096+
break;
3097+
case 1005:
3098+
command_only = true;
3099+
command = "uninstall_profile";
3100+
break;
3101+
case 1006:
3102+
command_only = true;
3103+
command = "download_profile";
3104+
profile_path = optarg;
3105+
break;
3106+
case 1007:
3107+
profile_uuid = optarg;
3108+
break;
3109+
case 'P':
3110+
command_only = true;
3111+
command = "list_profiles";
3112+
break;
29283113
case 'k':
29293114
if (!keys) keys = [[NSMutableArray alloc] init];
29303115
[keys addObject: [NSString stringWithUTF8String:optarg]];

0 commit comments

Comments
 (0)