Skip to content

Commit 457eba3

Browse files
committed
Customizable GUID
Allow users to specify the GUID to use in HTTP requests. Ipatool takes the GUID from the MAC address of the first suitable network interface. When running inside docker containers, this results in the fixed mac address 02:42:AC:11:00:XX being used. Apple seems to have blacklisted this GUID. I've added a global flag that allows to specify a custom GUID value.
1 parent 8a23d6f commit 457eba3

File tree

9 files changed

+96
-13
lines changed

9 files changed

+96
-13
lines changed

cmd/common.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ func initWithCommand(cmd *cobra.Command) {
101101
verbose := cmd.Flag("verbose").Value.String() == "true"
102102
interactive, _ := cmd.Context().Value("interactive").(bool)
103103
format := util.Must(OutputFormatFromString(cmd.Flag("format").Value.String()))
104+
guid := cmd.Flag("guid").Value.String()
104105

105106
dependencies.Logger = newLogger(format, verbose)
106107
dependencies.OS = operatingsystem.New()
@@ -112,6 +113,7 @@ func initWithCommand(cmd *cobra.Command) {
112113
OperatingSystem: dependencies.OS,
113114
Keychain: dependencies.Keychain,
114115
Machine: dependencies.Machine,
116+
Guid: guid,
115117
})
116118

117119
util.Must("", createConfigDirectory(dependencies.OS, dependencies.Machine))

cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func rootCmd() *cobra.Command {
4040
cmd.PersistentFlags().BoolVar(&verbose, "verbose", false, "enables verbose logs")
4141
cmd.PersistentFlags().BoolVarP(&nonInteractive, "non-interactive", "", false, "run in non-interactive session")
4242
cmd.PersistentFlags().StringVar(&keychainPassphrase, "keychain-passphrase", "", "passphrase for unlocking keychain")
43+
cmd.PersistentFlags().String("guid", "", "GUID used in HTTP requests")
4344

4445
cmd.AddCommand(authCmd())
4546
cmd.AddCommand(downloadCmd())

pkg/appstore/appstore.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package appstore
22

33
import (
4+
"fmt"
5+
"strings"
6+
47
"github.com/majd/ipatool/v2/pkg/http"
58
"github.com/majd/ipatool/v2/pkg/keychain"
69
"github.com/majd/ipatool/v2/pkg/util/machine"
@@ -36,13 +39,15 @@ type appstore struct {
3639
httpClient http.Client[interface{}]
3740
machine machine.Machine
3841
os operatingsystem.OperatingSystem
42+
guid string
3943
}
4044

4145
type Args struct {
4246
Keychain keychain.Keychain
4347
CookieJar http.CookieJar
4448
OperatingSystem operatingsystem.OperatingSystem
4549
Machine machine.Machine
50+
Guid string
4651
}
4752

4853
func NewAppStore(args Args) AppStore {
@@ -59,5 +64,17 @@ func NewAppStore(args Args) AppStore {
5964
httpClient: http.NewClient[interface{}](clientArgs),
6065
machine: args.Machine,
6166
os: args.OperatingSystem,
67+
guid: args.Guid,
68+
}
69+
}
70+
71+
func (t *appstore) getGuid() (string, error) {
72+
if t.guid == "" {
73+
if macAddr, err := t.machine.MacAddress(); err != nil {
74+
return "", fmt.Errorf("failed to get mac address: %w", err)
75+
} else {
76+
t.guid = strings.ReplaceAll(strings.ToUpper(macAddr), ":", "")
77+
}
6278
}
79+
return t.guid, nil
6380
}

pkg/appstore/appstore_download.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,11 @@ func (t *appstore) Download(input DownloadInput) (DownloadOutput, error) {
3636
return DownloadOutput{}, fmt.Errorf("failed to resolve destination path: %w", err)
3737
}
3838

39-
macAddr, err := t.machine.MacAddress()
39+
guid, err := t.getGuid()
4040
if err != nil {
41-
return DownloadOutput{}, fmt.Errorf("failed to get mac address: %w", err)
41+
return DownloadOutput{}, fmt.Errorf("failed to get GUID: %w", err)
4242
}
4343

44-
guid := strings.ReplaceAll(strings.ToUpper(macAddr), ":", "")
45-
4644
req := t.downloadRequest(input.Account, input.App, guid)
4745

4846
res, err := t.downloadClient.Send(req)

pkg/appstore/appstore_download_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,30 @@ var _ = Describe("AppStore (Download)", func() {
8888
})
8989
})
9090

