diff --git a/ios/afc/afc.go b/ios/afc/afc.go index 4622e39d..6719a875 100644 --- a/ios/afc/afc.go +++ b/ios/afc/afc.go @@ -16,6 +16,7 @@ const ( Afc_operation_remove_path uint64 = 0x00000008 Afc_operation_make_dir uint64 = 0x00000009 Afc_operation_file_info uint64 = 0x0000000A + Afc_operation_device_info uint64 = 0x0000000B Afc_operation_file_open uint64 = 0x0000000D Afc_operation_file_close uint64 = 0x00000014 Afc_operation_file_write uint64 = 0x00000010 @@ -63,6 +64,13 @@ const ( Afc_Err_DirNotEmpty = 33 ) +type AFCDeviceInfo struct { + Model string + TotalBytes uint64 + FreeBytes uint64 + BlockSize uint64 +} + func getError(errorCode uint64) error { switch errorCode { case Afc_Err_UnknownError: diff --git a/ios/afc/fsync.go b/ios/afc/fsync.go index 7ac82141..d2ae1f5b 100644 --- a/ios/afc/fsync.go +++ b/ios/afc/fsync.go @@ -169,6 +169,52 @@ func (conn *Connection) listDir(path string) ([]string, error) { return fileList, nil } +func (conn *Connection) GetSpaceInfo() (*AFCDeviceInfo, error) { + thisLength := Afc_header_size + header := AfcPacketHeader{Magic: Afc_magic, Packet_num: conn.packageNumber, Operation: Afc_operation_device_info, This_length: thisLength, Entire_length: thisLength} + conn.packageNumber++ + packet := AfcPacket{Header: header, HeaderPayload: nil, Payload: nil} + response, err := conn.sendAfcPacketAndAwaitResponse(packet) + if err != nil { + return nil, err + } + if err = conn.checkOperationStatus(response); err != nil { + return nil, fmt.Errorf("mkdir: unexpected afc status: %v", err) + } + + bs := bytes.Split(response.Payload, []byte{0}) + strs := make([]string, len(bs)-1) + for i := 0; i < len(strs); i++ { + strs[i] = string(bs[i]) + } + m := make(map[string]string) + if strs != nil { + for i := 0; i < len(strs); i += 2 { + m[strs[i]] = strs[i+1] + } + } + + totalBytes, err := strconv.ParseUint(m["FSTotalBytes"], 10, 64) + if err != nil { + return nil, err + } + freeBytes, err := strconv.ParseUint(m["FSFreeBytes"], 10, 64) + if err != nil { + return nil, err + } + blockSize, err := strconv.ParseUint(m["FSBlockSize"], 10, 64) + if err != nil { + return nil, err + } + + return &AFCDeviceInfo{ + Model: m["Model"], + TotalBytes: totalBytes, + FreeBytes: freeBytes, + BlockSize: blockSize, + }, nil +} + //ListFiles returns all files in the given directory, matching the pattern. //Example: ListFiles(".", "*") returns all files and dirs in the current path the afc connection is in func (conn *Connection) ListFiles(cwd string, matchPattern string) ([]string, error) { diff --git a/ios/utils.go b/ios/utils.go index 61cc0150..7c8ae2fb 100755 --- a/ios/utils.go +++ b/ios/utils.go @@ -115,3 +115,16 @@ func FixWindowsPaths(path string) string { } return path } + +func ByteCountDecimal(b int64) string { + const unit = 1000 + if b < unit { + return fmt.Sprintf("%dB", b) + } + div, exp := int64(unit), 0 + for n := b / unit; n >= unit; n /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%.1f%cB", float64(b)/float64(div), "kMGTPE"[exp]) +} diff --git a/main.go b/main.go index 76c2b32d..a84a1ba9 100644 --- a/main.go +++ b/main.go @@ -92,7 +92,7 @@ Usage: ios pcap [options] [--pid=] [--process=] ios install --path= [options] ios uninstall [options] - ios apps [--system] [--all] [options] + ios apps [--system] [--all] [--list] [options] ios launch [options] ios kill ( | --pid= | --process=) [options] ios runtest [options] @@ -108,6 +108,7 @@ Usage: ios setlocationgpx [options] [--gpxfilepath=] ios resetlocation [options] ios assistivetouch (enable | disable | toggle | get) [--force] [options] + ios diskspace [options] Options: -v --verbose Enable Debug Logging. @@ -175,7 +176,7 @@ The commands work as following: ios readpair Dump detailed information about the pairrecord for a device. ios install --path= [options] Specify a .app folder or an installable ipa file that will be installed. ios pcap [options] [--pid=] [--process=] Starts a pcap dump of network traffic, use --pid or --process to filter specific processes. - ios apps [--system] [--all] Retrieves a list of installed applications. --system prints out preinstalled system apps. --all prints all apps, including system, user, and hidden apps. + ios apps [--system] [--all] [--list] Retrieves a list of installed applications. --system prints out preinstalled system apps. --all prints all apps, including system, user, and hidden apps. --list only prints bundle ID, bundle name and version number. ios launch Launch app with the bundleID on the device. Get your bundle ID from the apps command. ios kill ( | --pid= | --process=) [options] Kill app with the specified bundleID, process id, or process name on the device. ios runtest Run a XCUITest. @@ -192,6 +193,7 @@ The commands work as following: ios setlocationgpx [options] [--gpxfilepath=] Updates the location of the device based on the data in a GPX file. Example: setlocationgpx --gpxfilepath=/home/username/location.gpx ios resetlocation [options] Resets the location of the device to the actual one ios assistivetouch (enable | disable | toggle | get) [--force] [options] Enables, disables, toggles, or returns the state of the "AssistiveTouch" software home-screen button. iOS 11+ only (Use --force to try on older versions). + ios diskspace [options] Prints disk space info. `, version) arguments, err := docopt.ParseDoc(usage) @@ -410,9 +412,10 @@ The commands work as following: b, _ = arguments.Bool("apps") if b { + list, _ := arguments.Bool("--list") system, _ := arguments.Bool("--system") all, _ := arguments.Bool("--all") - printInstalledApps(device, system, all) + printInstalledApps(device, system, all, list) return } @@ -682,6 +685,22 @@ The commands work as following: afcService.Close() return } + + b, _ = arguments.Bool("diskspace") + if b { + afcService, err := afc.New(device) + exitIfError("connect afc service failed", err) + info, err := afcService.GetSpaceInfo() + if err != nil { + exitIfError("get device info push failed", err) + } + fmt.Printf(" Model: %s\n", info.Model) + fmt.Printf(" BlockSize: %d\n", info.BlockSize/8) + fmt.Printf(" FreeSpace: %s\n", ios.ByteCountDecimal(int64(info.FreeBytes))) + fmt.Printf(" UsedSpace: %s\n", ios.ByteCountDecimal(int64(info.TotalBytes-info.FreeBytes))) + fmt.Printf(" TotalSpace: %s\n", ios.ByteCountDecimal(int64(info.TotalBytes))) + return + } } func mobileGestaltCommand(device ios.DeviceEntry, arguments docopt.Opts) bool { @@ -1126,7 +1145,7 @@ func printDeviceDate(device ios.DeviceEntry) { } } -func printInstalledApps(device ios.DeviceEntry, system bool, all bool) { +func printInstalledApps(device ios.DeviceEntry, system bool, all bool, list bool) { svc, _ := installationproxy.New(device) var err error var response []installationproxy.AppInfo @@ -1143,6 +1162,12 @@ func printInstalledApps(device ios.DeviceEntry, system bool, all bool) { } exitIfError("browsing "+appType+" apps failed", err) + if list { + for _, v := range response { + fmt.Printf("%s %s %s\n", v.CFBundleIdentifier, v.CFBundleName, v.CFBundleShortVersionString) + } + return + } if JSONdisabled { log.Info(response) } else {