@@ -4,9 +4,11 @@ import (
44 "encoding/json"
55 "errors"
66 "fmt"
7+ "strconv"
78 "strings"
89
910 "github.com/majd/ipatool/v2/pkg/http"
11+ "github.com/majd/ipatool/v2/pkg/util"
1012)
1113
1214var (
@@ -31,7 +33,7 @@ func (t *appstore) Login(input LoginInput) (LoginOutput, error) {
3133
3234 guid := strings .ReplaceAll (strings .ToUpper (macAddr ), ":" , "" )
3335
34- acc , err := t .login (input .Email , input .Password , input .AuthCode , guid , 0 )
36+ acc , err := t .login (input .Email , input .Password , input .AuthCode , guid )
3537 if err != nil {
3638 return LoginOutput {}, err
3739 }
@@ -59,28 +61,36 @@ type loginResult struct {
5961 PasswordToken string `plist:"passwordToken,omitempty"`
6062}
6163
62- func (t * appstore ) login (email , password , authCode , guid string , attempt int ) (Account , error ) {
63- request := t .loginRequest (email , password , authCode , guid )
64- res , err := t .loginClient .Send (request )
65-
66- if err != nil {
67- return Account {}, fmt .Errorf ("request failed: %w" , err )
68- }
69-
70- if attempt == 0 && res .Data .FailureType == FailureTypeInvalidCredentials {
71- return t .login (email , password , authCode , guid , 1 )
72- }
73-
74- if res .Data .FailureType != "" && res .Data .CustomerMessage != "" {
75- return Account {}, NewErrorWithMetadata (errors .New (res .Data .CustomerMessage ), res )
64+ func (t * appstore ) login (email , password , authCode , guid string ) (Account , error ) {
65+ redirect := ""
66+ var err error
67+ retry := true
68+ var res http.Result [loginResult ]
69+
70+ for attempt := 1 ; retry && attempt <= 4 ; attempt ++ {
71+ ac := authCode
72+ if attempt == 1 {
73+ ac = ""
74+ }
75+ request := t .loginRequest (email , password , ac , guid , attempt )
76+ request .URL , redirect = util .IfEmpty (redirect , request .URL ), ""
77+ res , err = t .loginClient .Send (request )
78+ if err != nil {
79+ return Account {}, fmt .Errorf ("request failed: %w" , err )
80+ }
81+
82+ if retry , redirect , err = t .parseLoginResponse (& res , attempt , authCode ); err != nil {
83+ return Account {}, err
84+ }
7685 }
7786
78- if res . Data . FailureType != "" {
79- return Account {}, NewErrorWithMetadata (errors .New ("something went wrong " ), res )
87+ if retry {
88+ return Account {}, NewErrorWithMetadata (errors .New ("too many attempts " ), res )
8089 }
8190
82- if res .Data .FailureType == "" && authCode == "" && res .Data .CustomerMessage == CustomerMessageBadLogin {
83- return Account {}, ErrAuthCodeRequired
91+ sf , err := res .GetHeader (HTTPHeaderStoreFront )
92+ if err != nil {
93+ return Account {}, NewErrorWithMetadata (fmt .Errorf ("failed to get storefront header: %w" , err ), res )
8494 }
8595
8696 addr := res .Data .Account .Address
@@ -89,7 +99,7 @@ func (t *appstore) login(email, password, authCode, guid string, attempt int) (A
8999 Email : res .Data .Account .Email ,
90100 PasswordToken : res .Data .PasswordToken ,
91101 DirectoryServicesID : res .Data .DirectoryServicesID ,
92- StoreFront : res . Headers [ HTTPHeaderStoreFront ] ,
102+ StoreFront : sf ,
93103 Password : password ,
94104 }
95105
@@ -106,39 +116,46 @@ func (t *appstore) login(email, password, authCode, guid string, attempt int) (A
106116 return acc , nil
107117}
108118
109- func (t * appstore ) loginRequest (email , password , authCode , guid string ) http.Request {
110- attempt := "4"
111- if authCode != "" {
112- attempt = "2"
119+ func (t * appstore ) parseLoginResponse (res * http.Result [loginResult ], attempt int , authCode string ) (retry bool , redirect string , err error ) {
120+ if res .StatusCode == 302 {
121+ if redirect , err = res .GetHeader ("location" ); err != nil {
122+ err = fmt .Errorf ("failed to retrieve redirect location: %w" , err )
123+ } else {
124+ retry = true
125+ }
126+ } else if attempt == 1 && res .Data .FailureType == FailureTypeInvalidCredentials {
127+ retry = true
128+ } else if res .Data .FailureType == "" && authCode == "" && res .Data .CustomerMessage == CustomerMessageBadLogin {
129+ err = ErrAuthCodeRequired
130+ } else if res .Data .FailureType != "" {
131+ if res .Data .CustomerMessage != "" {
132+ err = NewErrorWithMetadata (errors .New (res .Data .CustomerMessage ), res )
133+ } else {
134+ err = NewErrorWithMetadata (errors .New ("something went wrong" ), res )
135+ }
136+ } else if res .StatusCode != 200 || res .Data .PasswordToken == "" || res .Data .DirectoryServicesID == "" {
137+ err = NewErrorWithMetadata (errors .New ("something went wrong" ), res )
113138 }
139+ return
140+ }
114141
142+ func (t * appstore ) loginRequest (email , password , authCode , guid string , attempt int ) http.Request {
115143 return http.Request {
116144 Method : http .MethodPOST ,
117- URL : t . authDomain ( authCode , guid ),
145+ URL : fmt . Sprintf ( "https://%s%s" , PrivateAppStoreAPIDomain , PrivateAppStoreAPIPathAuthenticate ),
118146 ResponseFormat : http .ResponseFormatXML ,
119147 Headers : map [string ]string {
120148 "Content-Type" : "application/x-www-form-urlencoded" ,
121149 },
122150 Payload : & http.XMLPayload {
123151 Content : map [string ]interface {}{
124- "appleId" : email ,
125- "attempt" : attempt ,
126- "createSession" : "true" ,
127- "guid" : guid ,
128- "password" : fmt .Sprintf ("%s%s" , password , authCode ),
129- "rmp" : "0" ,
130- "why" : "signIn" ,
152+ "appleId" : email ,
153+ "attempt" : strconv .Itoa (attempt ),
154+ "guid" : guid ,
155+ "password" : fmt .Sprintf ("%s%s" , password , authCode ),
156+ "rmp" : "0" ,
157+ "why" : "signIn" ,
131158 },
132159 },
133160 }
134161}
135-
136- func (* appstore ) authDomain (authCode , guid string ) string {
137- prefix := PrivateAppStoreAPIDomainPrefixWithoutAuthCode
138- if authCode != "" {
139- prefix = PrivateAppStoreAPIDomainPrefixWithAuthCode
140- }
141-
142- return fmt .Sprintf (
143- "https://%s-%s%s?guid=%s" , prefix , PrivateAppStoreAPIDomain , PrivateAppStoreAPIPathAuthenticate , guid )
144- }
0 commit comments