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 {
|
for _, url := range urls {
|
||||||
m.logger.Debug("unfurling", zap.String("url", url))
|
m.logger.Debug("unfurling", zap.String("url", url))
|
||||||
|
|
||||||
if m.IsStatusSharedURL(url) {
|
if IsStatusSharedURL(url) {
|
||||||
unfurler := NewStatusUnfurler(url, m, m.logger)
|
unfurler := NewStatusUnfurler(url, m, m.logger)
|
||||||
preview, err := unfurler.Unfurl()
|
preview, err := unfurler.Unfurl()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -48,8 +48,22 @@ type URLDataResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseShareURL = "https://status.app"
|
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}$"
|
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) {
|
func (m *Messenger) SerializePublicKey(compressedKey types.HexBytes) (string, error) {
|
||||||
return utils.SerializePublicKey(compressedKey)
|
return utils.SerializePublicKey(compressedKey)
|
||||||
}
|
}
|
||||||
|
@ -511,51 +525,68 @@ func (m *Messenger) parseUserURLWithData(data string, chatKey string) (*URLDataR
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Messenger) IsStatusSharedURL(url string) bool {
|
func IsStatusSharedURL(url string) bool {
|
||||||
return strings.HasPrefix(url, baseShareURL)
|
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) {
|
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 strings.HasPrefix(url, sharedURLUserPrefixWithData) {
|
||||||
if len(urlContents) != 2 {
|
trimmedURL := strings.TrimPrefix(url, sharedURLUserPrefixWithData)
|
||||||
return nil, fmt.Errorf("url should contain at least one `#` separator")
|
encodedData, chatKey, err := splitSharedURLData(trimmedURL)
|
||||||
}
|
|
||||||
|
|
||||||
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 err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if isChannel {
|
return m.parseUserURLWithData(encodedData, chatKey)
|
||||||
return m.parseCommunityChannelURLWithChatKey(first, urlContents[1])
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(url, sharedURLCommunityPrefix) {
|
||||||
|
chatKey := strings.TrimPrefix(url, sharedURLCommunityPrefix)
|
||||||
|
return m.parseCommunityURLWithChatKey(chatKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(url, sharedURLCommunityPrefixWithData) {
|
||||||
|
trimmedURL := strings.TrimPrefix(url, sharedURLCommunityPrefixWithData)
|
||||||
|
encodedData, chatKey, err := splitSharedURLData(trimmedURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return m.parseCommunityChannelURLWithData(first, urlContents[1])
|
return m.parseCommunityURLWithData(encodedData, chatKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if urlContents[0] == "u" {
|
if strings.HasPrefix(url, sharedURLChannelPrefixWithData) {
|
||||||
if strings.HasPrefix(urlContents[1], "zQ3sh") {
|
trimmedURL := strings.TrimPrefix(url, sharedURLChannelPrefixWithData)
|
||||||
return m.parseUserURLWithChatKey(urlContents[1])
|
encodedData, chatKey, err := splitSharedURLData(trimmedURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return m.parseUserURLWithENS(urlContents[1])
|
|
||||||
|
if channelRegExp.MatchString(encodedData) {
|
||||||
|
return m.parseCommunityChannelURLWithChatKey(encodedData, chatKey)
|
||||||
|
}
|
||||||
|
return m.parseCommunityChannelURLWithData(encodedData, chatKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(urlContents[0], "u/") {
|
return nil, fmt.Errorf("not a status shared url")
|
||||||
return m.parseUserURLWithData(strings.TrimPrefix(urlContents[0], "u/"), urlContents[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("unhandled shared url: %s", url)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,15 @@ import (
|
||||||
"github.com/status-im/status-go/protocol/urls"
|
"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) {
|
func TestMessengerShareUrlsSuite(t *testing.T) {
|
||||||
suite.Run(t, new(MessengerShareUrlsSuite))
|
suite.Run(t, new(MessengerShareUrlsSuite))
|
||||||
}
|
}
|
||||||
|
@ -134,23 +143,85 @@ func (s *MessengerShareUrlsSuite) TestDeserializePublicKey() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessengerShareUrlsSuite) TestParseWrongUrls() {
|
func (s *MessengerShareUrlsSuite) TestParseWrongUrls() {
|
||||||
urls := map[string]string{
|
const notStatusSharedURLError = "not a status shared url"
|
||||||
"https://status.appc/#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": "unhandled shared url",
|
badURLs := map[string]string{
|
||||||
"https://status.app/cc#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": "unhandled shared url",
|
"https://status.appc/#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": notStatusSharedURLError,
|
||||||
"https://status.app/a#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11": "unhandled shared url",
|
"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.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)
|
urlData, err := s.m.ParseSharedURL(url)
|
||||||
s.Require().Error(err)
|
s.Require().Error(err)
|
||||||
|
s.Require().Equal(err.Error(), expectedError)
|
||||||
s.Require().True(strings.HasPrefix(err.Error(), expectedError))
|
|
||||||
s.Require().Nil(urlData)
|
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() {
|
func (s *MessengerShareUrlsSuite) TestShareCommunityURLWithChatKey() {
|
||||||
community := s.createCommunity()
|
community := s.createCommunity()
|
||||||
|
|
||||||
|
@ -198,9 +269,7 @@ func (s *MessengerShareUrlsSuite) TestShareCommunityURLWithData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessengerShareUrlsSuite) TestParseCommunityURLWithData() {
|
func (s *MessengerShareUrlsSuite) TestParseCommunityURLWithData() {
|
||||||
url := "https://status.app/c/iyKACkQKB0Rvb2RsZXMSJ0NvbG9yaW5nIHRoZSB3b3JsZCB3aXRoIGpveSDigKIg4bSXIOKAohiYohsiByMxMzFEMkYqAwEhMwM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11"
|
urlData, err := s.m.ParseSharedURL(communityURLWithData)
|
||||||
|
|
||||||
urlData, err := s.m.ParseSharedURL(url)
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotNil(urlData)
|
s.Require().NotNil(urlData)
|
||||||
|
|
||||||
|
@ -290,9 +359,7 @@ func (s *MessengerShareUrlsSuite) TestShareCommunityChannelURLWithData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessengerShareUrlsSuite) TestParseCommunityChannelURLWithData() {
|
func (s *MessengerShareUrlsSuite) TestParseCommunityChannelURLWithData() {
|
||||||
url := "https://status.app/cc/G54AAKwObLdpiGjXnckYzRcOSq0QQAS_CURGfqVU42ceGHCObstUIknTTZDOKF3E8y2MSicncpO7fTskXnoACiPKeejvjtLTGWNxUhlT7fyQS7Jrr33UVHluxv_PLjV2ePGw5GQ33innzeK34pInIgUGs5RjdQifMVmURalxxQKwiuoY5zwIjixWWRHqjHM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11"
|
urlData, err := s.m.ParseSharedURL(channelURLWithData)
|
||||||
|
|
||||||
urlData, err := s.m.ParseSharedURL(url)
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotNil(urlData)
|
s.Require().NotNil(urlData)
|
||||||
|
|
||||||
|
@ -392,8 +459,7 @@ func (s *MessengerShareUrlsSuite) TestShareUserURLWithENS() {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
func (s *MessengerShareUrlsSuite) TestParseUserURLWithData() {
|
func (s *MessengerShareUrlsSuite) TestParseUserURLWithData() {
|
||||||
url := "https://status.app/u/G10A4B0JdgwyRww90WXtnP1oNH1ZLQNM0yX0Ja9YyAMjrqSZIYINOHCbFhrnKRAcPGStPxCMJDSZlGCKzmZrJcimHY8BbcXlORrElv_BbQEegnMDPx1g9C5VVNl0fE4y#zQ3shwQPhRuDJSjVGVBnTjCdgXy5i9WQaeVPdGJD6yTarJQSj"
|
urlData, err := s.m.ParseSharedURL(userURLWithData)
|
||||||
urlData, err := s.m.ParseSharedURL(url)
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotNil(urlData)
|
s.Require().NotNil(urlData)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue