Skip to content

Commit

Permalink
Customizable GUID
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tux-mind committed Dec 19, 2024
1 parent 8a23d6f commit 457eba3
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 13 deletions.
2 changes: 2 additions & 0 deletions cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ func initWithCommand(cmd *cobra.Command) {
verbose := cmd.Flag("verbose").Value.String() == "true"
interactive, _ := cmd.Context().Value("interactive").(bool)
format := util.Must(OutputFormatFromString(cmd.Flag("format").Value.String()))
guid := cmd.Flag("guid").Value.String()

dependencies.Logger = newLogger(format, verbose)
dependencies.OS = operatingsystem.New()
Expand All @@ -112,6 +113,7 @@ func initWithCommand(cmd *cobra.Command) {
OperatingSystem: dependencies.OS,
Keychain: dependencies.Keychain,
Machine: dependencies.Machine,
Guid: guid,
})

util.Must("", createConfigDirectory(dependencies.OS, dependencies.Machine))
Expand Down
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func rootCmd() *cobra.Command {
cmd.PersistentFlags().BoolVar(&verbose, "verbose", false, "enables verbose logs")
cmd.PersistentFlags().BoolVarP(&nonInteractive, "non-interactive", "", false, "run in non-interactive session")
cmd.PersistentFlags().StringVar(&keychainPassphrase, "keychain-passphrase", "", "passphrase for unlocking keychain")
cmd.PersistentFlags().String("guid", "", "GUID used in HTTP requests")

cmd.AddCommand(authCmd())
cmd.AddCommand(downloadCmd())
Expand Down
17 changes: 17 additions & 0 deletions pkg/appstore/appstore.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package appstore

import (
"fmt"
"strings"

"github.com/majd/ipatool/v2/pkg/http"
"github.com/majd/ipatool/v2/pkg/keychain"
"github.com/majd/ipatool/v2/pkg/util/machine"
Expand Down Expand Up @@ -36,13 +39,15 @@ type appstore struct {
httpClient http.Client[interface{}]
machine machine.Machine
os operatingsystem.OperatingSystem
guid string
}

type Args struct {
Keychain keychain.Keychain
CookieJar http.CookieJar
OperatingSystem operatingsystem.OperatingSystem
Machine machine.Machine
Guid string
}

func NewAppStore(args Args) AppStore {
Expand All @@ -59,5 +64,17 @@ func NewAppStore(args Args) AppStore {
httpClient: http.NewClient[interface{}](clientArgs),
machine: args.Machine,
os: args.OperatingSystem,
guid: args.Guid,
}
}

func (t *appstore) getGuid() (string, error) {
if t.guid == "" {
if macAddr, err := t.machine.MacAddress(); err != nil {
return "", fmt.Errorf("failed to get mac address: %w", err)
} else {
t.guid = strings.ReplaceAll(strings.ToUpper(macAddr), ":", "")
}
}
return t.guid, nil

Check failure on line 79 in pkg/appstore/appstore.go

View workflow job for this annotation

GitHub Actions / Lint

return statements should not be cuddled if block has more than two lines (wsl)
}
6 changes: 2 additions & 4 deletions pkg/appstore/appstore_download.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,11 @@ func (t *appstore) Download(input DownloadInput) (DownloadOutput, error) {
return DownloadOutput{}, fmt.Errorf("failed to resolve destination path: %w", err)
}

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

guid := strings.ReplaceAll(strings.ToUpper(macAddr), ":", "")

req := t.downloadRequest(input.Account, input.App, guid)

res, err := t.downloadClient.Send(req)
Expand Down
24 changes: 24 additions & 0 deletions pkg/appstore/appstore_download_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,30 @@ var _ = Describe("AppStore (Download)", func() {
})
})

