fix: better `IsStatusSharedURL` (#4280)
* fix: better `IsStatusSharedURL` * refactor `ParseSharedURL`
This commit is contained in:
parent
c3687acc84
commit
b36d95d84d
|
@ -162,7 +162,7 @@ func (m *Messenger) UnfurlURLs(httpClient *http.Client, urls []string) (UnfurlUR
|
|||
for _, url := range urls {
|
||||
m.logger.Debug("unfurling", zap.String("url", url))
|
||||
|
||||
if m.IsStatusSharedURL(url) {
|
||||
if IsStatusSharedURL(url) {
|
||||
unfurler := NewStatusUnfurler(url, m, m.logger)
|
||||
preview, err := unfurler.Unfurl()
|
||||
if err != nil {
|
||||
|
|
|
@ -48,8 +48,22 @@ type URLDataResponse struct {
|
|||
}
|
||||
|
||||
const baseShareURL = "https://status.app"
|
||||
const userPath = "u#"
|
||||
const userWithDataPath = "u/"
|
||||
const communityPath = "c#"
|
||||
const communityWithDataPath = "c/"
|
||||
const channelPath = "cc/"
|
||||
|
||||
const sharedURLUserPrefix = baseShareURL + "/" + userPath
|
||||
const sharedURLUserPrefixWithData = baseShareURL + "/" + userWithDataPath
|
||||
const sharedURLCommunityPrefix = baseShareURL + "/" + communityPath
|
||||
const sharedURLCommunityPrefixWithData = baseShareURL + "/" + communityWithDataPath
|
||||
const sharedURLChannelPrefixWithData = baseShareURL + "/" + channelPath
|
||||
|
||||
const channelUUIDRegExp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||
|
||||
var channelRegExp = regexp.MustCompile(channelUUIDRegExp)
|
||||
|
||||
func (m *Messenger) SerializePublicKey(compressedKey types.HexBytes) (string, error) {
|
||||
return utils.SerializePublicKey(compressedKey)
|
||||
}
|
||||
|
@ -511,51 +525,68 @@ func (m *Messenger) parseUserURLWithData(data string, chatKey string) (*URLDataR
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (m *Messenger) IsStatusSharedURL(url string) bool {
|
||||
return strings.HasPrefix(url, baseShareURL)
|
||||
func IsStatusSharedURL(url string) bool {
|
||||
return strings.HasPrefix(url, sharedURLUserPrefix) ||
|
||||
strings.HasPrefix(url, sharedURLUserPrefixWithData) ||
|
||||
strings.HasPrefix(url, sharedURLCommunityPrefix) ||
|
||||
strings.HasPrefix(url, sharedURLCommunityPrefixWithData) ||
|
||||
strings.HasPrefix(url, sharedURLChannelPrefixWithData)
|
||||
}
|
||||
|
||||
func splitSharedURLData(data string) (string, string, error) {
|
||||
const count = 2
|
||||
contents := strings.SplitN(data, "#", count)
|
||||
if len(contents) != count {
|
||||
return "", "", fmt.Errorf("url should contain at least one `#` separator")
|
||||
}
|
||||
return contents[0], contents[1], nil
|
||||
}
|
||||
|
||||
func (m *Messenger) ParseSharedURL(url string) (*URLDataResponse, error) {
|
||||
if !m.IsStatusSharedURL(url) {
|
||||
return nil, fmt.Errorf("url should start with '%s'", baseShareURL)
|
||||
|
||||
if strings.HasPrefix(url, sharedURLUserPrefix) {
|
||||
chatKey := strings.TrimPrefix(url, sharedURLUserPrefix)
|
||||
if strings.HasPrefix(chatKey, "zQ3sh") {
|
||||
return m.parseUserURLWithChatKey(chatKey)
|
||||
}
|
||||
return m.parseUserURLWithENS(chatKey)
|
||||
}
|
||||
|
||||
urlContents := regexp.MustCompile(`\#`).Split(strings.TrimPrefix(url, baseShareURL+"/"), 2)
|
||||
if len(urlContents) != 2 {
|
||||
return nil, fmt.Errorf("url should contain at least one `#` separator")
|
||||
}
|
||||
|
||||
if urlContents[0] == "c" {
|
||||
return m.parseCommunityURLWithChatKey(urlContents[1])
|
||||
}
|
||||
|
||||
if strings.HasPrefix(urlContents[0], "c/") {
|
||||
return m.parseCommunityURLWithData(strings.TrimPrefix(urlContents[0], "c/"), urlContents[1])
|
||||
}
|
||||
|
||||
if strings.HasPrefix(urlContents[0], "cc/") {
|
||||
first := strings.TrimPrefix(urlContents[0], "cc/")
|
||||
|
||||
isChannel, err := regexp.MatchString(channelUUIDRegExp, first)
|
||||
if strings.HasPrefix(url, sharedURLUserPrefixWithData) {
|
||||
trimmedURL := strings.TrimPrefix(url, sharedURLUserPrefixWithData)
|
||||
encodedData, chatKey, err := splitSharedURLData(trimmedURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isChannel {
|
||||
return m.parseCommunityChannelURLWithChatKey(first, urlContents[1])
|
||||
}
|
||||
return m.parseCommunityChannelURLWithData(first, urlContents[1])
|
||||
return m.parseUserURLWithData(encodedData, chatKey)
|
||||
}
|
||||
|
||||
if urlContents[0] == "u" {
|
||||
if strings.HasPrefix(urlContents[1], "zQ3sh") {
|
||||
return m.parseUserURLWithChatKey(urlContents[1])
|
||||
}
|
||||
return m.parseUserURLWithENS(urlContents[1])
|
||||
if strings.HasPrefix(url, sharedURLCommunityPrefix) {
|
||||
chatKey := strings.TrimPrefix(url, sharedURLCommunityPrefix)
|
||||
return m.parseCommunityURLWithChatKey(chatKey)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(urlContents[0], "u/") {
|
||||
return m.parseUserURLWithData(strings.TrimPrefix(urlContents[0], "u/"), urlContents[1])
|
||||
if strings.HasPrefix(url, sharedURLCommunityPrefixWithData) {
|
||||
trimmedURL := strings.TrimPrefix(url, sharedURLCommunityPrefixWithData)
|
||||
encodedData, chatKey, err := splitSharedURLData(trimmedURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m.parseCommunityURLWithData(encodedData, chatKey)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unhandled shared url: %s", url)
|
||||
if strings.HasPrefix(url, sharedURLChannelPrefixWithData) {
|
||||
trimmedURL := strings.TrimPrefix(url, sharedURLChannelPrefixWithData)
|
||||
encodedData, chatKey, err := splitSharedURLData(trimmedURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if channelRegExp.MatchString(encodedData) {
|
||||
return m.parseCommunityChannelURLWithChatKey(encodedData, chatKey)
|
||||
}
|
||||
return m.parseCommunityChannelURLWithData(encodedData, chatKey)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("not a status shared url")
|
||||
}
|
||||
|
|
|
@ -17,6 +17,15 @@ import (
|
|||
"github.com/status-im/status-go/protocol/urls"
|
||||
)
|
||||
|
||||
const (
|
||||
userURL = "https://status.app/u#zQ3shwQPhRuDJSjVGVBnTjCdgXy5i9WQaeVPdGJD6yTarJQSj"
|
||||
userURLWithData = "https://status.app/u/G10A4B0JdgwyRww90WXtnP1oNH1ZLQNM0yX0Ja9YyAMjrqSZIYINOHCbFhrnKRAcPGStPxCMJDSZlGCKzmZrJcimHY8BbcXlORrElv_BbQEegnMDPx1g9C5VVNl0fE4y#zQ3shwQPhRuDJSjVGVBnTjCdgXy5i9WQaeVPdGJD6yTarJQSj"
|
||||
communityURL = "https://status.app/c#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11"
|
||||
communityURLWithData = "https://status.app/c/iyKACkQKB0Rvb2RsZXMSJ0NvbG9yaW5nIHRoZSB3b3JsZCB3aXRoIGpveSDigKIg4bSXIOKAohiYohsiByMxMzFEMkYqAwEhMwM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11"
|
||||
channelURL = "https://status.app/cc/003cdcd5-e065-48f9-b166-b1a94ac75a11#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11"
|
||||
channelURLWithData = "https://status.app/cc/G54AAKwObLdpiGjXnckYzRcOSq0QQAS_CURGfqVU42ceGHCObstUIknTTZDOKF3E8y2MSicncpO7fTskXnoACiPKeejvjtLTGWNxUhlT7fyQS7Jrr33UVHluxv_PLjV2ePGw5GQ33innzeK34pInIgUGs5RjdQifMVmURalxxQKwiuoY5zwIjixWWRHqjHM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11"
|
||||
)
|
||||
|
||||
func TestMessengerShareUrlsSuite(t *testing.T) {
|
||||
suite.Run(t, new(MessengerShareUrlsSuite))
|
||||
}
|
||||
|
@ -134,23 +143,85 @@ func (s *MessengerShareUrlsSuite) TestDeserializePublicKey() {
|
|||
}
|
||||
|
||||
func (s *MessengerShareUrlsSuite) TestParseWrongUrls() {
|
||||
urls := map[string]string{
|
||||
"https://status.appc/#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": "unhandled shared url",
|
||||
"https://status.app/cc#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": "unhandled shared url",
|
||||
"https://status.app/a#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": "unhandled shared url",
|
||||
const notStatusSharedURLError = "not a status shared url"
|
||||
badURLs := map[string]string{
|
||||
"https://status.appc/#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": notStatusSharedURLError,
|
||||
"https://status.app/cc#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": notStatusSharedURLError,
|
||||
"https://status.app/a#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": notStatusSharedURLError,
|
||||
"https://status.im/u#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": notStatusSharedURLError,
|
||||
"https://status.app/u/": "url should contain at least one `#` separator",
|
||||
"https://status.im/u#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": "url should start with 'https://status.app'",
|
||||
}
|
||||
|
||||
for url, expectedError := range urls {
|
||||
for url, expectedError := range badURLs {
|
||||
urlData, err := s.m.ParseSharedURL(url)
|
||||
s.Require().Error(err)
|
||||
|
||||
s.Require().True(strings.HasPrefix(err.Error(), expectedError))
|
||||
s.Require().Equal(err.Error(), expectedError)
|
||||
s.Require().Nil(urlData)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MessengerShareUrlsSuite) TestIsStatusSharedUrl() {
|
||||
testCases := []struct {
|
||||
Name string
|
||||
URL string
|
||||
Result bool
|
||||
}{
|
||||
{
|
||||
Name: "Direct website link",
|
||||
URL: "https://status.app",
|
||||
Result: false,
|
||||
},
|
||||
{
|
||||
Name: "Website page link",
|
||||
URL: "https://status.app/features/messenger",
|
||||
Result: false,
|
||||
},
|
||||
{
|
||||
// starts with `/c`, but no `#` after
|
||||
Name: "Website page link",
|
||||
URL: "https://status.app/communities",
|
||||
Result: false,
|
||||
},
|
||||
{
|
||||
Name: "User link",
|
||||
URL: userURL,
|
||||
Result: true,
|
||||
},
|
||||
{
|
||||
Name: "User link with data",
|
||||
URL: userURLWithData,
|
||||
Result: true,
|
||||
},
|
||||
{
|
||||
Name: "Community link",
|
||||
URL: communityURL,
|
||||
Result: true,
|
||||
},
|
||||
{
|
||||
Name: "Community link with data",
|
||||
URL: communityURLWithData,
|
||||
Result: true,
|
||||
},
|
||||
{
|
||||
Name: "Channel link",
|
||||
URL: channelURL,
|
||||
Result: true,
|
||||
},
|
||||
{
|
||||
Name: "Channel link with data",
|
||||
URL: channelURLWithData,
|
||||
Result: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.Name, func() {
|
||||
result := IsStatusSharedURL(tc.URL)
|
||||
s.Require().Equal(tc.Result, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MessengerShareUrlsSuite) TestShareCommunityURLWithChatKey() {
|
||||
community := s.createCommunity()
|
||||
|
||||
|
@ -198,9 +269,7 @@ func (s *MessengerShareUrlsSuite) TestShareCommunityURLWithData() {
|
|||
}
|
||||
|
||||
func (s *MessengerShareUrlsSuite) TestParseCommunityURLWithData() {
|
||||
url := "https://status.app/c/iyKACkQKB0Rvb2RsZXMSJ0NvbG9yaW5nIHRoZSB3b3JsZCB3aXRoIGpveSDigKIg4bSXIOKAohiYohsiByMxMzFEMkYqAwEhMwM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11"
|
||||
|
||||
urlData, err := s.m.ParseSharedURL(url)
|
||||
urlData, err := s.m.ParseSharedURL(communityURLWithData)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(urlData)
|
||||
|
||||
|
@ -290,9 +359,7 @@ func (s *MessengerShareUrlsSuite) TestShareCommunityChannelURLWithData() {
|
|||
}
|
||||
|
||||
func (s *MessengerShareUrlsSuite) TestParseCommunityChannelURLWithData() {
|
||||
url := "https://status.app/cc/G54AAKwObLdpiGjXnckYzRcOSq0QQAS_CURGfqVU42ceGHCObstUIknTTZDOKF3E8y2MSicncpO7fTskXnoACiPKeejvjtLTGWNxUhlT7fyQS7Jrr33UVHluxv_PLjV2ePGw5GQ33innzeK34pInIgUGs5RjdQifMVmURalxxQKwiuoY5zwIjixWWRHqjHM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11"
|
||||
|
||||
urlData, err := s.m.ParseSharedURL(url)
|
||||
urlData, err := s.m.ParseSharedURL(channelURLWithData)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(urlData)
|
||||
|
||||
|
@ -392,8 +459,7 @@ func (s *MessengerShareUrlsSuite) TestShareUserURLWithENS() {
|
|||
// }
|
||||
|
||||
func (s *MessengerShareUrlsSuite) TestParseUserURLWithData() {
|
||||
url := "https://status.app/u/G10A4B0JdgwyRww90WXtnP1oNH1ZLQNM0yX0Ja9YyAMjrqSZIYINOHCbFhrnKRAcPGStPxCMJDSZlGCKzmZrJcimHY8BbcXlORrElv_BbQEegnMDPx1g9C5VVNl0fE4y#zQ3shwQPhRuDJSjVGVBnTjCdgXy5i9WQaeVPdGJD6yTarJQSj"
|
||||
urlData, err := s.m.ParseSharedURL(url)
|
||||
urlData, err := s.m.ParseSharedURL(userURLWithData)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(urlData)
|
||||
|
||||
|
|
Loading…
Reference in New Issue