From 64b415c36dc0003dc2cb039eabe8021ae41b128e Mon Sep 17 00:00:00 2001 From: andig Date: Tue, 3 Feb 2026 23:00:28 +0100 Subject: [PATCH] SAIC: simplify (#27130) --- vehicle/saic/api.go | 95 ++++++--------- vehicle/saic/identity.go | 40 +++---- vehicle/saic/provider.go | 2 +- vehicle/saic/requests/api_config.go | 8 -- vehicle/saic/requests/basic_encryption.go | 60 ---------- vehicle/saic/requests/encryption.go | 109 +++++++---------- vehicle/saic/requests/hashUtils.go | 32 ----- vehicle/saic/requests/helper.go | 40 +++++++ vehicle/saic/requests/macUtils.go | 18 --- vehicle/saic/requests/request.go | 135 ++++++++++++++++++++++ vehicle/saic/requests/sendRequest.go | 98 ---------------- vehicle/saic/requests/types.go | 11 +- 12 files changed, 282 insertions(+), 366 deletions(-) delete mode 100644 vehicle/saic/requests/api_config.go delete mode 100644 vehicle/saic/requests/basic_encryption.go delete mode 100644 vehicle/saic/requests/hashUtils.go create mode 100644 vehicle/saic/requests/helper.go delete mode 100644 vehicle/saic/requests/macUtils.go create mode 100644 vehicle/saic/requests/request.go delete mode 100644 vehicle/saic/requests/sendRequest.go diff --git a/vehicle/saic/api.go b/vehicle/saic/api.go index 2a90fd1e6..e0258c0f2 100644 --- a/vehicle/saic/api.go +++ b/vehicle/saic/api.go @@ -11,7 +11,6 @@ import ( "github.com/evcc-io/evcc/util/request" "github.com/evcc-io/evcc/util/transport" "github.com/evcc-io/evcc/vehicle/saic/requests" - "golang.org/x/oauth2" ) const ( @@ -56,19 +55,13 @@ func NewAPI(log *util.Logger, identity *Identity) *API { } func (v *API) doRepeatedRequest(path string, event_id string) error { - var req *http.Request - - answer := requests.Answer{ - Data: &v.request.Result, - } - token, err := v.identity.Token() if err != nil { v.request.Status = StatInvalid return err } - req, err = requests.CreateRequest( + req, err := requests.CreateRequest( v.identity.baseUrl, path, http.MethodGet, @@ -81,9 +74,13 @@ func (v *API) doRepeatedRequest(path string, event_id string) error { return err } - _, err = v.DoRequest(req, &answer) + var res requests.Answer[requests.ChargeStatus] + _, err = doRequest(v, req, &res) if err == nil { - v.request.Status = StatValid + v.request = ConcurrentRequest{ + Status: StatValid, + Result: res.Data, + } } else if err != api.ErrMustRetry { v.request.Status = StatInvalid } @@ -92,11 +89,10 @@ func (v *API) doRepeatedRequest(path string, event_id string) error { // This is running concurrently func (v *API) repeatRequest(path string, event_id string) { - var err error var count int v.request.Status = StatRunning - for err = api.ErrMustRetry; err == api.ErrMustRetry && count < 20; { + for err := api.ErrMustRetry; err == api.ErrMustRetry && count < 20; { time.Sleep(2 * time.Second) v.log.TRACE.Printf("Starting repeated query. Count: %d", count) err = v.doRepeatedRequest(path, event_id) @@ -108,20 +104,17 @@ func (v *API) repeatRequest(path string, event_id string) { if v.request.Status == StatRunning { v.request.Status = StatInvalid } - v.log.TRACE.Printf("Exiting repeated query. Count: %d", count) } -func (v *API) DoRequest(req *http.Request, result *requests.Answer) (string, error) { - var body []byte - +func doRequest[T any](v *API, req *http.Request, result *requests.Answer[T]) (string, error) { resp, err := v.Do(req) if err != nil { return "", err } - defer resp.Body.Close() + if resp.StatusCode == http.StatusUnauthorized { - v.log.TRACE.Printf("DoRequest: %s", resp.Status) + v.log.TRACE.Printf("doRequest: %s", resp.Status) v.identity.Login() return "", api.ErrMustRetry } @@ -129,17 +122,17 @@ func (v *API) DoRequest(req *http.Request, result *requests.Answer) (string, err event_id := resp.Header.Get("event-id") if result != nil { - body, err = requests.DecryptAnswer(resp) - if err == nil { - if err2 := json.Unmarshal(body, result); err2 == nil && result.Code != 0 { - if result.Code == 4 { - err = api.ErrMustRetry - } else { - err = fmt.Errorf("%d: %s", result.Code, result.Message) - } + body, err2 := requests.DecodeResponse(resp) + if err2 != nil { + return event_id, fmt.Errorf("decrypt: %w", err2) + } + + if err2 := json.Unmarshal(body, result); err2 == nil && result.Code != 0 { + if result.Code == 4 { + err = api.ErrMustRetry + } else { + err = fmt.Errorf("%d: %s", result.Code, result.Message) } - } else { - err = fmt.Errorf("decrypt: %w", err) } } @@ -156,17 +149,13 @@ func (v *API) Vehicles() ([]Vehicle, error) { */ func (v *API) Wakeup(vin string) error { - var req *http.Request - var err error - var token *oauth2.Token - - token, err = v.identity.Token() + token, err := v.identity.Token() if err != nil { return err } path := "vehicle/status?vin=" + requests.Sha256(vin) - req, err = requests.CreateRequest( + req, err := requests.CreateRequest( v.identity.baseUrl, path, http.MethodGet, @@ -178,21 +167,14 @@ func (v *API) Wakeup(vin string) error { return err } - v.DoRequest(req, nil) + doRequest[any](v, req, nil) return nil } // Status implements the /user/vehicles//status api func (v *API) Status(vin string) (requests.ChargeStatus, error) { - var req *http.Request - var res requests.ChargeStatus - var event_id string - var err error - var token *oauth2.Token - answer := requests.Answer{ - Data: &res, - } + var zero requests.ChargeStatus // Check if we are already running in the background if v.request.Status == StatValid { @@ -203,18 +185,18 @@ func (v *API) Status(vin string) (requests.ChargeStatus, error) { } if v.request.Status == StatRunning { v.log.TRACE.Printf("StatRunning. Exiting") - return res, api.ErrMustRetry + return zero, api.ErrMustRetry } v.log.TRACE.Printf("StatInvalid. Starting query") - token, err = v.identity.Token() + token, err := v.identity.Token() if err != nil { - return res, err + return zero, err } path := "vehicle/charging/mgmtData?vin=" + requests.Sha256(vin) // get charging status of vehicle - req, err = requests.CreateRequest( + req, err := requests.CreateRequest( v.identity.baseUrl, path, http.MethodGet, @@ -223,18 +205,18 @@ func (v *API) Status(vin string) (requests.ChargeStatus, error) { token.AccessToken, "") if err != nil { - return res, err + return zero, err } - event_id, err = v.DoRequest(req, &answer) + var res requests.Answer[requests.ChargeStatus] + event_id, err := doRequest(v, req, &res) if err != nil { - v.log.TRACE.Printf("Getting event id failed") - return res, err + return zero, err } if event_id == "" { v.log.TRACE.Printf("Answer without event ID") - return res, api.ErrMustRetry + return zero, api.ErrMustRetry } req, err = requests.CreateRequest( @@ -246,20 +228,17 @@ func (v *API) Status(vin string) (requests.ChargeStatus, error) { token.AccessToken, event_id) if err != nil { - v.log.TRACE.Printf("Could not create request %s", err.Error()) - return res, err + return zero, err } - _, err = v.DoRequest(req, &answer) + _, err = doRequest(v, req, &res) // Continue checking.... if err == api.ErrMustRetry { v.request.Status = StatRunning v.log.TRACE.Printf(" No answer yet. Continue status query in background") go v.repeatRequest(path, event_id) - } else if err != nil { - v.log.TRACE.Printf("doRequest failed with %s", err.Error()) } - return res, err + return res.Data, err } diff --git a/vehicle/saic/identity.go b/vehicle/saic/identity.go index aba8fb97c..dc00245b0 100644 --- a/vehicle/saic/identity.go +++ b/vehicle/saic/identity.go @@ -62,11 +62,6 @@ func (v *Identity) Login() error { } func (v *Identity) retrieveToken(data url.Values) (*oauth2.Token, error) { - var loginData requests.LoginData - answer := requests.Answer{ - Data: &loginData, - } - data.Set("deviceId", v.deviceId) data.Set("response_type", "code") data.Set("scope", "all") @@ -92,24 +87,27 @@ func (v *Identity) retrieveToken(data url.Values) (*oauth2.Token, error) { return nil, err } - var body []byte - body, err = requests.DecryptAnswer(resp) - if err == nil && len(body) != 0 { - err = json.Unmarshal(body, &answer) - if err == nil && answer.Code != 0 { - err = fmt.Errorf("%d: %s", answer.Code, answer.Message) - } - - tok := oauth2.Token{ - AccessToken: loginData.Access_token, - RefreshToken: loginData.Refresh_token, - TokenType: loginData.Token_type, - } - tok.Expiry = time.Now().Add(time.Second * time.Duration(loginData.Expires_in)) - return &tok, err + body, err := requests.DecodeResponse(resp) + if err != nil { + return nil, err } - return nil, err + var res requests.Answer[requests.LoginData] + if err := json.Unmarshal(body, &res); err != nil { + return nil, err + } + if res.Code != 0 { + return nil, fmt.Errorf("%d: %s", res.Code, res.Message) + } + + tok := oauth2.Token{ + AccessToken: res.Data.Access_token, + RefreshToken: res.Data.Refresh_token, + TokenType: res.Data.Token_type, + Expiry: time.Now().Add(time.Second * time.Duration(res.Data.Expires_in)), + } + + return &tok, nil } func (v *Identity) refreshToken(token *oauth2.Token) (*oauth2.Token, error) { diff --git a/vehicle/saic/provider.go b/vehicle/saic/provider.go index b75b8c2fb..1ea32f51b 100644 --- a/vehicle/saic/provider.go +++ b/vehicle/saic/provider.go @@ -106,7 +106,7 @@ func (v *Provider) Range() (int64, error) { val := res.RvsChargeStatus.FuelRangeElec if val < 10 { // Ok, 0 would be possible, but it's more likely that it's an invalid answer. - return val, fmt.Errorf("invalid raw range value: %d: %w", val, api.ErrMustRetry) + return 0, api.ErrMustRetry } return val / 10, nil } diff --git a/vehicle/saic/requests/api_config.go b/vehicle/saic/requests/api_config.go deleted file mode 100644 index 40c63863e..000000000 --- a/vehicle/saic/requests/api_config.go +++ /dev/null @@ -1,8 +0,0 @@ -package requests - -const ( - CONTENT_ENCRYPTED = "1" - PARAM_AUTHENTICATION = "Basic c3dvcmQ6c3dvcmRfc2VjcmV0" - TENANT_ID = "459771" - USER_TYPE = "app" -) diff --git a/vehicle/saic/requests/basic_encryption.go b/vehicle/saic/requests/basic_encryption.go deleted file mode 100644 index 04cf6cbe8..000000000 --- a/vehicle/saic/requests/basic_encryption.go +++ /dev/null @@ -1,60 +0,0 @@ -package requests - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "encoding/hex" -) - -func PKCS5Padding(ciphertext []byte, blockSize int) []byte { - padding := blockSize - len(ciphertext)%blockSize - padtext := bytes.Repeat([]byte{byte(padding)}, padding) - return append(ciphertext, padtext...) -} - -func PKCS5Trimming(encrypt []byte) []byte { - padding := encrypt[len(encrypt)-1] - return encrypt[:len(encrypt)-int(padding)] -} - -func Decrypt(cipherText, hexKey, hexIV string) string { - if len(cipherText) == 0 || len(hexKey) == 0 || len(hexIV) == 0 { - return "" - } - - secretKey, _ := hex.DecodeString(hexKey) - ivParameter, _ := hex.DecodeString(hexIV) - block, err := aes.NewCipher(secretKey) - if err != nil { - return "" - } - - decrypter := cipher.NewCBCDecrypter(block, ivParameter) - text, _ := hex.DecodeString(cipherText) - decrypted := make([]byte, len(text)) - decrypter.CryptBlocks(decrypted, text) - - return string(PKCS5Trimming(decrypted)) -} - -func Encrypt(plainText, hexKey, hexIV string) string { - if len(plainText) == 0 || len(hexKey) == 0 || len(hexIV) == 0 { - return "" - } - - secretKey, _ := hex.DecodeString(hexKey) - ivParameter, _ := hex.DecodeString(hexIV) - block, err := aes.NewCipher(secretKey) - if err != nil { - return "" - } - - content := PKCS5Padding([]byte(plainText), block.BlockSize()) - encrypter := cipher.NewCBCEncrypter(block, ivParameter) - - ciphertext := make([]byte, len(content)) - encrypter.CryptBlocks(ciphertext, content) - - return hex.EncodeToString(ciphertext) -} diff --git a/vehicle/saic/requests/encryption.go b/vehicle/saic/requests/encryption.go index 6e17d8fef..04cf6cbe8 100644 --- a/vehicle/saic/requests/encryption.go +++ b/vehicle/saic/requests/encryption.go @@ -1,87 +1,60 @@ package requests import ( - "strconv" + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/hex" ) -func CalculateRequestVerification( - resourcePath string, - sendDate int64, - tenant, - contentType, - bodyEncrypted, - token string, -) string { - dateString := strconv.FormatInt(sendDate, 10) - str9 := resourcePath + tenant + token + USER_TYPE - a2 := Md5(str9) - str10 := dateString + CONTENT_ENCRYPTED + contentType - a3 := Md5(a2 + str10) - str11 := resourcePath + tenant + token + USER_TYPE + dateString + CONTENT_ENCRYPTED + contentType + bodyEncrypted +func PKCS5Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} - a5 := Md5(a3 + dateString) +func PKCS5Trimming(encrypt []byte) []byte { + padding := encrypt[len(encrypt)-1] + return encrypt[:len(encrypt)-int(padding)] +} - if len(a5) != 0 && len(str11) != 0 { - return HmacSha256(a5, str11) - } else { +func Decrypt(cipherText, hexKey, hexIV string) string { + if len(cipherText) == 0 || len(hexKey) == 0 || len(hexIV) == 0 { return "" } -} -func DecryptResponse(timeStamp, contentType, cipherText string) string { - str4 := timeStamp + CONTENT_ENCRYPTED + contentType - a2 := Md5(str4) - hashedTimeStamp := Md5(timeStamp) - - if len(cipherText) != 0 { - return Decrypt(cipherText, a2, hashedTimeStamp) - } - return "" -} - -func CalculateResponseVerification(str, str2, str3 string) string { - str4 := str + CONTENT_ENCRYPTED + str2 - a2 := Md5(str4) - str5 := str + CONTENT_ENCRYPTED + str2 + str3 - a4 := Md5(a2 + str) - return HmacSha256(a4, str5) -} - -func EncryptRequest(path string, time int64, tenant, token, body, contentType string) string { - sendDate := strconv.FormatInt(time, 10) - // tenant - resourcePath := "" - if len(path) != 0 { - resourcePath = "/" + path + secretKey, _ := hex.DecodeString(hexKey) + ivParameter, _ := hex.DecodeString(hexIV) + block, err := aes.NewCipher(secretKey) + if err != nil { + return "" } - encryptedBody := "" + decrypter := cipher.NewCBCDecrypter(block, ivParameter) + text, _ := hex.DecodeString(cipherText) + decrypted := make([]byte, len(text)) + decrypter.CryptBlocks(decrypted, text) - if len(body) != 0 { - sb3 := Md5(resourcePath+tenant+token+USER_TYPE) + sendDate + CONTENT_ENCRYPTED + contentType - a2 := Md5(sb3) - a3 := Md5(sendDate) - if len(body) != 0 && len(a2) != 0 && len(a3) != 0 { - encryptedBody = Encrypt(body, a2, a3) - } - } - - return encryptedBody + return string(PKCS5Trimming(decrypted)) } -func DecryptRequest(path string, time int64, tenant, token, body, contentType string) string { - timeStamp := strconv.FormatInt(time, 10) - resourcePath := "" - - if len(path) != 0 { - resourcePath = "/" + path +func Encrypt(plainText, hexKey, hexIV string) string { + if len(plainText) == 0 || len(hexKey) == 0 || len(hexIV) == 0 { + return "" } - if len(body) != 0 { - sb3 := Md5(resourcePath+tenant+token+USER_TYPE) + timeStamp + CONTENT_ENCRYPTED + contentType - a2 := Md5(sb3) - timeStampHash := Md5(timeStamp) - return Decrypt(body, a2, timeStampHash) + secretKey, _ := hex.DecodeString(hexKey) + ivParameter, _ := hex.DecodeString(hexIV) + block, err := aes.NewCipher(secretKey) + if err != nil { + return "" } - return "" + + content := PKCS5Padding([]byte(plainText), block.BlockSize()) + encrypter := cipher.NewCBCEncrypter(block, ivParameter) + + ciphertext := make([]byte, len(content)) + encrypter.CryptBlocks(ciphertext, content) + + return hex.EncodeToString(ciphertext) } diff --git a/vehicle/saic/requests/hashUtils.go b/vehicle/saic/requests/hashUtils.go deleted file mode 100644 index 78ec56e73..000000000 --- a/vehicle/saic/requests/hashUtils.go +++ /dev/null @@ -1,32 +0,0 @@ -package requests - -import ( - "crypto/md5" - "crypto/sha1" - "crypto/sha256" - "encoding/hex" -) - -func Md5(value string) string { - if len(value) == 0 { - return "" - } - result := md5.Sum([]byte(value)) - return hex.EncodeToString(result[:]) -} - -func Sha1(value string) string { - if len(value) == 0 { - return "" - } - result := sha1.Sum([]byte(value)) - return hex.EncodeToString(result[:]) -} - -func Sha256(value string) string { - if len(value) == 0 { - return "" - } - result := sha256.Sum256([]byte(value)) - return hex.EncodeToString(result[:]) -} diff --git a/vehicle/saic/requests/helper.go b/vehicle/saic/requests/helper.go new file mode 100644 index 000000000..ac335d767 --- /dev/null +++ b/vehicle/saic/requests/helper.go @@ -0,0 +1,40 @@ +package requests + +import ( + "crypto/hmac" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "encoding/hex" + "hash" +) + +func sum(hash hash.Hash, value string) string { + if len(value) == 0 { + return "" + } + hash.Write([]byte(value)) + return hex.EncodeToString(hash.Sum(nil)) +} + +func Md5(value string) string { + return sum(md5.New(), value) +} + +func Sha1(value string) string { + return sum(sha1.New(), value) +} + +func Sha256(value string) string { + return sum(sha256.New(), value) +} + +func HmacSha256(secret string, message string) string { + if len(secret) == 0 || len(message) == 0 { + return "" + } + + mac := hmac.New(sha256.New, []byte(secret)) + mac.Write([]byte(message)) + return hex.EncodeToString(mac.Sum(nil)) +} diff --git a/vehicle/saic/requests/macUtils.go b/vehicle/saic/requests/macUtils.go deleted file mode 100644 index a08b97cc8..000000000 --- a/vehicle/saic/requests/macUtils.go +++ /dev/null @@ -1,18 +0,0 @@ -package requests - -import ( - "crypto/hmac" - "crypto/sha256" - "encoding/hex" -) - -func HmacSha256(secret string, message string) string { - if len(secret) == 0 || len(message) == 0 { - return "" - } - - key := []byte(secret) - h := hmac.New(sha256.New, key) - h.Write([]byte(message)) - return hex.EncodeToString(h.Sum(nil)) -} diff --git a/vehicle/saic/requests/request.go b/vehicle/saic/requests/request.go new file mode 100644 index 000000000..06824d881 --- /dev/null +++ b/vehicle/saic/requests/request.go @@ -0,0 +1,135 @@ +package requests + +import ( + "bytes" + "errors" + "io" + "net/http" + "strconv" + "strings" + "time" +) + +func Decorate(req *http.Request) error { + req.Header.Set("tenant-id", TENANT_ID) + req.Header.Set("user-type", USER_TYPE) + req.Header.Set("app-content-encrypted", CONTENT_ENCRYPTED) + req.Header.Set("Authorization", PARAM_AUTHENTICATION) + + return nil +} + +func encryptRequest(resourcePath string, sendDate int64, tenant, token, body, contentType string) string { + if len(body) == 0 { + return "" + } + + dateString := strconv.FormatInt(sendDate, 10) + + // tenant + if len(resourcePath) != 0 { + resourcePath = "/" + resourcePath + } + + sb3 := Md5(resourcePath+tenant+token+USER_TYPE) + dateString + CONTENT_ENCRYPTED + contentType + a2 := Md5(sb3) + a3 := Md5(dateString) + + return Encrypt(body, a2, a3) +} + +func calculateRequestVerification( + resourcePath string, + sendDate int64, + tenant, contentType, bodyEncrypted, token string, +) string { + dateString := strconv.FormatInt(sendDate, 10) + str9 := resourcePath + tenant + token + USER_TYPE + a2 := Md5(str9) + str10 := dateString + CONTENT_ENCRYPTED + contentType + a3 := Md5(a2 + str10) + str11 := resourcePath + tenant + token + USER_TYPE + dateString + CONTENT_ENCRYPTED + contentType + bodyEncrypted + + a5 := Md5(a3 + dateString) + + return HmacSha256(a5, str11) +} + +func CreateRequest(baseUrl, path, httpMethod, request, contentType, token, eventId string) (*http.Request, error) { + sendDate := time.Now().UnixMilli() + + endpoint := baseUrl + path + + if len(request) != 0 { + request = encryptRequest( + path, + sendDate, + TENANT_ID, + token, + request, + contentType) + } + + req, err := http.NewRequest(httpMethod, endpoint, bytes.NewReader([]byte(request))) + if err != nil { + return nil, err + } + + Decorate(req) + req.Header.Set("app-send-date", strconv.FormatInt(sendDate, 10)) + req.Header.Set("original-content-type", contentType) + + if len(token) != 0 { + req.Header.Set("blade-auth", token) + } + if len(eventId) != 0 { + req.Header.Set("event-id", eventId) + } + + replace := strings.Replace(endpoint, baseUrl, "/", -1) + + req.Header.Set("app-verification-string", + calculateRequestVerification( + replace, + sendDate, + TENANT_ID, + contentType, + request, + token)) + + return req, nil +} + +func decryptResponse(timeStamp, contentType, cipherText string) string { + if len(cipherText) == 0 { + return "" + } + + str4 := timeStamp + CONTENT_ENCRYPTED + contentType + a2 := Md5(str4) + hashedTimeStamp := Md5(timeStamp) + + return Decrypt(cipherText, a2, hashedTimeStamp) +} + +func DecodeResponse(resp *http.Response) ([]byte, error) { + if resp.StatusCode != http.StatusOK { + return nil, errors.New(resp.Status) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return body, err + } + + if resp.Header.Get("app-content-encrypted") != "1" { + return body, nil + } + + decoded := decryptResponse( + resp.Header.Get("app-send-date"), + resp.Header.Get("original-content-type"), + string(body)) + + return []byte(decoded), nil +} diff --git a/vehicle/saic/requests/sendRequest.go b/vehicle/saic/requests/sendRequest.go deleted file mode 100644 index 6af226ee8..000000000 --- a/vehicle/saic/requests/sendRequest.go +++ /dev/null @@ -1,98 +0,0 @@ -package requests - -import ( - "bytes" - "errors" - "io" - "net/http" - "strconv" - "strings" - "time" -) - -func Decorate(req *http.Request) error { - req.Header.Set("tenant-id", TENANT_ID) - req.Header.Set("user-type", USER_TYPE) - req.Header.Set("app-content-encrypted", CONTENT_ENCRYPTED) - req.Header.Set("Authorization", PARAM_AUTHENTICATION) - - return nil -} - -func CreateRequest( - baseUrl string, - path string, - httpMethod string, - request string, - contentType string, - token string, - eventId string, -) (*http.Request, error) { - appSendDate := time.Now().UnixMilli() - - endpoint := baseUrl + path - - if len(request) != 0 { - request = EncryptRequest( - path, - appSendDate, - TENANT_ID, - token, - request, - contentType) - } - - bodyReader := bytes.NewReader([]byte(request)) - req, err := http.NewRequest(httpMethod, endpoint, bodyReader) - if err != nil { - return nil, err - } - - Decorate(req) - req.Header.Set("app-send-date", strconv.FormatInt(appSendDate, 10)) - req.Header.Set("original-content-type", contentType) - - if len(token) != 0 { - req.Header.Set("blade-auth", token) - } - if len(eventId) != 0 { - req.Header.Set("event-id", eventId) - } - - replace := strings.Replace(endpoint, baseUrl, "/", -1) - - req.Header.Set("app-verification-string", - CalculateRequestVerification( - replace, - appSendDate, - TENANT_ID, - contentType, - request, - token)) - - return req, nil -} - -func DecryptAnswer(resp *http.Response) ([]byte, error) { - var body string - var bodyRaw []byte - var err error - if resp.StatusCode == http.StatusOK { - bodyRaw, err = io.ReadAll(resp.Body) - if err != nil { - return bodyRaw, err - } - if resp.Header.Get("app-content-encrypted") == "1" { - body = DecryptResponse( - resp.Header.Get("app-send-date"), - resp.Header.Get("original-content-type"), - string(bodyRaw)) - } else { - body = string(bodyRaw) - } - } else { - err = errors.New(resp.Status) - } - - return []byte(body), err -} diff --git a/vehicle/saic/requests/types.go b/vehicle/saic/requests/types.go index d16bbd271..4e51b3ad7 100644 --- a/vehicle/saic/requests/types.go +++ b/vehicle/saic/requests/types.go @@ -1,5 +1,12 @@ package requests +const ( + CONTENT_ENCRYPTED = "1" + PARAM_AUTHENTICATION = "Basic c3dvcmQ6c3dvcmRfc2VjcmV0" + TENANT_ID = "459771" + USER_TYPE = "app" +) + type ChargeStatus struct { RvsChargeStatus struct { MileageSinceLastCharge int @@ -91,8 +98,8 @@ type LoginData struct { Jti string } -type Answer struct { +type Answer[T any] struct { Code int `json:"code"` - Data any `json:"data,omitempty"` Message string `json:"message"` + Data T `json:"data,omitempty"` }