When("user provides GUID", func() {
BeforeEach(func() {
as.(*appstore).guid = "GUID"
mockMachine.EXPECT().MacAddress().Times(0)
mockOS.EXPECT().Getwd().Return("", nil)
mockDownloadClient.EXPECT().
Send(gomock.Any()).
Do(func(req http.Request) {
Expect(req.Payload).To(BeAssignableToTypeOf(&http.XMLPayload{}))
x := req.Payload.(*http.XMLPayload)
Expect(x.Content).To(HaveKeyWithValue("guid", "GUID"))
}).
Return(http.Result[downloadResult]{}, errors.New(""))
})
AfterEach(func() {
as.(*appstore).guid = ""
})

It("sends the HTTP request with the specified GUID", func() {
_, err := as.Download(DownloadInput{})
Expect(err).To(HaveOccurred())
})
})

When("request fails", func() {
BeforeEach(func() {
mockOS.EXPECT().
Expand Down
6 changes: 2 additions & 4 deletions pkg/appstore/appstore_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,11 @@ type LoginOutput struct {
}

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

guid := strings.ReplaceAll(strings.ToUpper(macAddr), ":", "")

acc, err := t.login(input.Email, input.Password, input.AuthCode, guid)
if err != nil {
return LoginOutput{}, err
Expand Down
23 changes: 23 additions & 0 deletions pkg/appstore/appstore_login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,29 @@ var _ = Describe("AppStore (Login)", func() {
})
})

When("user provides GUID", func() {
BeforeEach(func() {
as.(*appstore).guid = "GUID"
mockMachine.EXPECT().MacAddress().Times(0)
mockClient.EXPECT().
Send(gomock.Any()).
Do(func(req http.Request) {
Expect(req.Payload).To(BeAssignableToTypeOf(&http.XMLPayload{}))
x := req.Payload.(*http.XMLPayload)
Expect(x.Content).To(HaveKeyWithValue("guid", "GUID"))
}).
Return(http.Result[loginResult]{}, errors.New(""))
})
AfterEach(func() {
as.(*appstore).guid = ""
})

It("sends the HTTP request with the specified GUID", func() {
_, err := as.Login(LoginInput{})
Expect(err).To(HaveOccurred())
})
})

When("successfully reads machine's MAC address", func() {
BeforeEach(func() {
mockMachine.EXPECT().
Expand Down
7 changes: 2 additions & 5 deletions pkg/appstore/appstore_purchase.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"errors"
"fmt"
gohttp "net/http"
"strings"

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

func (t *appstore) Purchase(input PurchaseInput) error {
macAddr, err := t.machine.MacAddress()
guid, err := t.getGuid()
if err != nil {
return fmt.Errorf("failed to get mac address: %w", err)
return fmt.Errorf("failed to get GUID: %w", err)
}

guid := strings.ReplaceAll(strings.ToUpper(macAddr), ":", "")

if input.App.Price > 0 {
return errors.New("purchasing paid apps is not supported")
}
Expand Down
23 changes: 23 additions & 0 deletions pkg/appstore/appstore_purchase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,29 @@ var _ = Describe("AppStore (Purchase)", func() {
})
})

When("user provides GUID", func() {
BeforeEach(func() {
as.guid = "GUID"
mockMachine.EXPECT().MacAddress().Times(0)
mockPurchaseClient.EXPECT().
Send(gomock.Any()).
Do(func(req http.Request) {
Expect(req.Payload).To(BeAssignableToTypeOf(&http.XMLPayload{}))
x := req.Payload.(*http.XMLPayload)
Expect(x.Content).To(HaveKeyWithValue("guid", "GUID"))
}).
Return(http.Result[purchaseResult]{}, errors.New(""))
})
AfterEach(func() {
as.guid = ""
})

It("sends the HTTP request with the specified GUID", func() {
err := as.Purchase(PurchaseInput{})
Expect(err).To(HaveOccurred())
})
})

When("app is paid", func() {
BeforeEach(func() {
mockMachine.EXPECT().
Expand Down

0 comments on commit 457eba3

Please sign in to comment.