11use std:: cell:: RefCell ;
22use std:: path:: PathBuf ;
33use std:: rc:: Rc ;
4- use std:: { env, ptr, thread} ;
4+ use std:: { env, fs , ptr, thread} ;
55
6- use grand_slam:: AnisetteConfiguration ;
6+ use grand_slam:: { AnisetteConfiguration , BundleType , MachO , MobileProvision } ;
77use grand_slam:: auth:: Account ;
88use grand_slam:: developer:: DeveloperSession ;
99use grand_slam:: utils:: PlistInfoTrait ;
@@ -311,12 +311,9 @@ impl PlumeFrame {
311311 let rt = Builder :: new_current_thread ( ) . enable_all ( ) . build ( ) . unwrap ( ) ;
312312
313313 let install_result = rt. block_on ( async {
314- let anisette_config = AnisetteConfiguration :: default ( )
315- . set_configuration_path ( PathBuf :: from ( env:: temp_dir ( ) ) ) ;
316-
317314 let session = DeveloperSession :: with ( account. clone ( ) ) ;
318315
319- sender_clone. send ( PlumeFrameMessage :: InstallProgress ( 0 , Some ( "Ensuring device is registered..." . to_string ( ) ) ) ) . ok ( ) ;
316+ sender_clone. send ( PlumeFrameMessage :: InstallProgress ( 10 , Some ( "Ensuring current device is registered..." . to_string ( ) ) ) ) . ok ( ) ;
320317
321318 let mut usbmuxd = UsbmuxdConnection :: default ( ) . await
322319 . map_err ( |e| format ! ( "usbmuxd connect error: {e}" ) ) ?;
@@ -326,17 +323,151 @@ impl PlumeFrame {
326323 . find ( |d| d. device_id . to_string ( ) == device_id)
327324 . ok_or_else ( || format ! ( "Device ID {device_id} not found" ) ) ?;
328325
329- let mut lockdown = LockdownClient :: connect (
330- & usbmuxd_device. to_provider ( UsbmuxdAddr :: default ( ) , "plume_install" )
331- )
332- . await
333- . map_err ( |e| format ! ( "lockdown connect error: {e}" ) ) ?;
326+ let device = Device :: new ( usbmuxd_device) . await ;
327+
328+ // TODO: Handle multiple teams properly
329+ let teams = session. qh_list_teams ( ) . await . map_err ( |e| format ! ( "Failed to list teams: {}" , e) ) ?;
330+
331+ session. qh_ensure_device (
332+ & teams. teams . get ( 0 )
333+ . ok_or ( "No teams available for the Apple ID account." ) ?
334+ . team_id ,
335+ & device. name ,
336+ & device. uuid ,
337+ ) . await . map_err ( |e| format ! ( "Failed to ensure device is registered: {}" , e) ) ?;
338+
339+ let bundle = package. get_package_bundle ( )
340+ . map_err ( |e| format ! ( "Failed to get package bundle: {}" , e) ) ?;
341+ let bundles = bundle. collect_bundles_sorted ( )
342+ . map_err ( |e| format ! ( "Failed to collect bundles: {}" , e) ) ?;
343+
344+ let team_id = & teams. teams . get ( 0 )
345+ . ok_or ( "No teams available for the Apple ID account." ) ?
346+ . team_id ;
347+
348+ let bundle_identifier = bundle. get_bundle_identifier ( )
349+ . ok_or ( "Failed to get bundle identifier from package." ) ?;
350+
351+ let new_id = new_identifier ( & bundle_identifier, team_id) ;
352+
353+ fn new_identifier ( original : & str , team_id : & str ) -> String {
354+ format ! ( "{}.{}" , original, team_id)
355+ }
356+
357+ if let Some ( old_identifier) = bundle. get_bundle_identifier ( ) {
358+ for embedded_bundle in & bundles {
359+ embedded_bundle. set_matching_identifier (
360+ & old_identifier,
361+ & new_id,
362+ ) . map_err ( |e| format ! ( "Failed to set matching identifier: {}" , e) ) ?;
363+ }
364+ }
365+
366+ let mut provisionings: Vec < MobileProvision > = Vec :: new ( ) ;
334367
368+ for bundle in & bundles {
369+ if
370+ bundle. _type != BundleType :: AppExtension &&
371+ bundle. _type != BundleType :: App
372+ {
373+ continue ;
374+ }
375+
376+ sender_clone. send ( PlumeFrameMessage :: InstallProgress (
377+ 20 ,
378+ Some ( format ! ( "Registering {}..." , bundle. get_name( ) . unwrap_or_default( ) ) )
379+ ) ) . ok ( ) ;
380+
381+ let bundle_executable_name = bundle. get_executable ( )
382+ . ok_or ( "Failed to get executable from bundle." ) ?;
383+
384+ let bundle_executable_path = bundle. dir ( ) . join ( & bundle_executable_name) ;
385+
386+ let macho = MachO :: new ( & bundle_executable_path)
387+ . map_err ( |e| format ! ( "Failed to read Mach-O binary: {}" , e) ) ?;
388+
389+ let macho_entitlements = macho. entitlements ( )
390+ . map_err ( |e| format ! ( "Failed to get entitlements from Mach-O binary: {}" , e) ) ?;
391+
392+ let id = bundle. get_bundle_identifier ( )
393+ . ok_or ( "Failed to get bundle identifier from bundle." ) ?;
394+
395+ let app_groups: Vec < String > = macho_entitlements
396+ . as_ref ( )
397+ . and_then ( |dict| dict. get ( "com.apple.security.application-groups" ) )
398+ . and_then ( |val| val. as_array ( ) )
399+ . map ( |arr| {
400+ arr. iter ( )
401+ . filter_map ( |v| v. as_string ( ) . map ( |s| format ! ( "{}.{}" , s, team_id) ) )
402+ . collect ( )
403+ } )
404+ . unwrap_or_else ( Vec :: new) ;
405+
406+ session. qh_ensure_app_id ( team_id, & bundle. get_name ( ) . unwrap_or_default ( ) , & id)
407+ . await
408+ . map_err ( |e| format ! ( "Failed to ensure app ID: {}" , e) ) ?;
409+
410+ let capabilities = session. v1_list_capabilities ( team_id) . await
411+ . map_err ( |e| format ! ( "Failed to list capabilities: {}" , e) ) ?;
412+
413+ println ! ( "Mach-O Entitlements: {:?}" , & macho_entitlements) ;
414+
415+ let mut capabilities_to_enable = Vec :: new ( ) ;
416+
417+ if let Some ( entitlements) = & macho_entitlements {
418+ for ( ent_key, _) in entitlements {
419+ for cap in & capabilities. data {
420+ if let Some ( ent_list) = & cap. attributes . entitlements {
421+ if ent_list. iter ( ) . any ( |e| e. profile_key == * ent_key) {
422+ capabilities_to_enable. push ( cap. id . clone ( ) ) ;
423+ }
424+ }
425+ }
426+ }
427+ }
428+
429+ println ! ( "Enabling capabilities: {:?}" , & capabilities_to_enable) ;
430+
431+ let app_id_id = session. qh_get_app_id ( team_id, & id) . await
432+ . map_err ( |e| e. to_string ( ) ) ?
433+ . ok_or ( "Failed to get ensured app ID." ) ?;
434+
435+ if !capabilities_to_enable. is_empty ( ) {
436+ session. v1_update_app_id ( team_id, & id, capabilities_to_enable)
437+ . await
438+ . map_err ( |e| format ! ( "Failed to enable capabilities: {}" , e) ) ?;
439+ }
440+
441+ for group in & app_groups {
442+ let group_id = session. qh_ensure_app_group ( team_id, group, group)
443+ . await
444+ . map_err ( |e| format ! ( "Failed to ensure app group: {}" , e) ) ?;
445+
446+ println ! ( "{:#?}" , group_id) ;
447+
448+ session. qh_assign_app_group ( team_id, & app_id_id. app_id_id , & group_id. application_group )
449+ . await
450+ . map_err ( |e| format ! ( "Failed to add app group to app ID: {}" , e) ) ?;
451+ }
452+
453+ let profiles = session. qh_get_profile ( team_id, & app_id_id. app_id_id ) . await
454+ . map_err ( |e| format ! ( "Failed to list profiles: {}" , e) ) ?;
455+
456+ let profile_data = profiles. provisioning_profile . encoded_profile ;
457+
458+ let mobile_provision = MobileProvision :: load_from_bytes ( profile_data. as_ref ( ) )
459+ . map_err ( |e| format ! ( "Failed to load mobile provision: {}" , e) ) ?;
460+
461+ provisionings. push ( mobile_provision) ;
462+ }
463+
464+ sender_clone. send ( PlumeFrameMessage :: InstallProgress ( 30 , Some ( "Downloading Certificates..." . to_string ( ) ) ) ) . ok ( ) ;
465+
335466 Ok :: < _ , String > ( ( ) )
336467 } ) ;
337468
338469 if let Err ( e) = install_result {
339- sender_clone. send ( PlumeFrameMessage :: InstallProgress ( 100 , Some ( format ! ( "Install failed: {}" , e) ) ) ) . ok ( ) ;
470+ sender_clone. send ( PlumeFrameMessage :: InstallProgress ( 99 , Some ( format ! ( "{}" , e) ) ) ) . ok ( ) ;
340471 return ;
341472 }
342473 } ) ;
0 commit comments