diff --git a/ios/afc/fsync.go b/ios/afc/fsync.go index e049cc11..8baeb7da 100644 --- a/ios/afc/fsync.go +++ b/ios/afc/fsync.go @@ -3,6 +3,7 @@ package afc import ( "bytes" "encoding/binary" + "errors" "fmt" "io" "os" @@ -13,6 +14,7 @@ import ( "github.com/danielpaulus/go-ios/ios" log "github.com/sirupsen/logrus" + "howett.net/plist" ) const serviceName = "com.apple.afc" @@ -48,6 +50,67 @@ func New(device ios.DeviceEntry) (*Connection, error) { return &Connection{deviceConn: deviceConn}, nil } +func NewContainer(device ios.DeviceEntry, bundleID string) (*Connection, error) { + deviceConn, err := ios.ConnectToService(device, "com.apple.mobile.house_arrest") + if err != nil { + return nil, err + } + err = vendContainer(deviceConn, bundleID) + if err != nil { + return nil, err + } + return &Connection{deviceConn: deviceConn}, nil +} + +func vendContainer(deviceConn ios.DeviceConnectionInterface, bundleID string) error { + plistCodec := ios.NewPlistCodec() + vendContainer := map[string]interface{}{"Command": "VendContainer", "Identifier": bundleID} + msg, err := plistCodec.Encode(vendContainer) + if err != nil { + return fmt.Errorf("VendContainer Encoding cannot fail unless the encoder is broken: %v", err) + } + err = deviceConn.Send(msg) + if err != nil { + return err + } + reader := deviceConn.Reader() + response, err := plistCodec.Decode(reader) + if err != nil { + return err + } + return checkResponse(response) +} + +func checkResponse(vendContainerResponseBytes []byte) error { + response, err := plistFromBytes(vendContainerResponseBytes) + if err != nil { + return err + } + if "Complete" == response.Status { + return nil + } + if response.Error != "" { + return errors.New(response.Error) + } + return errors.New("unknown error during vendcontainer") +} + +func plistFromBytes(plistBytes []byte) (vendContainerResponse, error) { + var vendResponse vendContainerResponse + decoder := plist.NewDecoder(bytes.NewReader(plistBytes)) + + err := decoder.Decode(&vendResponse) + if err != nil { + return vendResponse, err + } + return vendResponse, nil +} + +type vendContainerResponse struct { + Status string + Error string +} + // NewFromConn allows to use AFC on a DeviceConnectionInterface, see crashreport for an example func NewFromConn(deviceConn ios.DeviceConnectionInterface) *Connection { return &Connection{deviceConn: deviceConn} diff --git a/main.go b/main.go index 14883446..579c4afa 100644 --- a/main.go +++ b/main.go @@ -109,8 +109,8 @@ Usage: ios runwda [--bundleid=] [--testrunnerbundleid=] [--xctestconfig=] [--arg=]... [--env=]... [options] ios ax [options] ios debug [options] [--stop-at-entry] - ios fsync (rm [--r] | tree | mkdir) --path= - ios fsync (pull | push) --srcPath= --dstPath= + ios fsync [--app=bundleId] [options] (rm [--r] | tree | mkdir) --path= + ios fsync [--app=bundleId] [options] (pull | push) --srcPath= --dstPath= ios reboot [options] ios -h | --help ios --version | version [options] @@ -206,8 +206,8 @@ The commands work as following: > specify runtime args and env vars like --env ENV_1=something --env ENV_2=else and --arg ARG1 --arg ARG2 ios ax [options] Access accessibility inspector features. ios debug [--stop-at-entry] Start debug with lldb - ios fsync (rm [--r] | tree | mkdir) --path= Remove | treeview | mkdir in target path. --r used alongside rm will recursively remove all files and directories from target path. - ios fsync (pull | push) --srcPath= --dstPath= Pull or Push file from srcPath to dstPath. + ios fsync [--app=bundleId] [options] (rm [--r] | tree | mkdir) --path= Remove | treeview | mkdir in target path. --r used alongside rm will recursively remove all files and directories from target path. + ios fsync [--app=bundleId] [options] (pull | push) --srcPath= --dstPath= Pull or Push file from srcPath to dstPath. ios reboot [options] Reboot the given device ios -h | --help Prints this screen. ios --version | version [options] Prints the version @@ -825,7 +825,13 @@ The commands work as following: b, _ = arguments.Bool("fsync") if b { - afcService, err := afc.New(device) + containerBundleId, _ := arguments.String("--app") + var afcService *afc.Connection + if containerBundleId == "" { + afcService, err = afc.New(device) + } else { + afcService, err = afc.NewContainer(device, containerBundleId) + } exitIfError("fsync: connect afc service failed", err) b, _ = arguments.Bool("rm") if b {