diff --git a/csgo/demo_loader.go b/csgo/demo_loader.go index de393fd..9c60ed2 100644 --- a/csgo/demo_loader.go +++ b/csgo/demo_loader.go @@ -115,7 +115,7 @@ func (d *DemoMatchLoader) getMatchDetails(sharecode string) (*protobuf.CMsgGCCSt if err != nil { return nil, err } - err = d.requestDemoInfo(matchId, outcomeId, tokenId) + err = d.requestDemoInfo(matchId, outcomeId, uint32(tokenId)) if err != nil { return nil, err } diff --git a/csgo/sharecode.go b/csgo/sharecode.go index 679230a..55e7f68 100644 --- a/csgo/sharecode.go +++ b/csgo/sharecode.go @@ -1,7 +1,7 @@ package csgo import ( - "encoding/hex" + "encoding/binary" "fmt" "math/big" "regexp" @@ -12,13 +12,12 @@ import ( var DICT = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789" var sharecodeRexEx = regexp.MustCompile("^CSGO(?:-?[\\w]{5}){5}$") -func DecodeSharecode(code string) (uint64, uint64, uint32, error) { +func DecodeSharecode(code string) (uint64, uint64, uint16, error) { if !sharecodeRexEx.MatchString(code) { return 0, 0, 0, fmt.Errorf("not a CSGO sharecode: %s", code) } - cleanCode := strings.ReplaceAll(code, "CSGO", "") - cleanCode = strings.ReplaceAll(cleanCode, "-", "") + cleanCode := strings.ReplaceAll(strings.ReplaceAll(code, "CSGO", ""), "-", "") chars := ReverseString(strings.Split(cleanCode, "")) bigInt := new(big.Int) @@ -29,24 +28,17 @@ func DecodeSharecode(code string) (uint64, uint64, uint32, error) { bigInt.Add(bigInt, big.NewInt(int64(strings.Index(DICT, c)))) } + if bigInt.BitLen() > 144 { + return 0, 0, 0, fmt.Errorf("invalid sharecode") + } bytes := make([]byte, 18) bigInt.FillBytes(bytes) - matchId := new(big.Int) - matchId.SetString(hex.EncodeToString(Reverse(bytes[0:8])), 16) - outcomeId := new(big.Int) - outcomeId.SetString(hex.EncodeToString(Reverse(bytes[8:16])), 16) - tokenId := new(big.Int) - tokenId.SetString(hex.EncodeToString(Reverse(bytes[16:18])), 16) + matchId := binary.LittleEndian.Uint64(bytes[0:8]) + outcomeId := binary.LittleEndian.Uint64(bytes[8:16]) + tokenId := binary.LittleEndian.Uint16(bytes[16:18]) - return matchId.Uint64(), outcomeId.Uint64(), uint32(tokenId.Uint64()), nil -} - -func Reverse(numbers []byte) []byte { - for i, j := 0, len(numbers)-1; i < j; i, j = i+1, j-1 { - numbers[i], numbers[j] = numbers[j], numbers[i] - } - return numbers + return matchId, outcomeId, tokenId, nil } func ReverseString(numbers []string) []string { diff --git a/csgo/sharecode_test.go b/csgo/sharecode_test.go index 9ebc254..8f37adc 100644 --- a/csgo/sharecode_test.go +++ b/csgo/sharecode_test.go @@ -3,10 +3,10 @@ package csgo import "testing" //goland:noinspection SpellCheckingInspection -func TestSharecode(t *testing.T) { +func TestGoodSharecodes(t *testing.T) { eMatchId := uint64(3505575050994516382) eOutcomeId := uint64(3505581094013501947) - eTokenId := uint32(12909) + eTokenId := uint16(12909) matchId, outcomeId, tokenId, err := DecodeSharecode("CSGO-P9k3F-eVL9n-LZLXN-DrBGF-VKD7K") if err != nil { @@ -27,3 +27,43 @@ func TestSharecode(t *testing.T) { t.Fail() } } + +func TestBadSharecodes(t *testing.T) { + matchId, outcomeId, tokenId, err := DecodeSharecode("CSGO-AAAAA-AAAAA-AAAAA-AAAAA-AAAAA") + if err != nil { + t.Log("error should not be set", err) + t.Fail() + } + + if matchId != 0 { + t.Logf("matchID should be 0, is %d", matchId) + t.Fail() + } + if outcomeId != 0 { + t.Logf("outcomeID should be 0, is %d", outcomeId) + t.Fail() + } + if tokenId != 0 { + t.Logf("tokenID should be 0, is %d", tokenId) + t.Fail() + } + + matchId, outcomeId, tokenId, err = DecodeSharecode("CSGO-99999-99999-99999-99999-99999") + if err == nil { + t.Log("error should be set", err) + t.Fail() + } + + if matchId != 0 { + t.Logf("matchID should be 0, is %d", matchId) + t.Fail() + } + if outcomeId != 0 { + t.Logf("outcomeID should be 0, is %d", outcomeId) + t.Fail() + } + if tokenId != 0 { + t.Logf("tokenID should be 0, is %d", tokenId) + t.Fail() + } +} diff --git a/utils/utils.go b/utils/utils.go index b610097..abf250c 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -155,7 +155,7 @@ type PlayerResponse struct { Name string `json:"name"` Avatar string `json:"avatar"` VAC bool `json:"vac"` - VACDate *time.Time `json:"vac_date,omitempty"` + VACDate *time.Time `json:"vac_date,omitempty"` Tracked bool `json:"tracked"` VanityURL string `json:"vanity_url,omitempty"` MatchStats MatchStats `json:"match_stats,omitempty"` @@ -188,11 +188,10 @@ type MatchResponse struct { } const ( - steamID64Entry = "https://steamcommunity.com/profiles/%d?xml=1" - steamVanityURLEntry = "https://steamcommunity.com/id/%s?xml=1" - shareCodeURLEntry = "https://api.steampowered.com/ICSGOPlayers_730/GetNextMatchSharingCode/v1?key=%s&steamid=%d&steamidkey=%s&knowncode=%s" + shareCodeURLEntry = "https://api.steampowered.com/ICSGOPlayers_730/GetNextMatchSharingCode/v1?key=%s&steamid=%d&steamidkey=%s&knowncode=%s" ) +//goland:noinspection SpellCheckingInspection var ( SteamId64RegEx = regexp.MustCompile(`^\d{17}$`) ShareCodeRegEx = regexp.MustCompile(`^CSGO(?:-?[ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789]{5}){5}$`) @@ -213,7 +212,7 @@ func SendJSON(data interface{}, w http.ResponseWriter) error { return nil } -func GetMatchStats(db *ent.Client, lock *sync.RWMutex, dbPlayer *ent.Player) (int, int, int, error) { +func GetMatchStats(dbPlayer *ent.Player, lock *sync.RWMutex) (int, int, int, error) { var res []struct { MatchResult int `json:"match_result"` Count int `json:"count"`