91+
When("user provides GUID", func() {
92+
BeforeEach(func() {
93+
as.(*appstore).guid = "GUID"
94+
mockMachine.EXPECT().MacAddress().Times(0)
95+
mockOS.EXPECT().Getwd().Return("", nil)
96+
mockDownloadClient.EXPECT().
97+
Send(gomock.Any()).
98+
Do(func(req http.Request) {
99+
Expect(req.Payload).To(BeAssignableToTypeOf(&http.XMLPayload{}))
100+
x := req.Payload.(*http.XMLPayload)
101+
Expect(x.Content).To(HaveKeyWithValue("guid", "GUID"))
102+
}).
103+
Return(http.Result[downloadResult]{}, errors.New(""))
104+
})
105+
AfterEach(func() {
106+
as.(*appstore).guid = ""
107+
})
108+
109+
It("sends the HTTP request with the specified GUID", func() {
110+
_, err := as.Download(DownloadInput{})
111+
Expect(err).To(HaveOccurred())
112+
})
113+
})
114+
91115
When("request fails", func() {
92116
BeforeEach(func() {
93117
mockOS.EXPECT().

pkg/appstore/appstore_login.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,11 @@ type LoginOutput struct {
2626
}
2727

2828
func (t *appstore) Login(input LoginInput) (LoginOutput, error) {
29-
macAddr, err := t.machine.MacAddress()
29+
guid, err := t.getGuid()
3030
if err != nil {
31-
return LoginOutput{}, fmt.Errorf("failed to get mac address: %w", err)
31+
return LoginOutput{}, fmt.Errorf("failed to get GUID: %w", err)
3232
}
3333

34-
guid := strings.ReplaceAll(strings.ToUpper(macAddr), ":", "")
35-
3634
acc, err := t.login(input.Email, input.Password, input.AuthCode, guid)
3735
if err != nil {
3836
return LoginOutput{}, err

pkg/appstore/appstore_login_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,29 @@ var _ = Describe("AppStore (Login)", func() {
6161
})
6262
})
6363

64+
When("user provides GUID", func() {
65+
BeforeEach(func() {
66+
as.(*appstore).guid = "GUID"
67+
mockMachine.EXPECT().MacAddress().Times(0)
68+
mockClient.EXPECT().
69+
Send(gomock.Any()).
70+
Do(func(req http.Request) {
71+
Expect(req.Payload).To(BeAssignableToTypeOf(&http.XMLPayload{}))
72+
x := req.Payload.(*http.XMLPayload)
73+
Expect(x.Content).To(HaveKeyWithValue("guid", "GUID"))
74+
}).
75+
Return(http.Result[loginResult]{}, errors.New(""))
76+
})
77+
AfterEach(func() {
78+
as.(*appstore).guid = ""
79+
})
80+
81+
It("sends the HTTP request with the specified GUID", func() {
82+
_, err := as.Login(LoginInput{})
83+
Expect(err).To(HaveOccurred())
84+
})
85+
})
86+
6487
When("successfully reads machine's MAC address", func() {
6588
BeforeEach(func() {
6689
mockMachine.EXPECT().

pkg/appstore/appstore_purchase.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"errors"
55
"fmt"
66
gohttp "net/http"
7-
"strings"
87

98
"github.com/majd/ipatool/v2/pkg/http"
109
)
@@ -21,13 +20,11 @@ type PurchaseInput struct {
2120
}
2221

2322
func (t *appstore) Purchase(input PurchaseInput) error {
24-
macAddr, err := t.machine.MacAddress()
23+
guid, err := t.getGuid()
2524
if err != nil {
26-
return fmt.Errorf("failed to get mac address: %w", err)
25+
return fmt.Errorf("failed to get GUID: %w", err)
2726
}
2827

29-
guid := strings.ReplaceAll(strings.ToUpper(macAddr), ":", "")
30-
3128
if input.App.Price > 0 {
3229
return errors.New("purchasing paid apps is not supported")
3330
}

pkg/appstore/appstore_purchase_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,29 @@ var _ = Describe("AppStore (Purchase)", func() {
5252
})
5353
})
5454

55+
When("user provides GUID", func() {
56+
BeforeEach(func() {
57+
as.guid = "GUID"
58+
mockMachine.EXPECT().MacAddress().Times(0)
59+
mockPurchaseClient.EXPECT().
60+
Send(gomock.Any()).
61+
Do(func(req http.Request) {
62+
Expect(req.Payload).To(BeAssignableToTypeOf(&http.XMLPayload{}))
63+
x := req.Payload.(*http.XMLPayload)
64+
Expect(x.Content).To(HaveKeyWithValue("guid", "GUID"))
65+
}).
66+
Return(http.Result[purchaseResult]{}, errors.New(""))
67+
})
68+
AfterEach(func() {
69+
as.guid = ""
70+
})
71+
72+
It("sends the HTTP request with the specified GUID", func() {
73+
err := as.Purchase(PurchaseInput{})
74+
Expect(err).To(HaveOccurred())
75+
})
76+
})
77+
5578
When("app is paid", func() {
5679
BeforeEach(func() {
5780
mockMachine.EXPECT().

0 commit comments

Comments
 (0)