Source file
misc/ios/detect.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14 package main
15
16 import (
17 "bytes"
18 "crypto/x509"
19 "fmt"
20 "os"
21 "os/exec"
22 "strings"
23 )
24
25 func main() {
26 udids := getLines(exec.Command("idevice_id", "-l"))
27 if len(udids) == 0 {
28 fail("no udid found; is a device connected?")
29 }
30
31 mps := detectMobileProvisionFiles(udids)
32 if len(mps) == 0 {
33 fail("did not find mobile provision matching device udids %q", udids)
34 }
35
36 fmt.Println("# Available provisioning profiles below.")
37 fmt.Println("# NOTE: Any existing app on the device with the app id specified by GOIOS_APP_ID")
38 fmt.Println("# will be overwritten when running Go programs.")
39 for _, mp := range mps {
40 fmt.Println()
41 f, err := os.CreateTemp("", "go_ios_detect_")
42 check(err)
43 fname := f.Name()
44 defer os.Remove(fname)
45
46 out := output(parseMobileProvision(mp))
47 _, err = f.Write(out)
48 check(err)
49 check(f.Close())
50
51 cert, err := plistExtract(fname, "DeveloperCertificates:0")
52 check(err)
53 pcert, err := x509.ParseCertificate(cert)
54 check(err)
55 fmt.Printf("export GOIOS_DEV_ID=\"%s\"\n", pcert.Subject.CommonName)
56
57 appID, err := plistExtract(fname, "Entitlements:application-identifier")
58 check(err)
59 fmt.Printf("export GOIOS_APP_ID=%s\n", appID)
60
61 teamID, err := plistExtract(fname, "Entitlements:com.apple.developer.team-identifier")
62 check(err)
63 fmt.Printf("export GOIOS_TEAM_ID=%s\n", teamID)
64 }
65 }
66
67 func detectMobileProvisionFiles(udids [][]byte) []string {
68 cmd := exec.Command("mdfind", "-name", ".mobileprovision")
69 lines := getLines(cmd)
70
71 var files []string
72 for _, line := range lines {
73 if len(line) == 0 {
74 continue
75 }
76 xmlLines := getLines(parseMobileProvision(string(line)))
77 matches := 0
78 for _, udid := range udids {
79 for _, xmlLine := range xmlLines {
80 if bytes.Contains(xmlLine, udid) {
81 matches++
82 }
83 }
84 }
85 if matches == len(udids) {
86 files = append(files, string(line))
87 }
88 }
89 return files
90 }
91
92 func parseMobileProvision(fname string) *exec.Cmd {
93 return exec.Command("security", "cms", "-D", "-i", string(fname))
94 }
95
96 func plistExtract(fname string, path string) ([]byte, error) {
97 out, err := exec.Command("/usr/libexec/PlistBuddy", "-c", "Print "+path, fname).CombinedOutput()
98 if err != nil {
99 return nil, err
100 }
101 return bytes.TrimSpace(out), nil
102 }
103
104 func getLines(cmd *exec.Cmd) [][]byte {
105 out := output(cmd)
106 lines := bytes.Split(out, []byte("\n"))
107
108 if len(lines[len(lines)-1]) == 0 {
109 lines = lines[:len(lines)-1]
110 }
111 return lines
112 }
113
114 func output(cmd *exec.Cmd) []byte {
115 out, err := cmd.Output()
116 if err != nil {
117 fmt.Println(strings.Join(cmd.Args, "\n"))
118 fmt.Fprintln(os.Stderr, err)
119 os.Exit(1)
120 }
121 return out
122 }
123
124 func check(err error) {
125 if err != nil {
126 fail(err.Error())
127 }
128 }
129
130 func fail(msg string, v ...interface{}) {
131 fmt.Fprintf(os.Stderr, msg, v...)
132 fmt.Fprintln(os.Stderr)
133 os.Exit(1)
134 }
135
View as plain text