mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-24 10:15:47 +03:00
[bugfix] Fix images not being processed correctly sometimes (#437)
* bump exif-terminator to latest version * add and test giant turnip from turnip.farm * don't error if content property is nil
This commit is contained in:
parent
36b2f2b4e6
commit
25cab0e1f4
22 changed files with 245 additions and 46 deletions
4
go.mod
4
go.mod
|
@ -32,7 +32,7 @@ require (
|
||||||
github.com/spf13/viper v1.10.0
|
github.com/spf13/viper v1.10.0
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/superseriousbusiness/activity v1.0.1-0.20211113133524-56560b73ace8
|
github.com/superseriousbusiness/activity v1.0.1-0.20211113133524-56560b73ace8
|
||||||
github.com/superseriousbusiness/exif-terminator v0.1.0
|
github.com/superseriousbusiness/exif-terminator v0.2.0
|
||||||
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB
|
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB
|
||||||
github.com/tdewolff/minify/v2 v2.9.22
|
github.com/tdewolff/minify/v2 v2.9.22
|
||||||
github.com/uptrace/bun v1.0.20
|
github.com/uptrace/bun v1.0.20
|
||||||
|
@ -59,7 +59,6 @@ require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b // indirect
|
github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b // indirect
|
||||||
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 // indirect
|
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 // indirect
|
||||||
github.com/dsoprea/go-jpeg-image-structure/v2 v2.0.0-20210512043942-b434301c6836 // indirect
|
|
||||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
||||||
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200610045659-121dd752914d // indirect
|
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200610045659-121dd752914d // indirect
|
||||||
github.com/dsoprea/go-png-image-structure/v2 v2.0.0-20210512210324-29b889a6093d // indirect
|
github.com/dsoprea/go-png-image-structure/v2 v2.0.0-20210512210324-29b889a6093d // indirect
|
||||||
|
@ -103,6 +102,7 @@ require (
|
||||||
github.com/spf13/cast v1.4.1 // indirect
|
github.com/spf13/cast v1.4.1 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/subosito/gotenv v1.2.0 // indirect
|
github.com/subosito/gotenv v1.2.0 // indirect
|
||||||
|
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe // indirect
|
||||||
github.com/tdewolff/parse/v2 v2.5.23 // indirect
|
github.com/tdewolff/parse/v2 v2.5.23 // indirect
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
github.com/ugorji/go/codec v1.2.6 // indirect
|
github.com/ugorji/go/codec v1.2.6 // indirect
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -155,8 +155,6 @@ github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b/go.mod h1:cg5SN
|
||||||
github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM=
|
github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM=
|
||||||
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 h1:YDRiMEm32T60Kpm35YzOK9ZHgjsS1Qrid+XskNcsdp8=
|
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 h1:YDRiMEm32T60Kpm35YzOK9ZHgjsS1Qrid+XskNcsdp8=
|
||||||
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM=
|
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM=
|
||||||
github.com/dsoprea/go-jpeg-image-structure/v2 v2.0.0-20210512043942-b434301c6836 h1:KGCiMMWxODEMmI3+9Ms04l73efoqFVNKKKPbVyOvKrU=
|
|
||||||
github.com/dsoprea/go-jpeg-image-structure/v2 v2.0.0-20210512043942-b434301c6836/go.mod h1:WaARaUjQuSuDCDFAiU/GwzfxMTJBulfEhqEA2Tx6B4Y=
|
|
||||||
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
|
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
|
||||||
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
|
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
|
||||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd h1:l+vLbuxptsC6VQyQsfD7NnEC8BZuFpz45PgY+pH8YTg=
|
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd h1:l+vLbuxptsC6VQyQsfD7NnEC8BZuFpz45PgY+pH8YTg=
|
||||||
|
@ -655,8 +653,10 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/superseriousbusiness/activity v1.0.1-0.20211113133524-56560b73ace8 h1:8Bwy6CSsT33/sF5FhjND4vr7jiJCaq4elNTAW4rUzVc=
|
github.com/superseriousbusiness/activity v1.0.1-0.20211113133524-56560b73ace8 h1:8Bwy6CSsT33/sF5FhjND4vr7jiJCaq4elNTAW4rUzVc=
|
||||||
github.com/superseriousbusiness/activity v1.0.1-0.20211113133524-56560b73ace8/go.mod h1:ZY9xwFDucvp6zTvM6FQZGl8PSOofPBFIAy6gSc85XkY=
|
github.com/superseriousbusiness/activity v1.0.1-0.20211113133524-56560b73ace8/go.mod h1:ZY9xwFDucvp6zTvM6FQZGl8PSOofPBFIAy6gSc85XkY=
|
||||||
github.com/superseriousbusiness/exif-terminator v0.1.0 h1:ePzfV0vcw+tm/haSOGzKbBTKkHAvyQLbCzfsdVkb3hM=
|
github.com/superseriousbusiness/exif-terminator v0.2.0 h1:C21KOUr54E37qTqYS7WJX0J83sNzzCwBEy0KXyDprqU=
|
||||||
github.com/superseriousbusiness/exif-terminator v0.1.0/go.mod h1:pmlOKzkFZWmqaucLAtrRbZG0R5F3dbrcLWOcd7gAOLI=
|
github.com/superseriousbusiness/exif-terminator v0.2.0/go.mod h1:DHJuKguXqyOVqB/oyOylutEDIZCbkYsn2GZFNSUDT9E=
|
||||||
|
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe h1:ksl2oCx/Qo8sNDc3Grb8WGKBM9nkvhCm25uvlT86azE=
|
||||||
|
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe/go.mod h1:gH4P6gN1V+wmIw5o97KGaa1RgXB/tVpC2UNzijhg3E4=
|
||||||
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB h1:PtW2w6budTvRV2J5QAoSvThTHBuvh8t/+BXIZFAaBSc=
|
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB h1:PtW2w6budTvRV2J5QAoSvThTHBuvh8t/+BXIZFAaBSc=
|
||||||
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB/go.mod h1:uYC/W92oVRJ49Vh1GcvTqpeFqHi+Ovrl2sMllQWRAEo=
|
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB/go.mod h1:uYC/W92oVRJ49Vh1GcvTqpeFqHi+Ovrl2sMllQWRAEo=
|
||||||
github.com/tdewolff/minify/v2 v2.9.22 h1:PlmaAakaJHdMMdTTwjjsuSwIxKqWPTlvjTj6a/g/ILU=
|
github.com/tdewolff/minify/v2 v2.9.22 h1:PlmaAakaJHdMMdTTwjjsuSwIxKqWPTlvjTj6a/g/ILU=
|
||||||
|
|
|
@ -50,17 +50,15 @@ type DereferencerStandardTestSuite struct {
|
||||||
dereferencer dereferencing.Dereferencer
|
dereferencer dereferencing.Dereferencer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *DereferencerStandardTestSuite) SetupSuite() {
|
func (suite *DereferencerStandardTestSuite) SetupTest() {
|
||||||
|
testrig.InitTestConfig()
|
||||||
|
testrig.InitTestLog()
|
||||||
|
|
||||||
suite.testAccounts = testrig.NewTestAccounts()
|
suite.testAccounts = testrig.NewTestAccounts()
|
||||||
suite.testRemoteStatuses = testrig.NewTestFediStatuses()
|
suite.testRemoteStatuses = testrig.NewTestFediStatuses()
|
||||||
suite.testRemotePeople = testrig.NewTestFediPeople()
|
suite.testRemotePeople = testrig.NewTestFediPeople()
|
||||||
suite.testRemoteGroups = testrig.NewTestFediGroups()
|
suite.testRemoteGroups = testrig.NewTestFediGroups()
|
||||||
suite.testRemoteAttachments = testrig.NewTestFediAttachments("../../../testrig/media")
|
suite.testRemoteAttachments = testrig.NewTestFediAttachments("../../../testrig/media")
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *DereferencerStandardTestSuite) SetupTest() {
|
|
||||||
testrig.InitTestLog()
|
|
||||||
testrig.InitTestConfig()
|
|
||||||
|
|
||||||
suite.db = testrig.NewTestDB()
|
suite.db = testrig.NewTestDB()
|
||||||
suite.storage = testrig.NewTestStorage()
|
suite.storage = testrig.NewTestStorage()
|
||||||
|
|
|
@ -131,6 +131,53 @@ func (suite *StatusTestSuite) TestDereferenceStatusWithMention() {
|
||||||
suite.False(m.Silent)
|
suite.False(m.Silent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *StatusTestSuite) TestDereferenceStatusWithImageAndNoContent() {
|
||||||
|
fetchingAccount := suite.testAccounts["local_account_1"]
|
||||||
|
|
||||||
|
statusURL := testrig.URLMustParse("https://turnip.farm/users/turniplover6969/statuses/70c53e54-3146-42d5-a630-83c8b6c7c042")
|
||||||
|
status, statusable, new, err := suite.dereferencer.GetRemoteStatus(context.Background(), fetchingAccount.Username, statusURL, false, false)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.NotNil(status)
|
||||||
|
suite.NotNil(statusable)
|
||||||
|
suite.True(new)
|
||||||
|
|
||||||
|
// status values should be set
|
||||||
|
suite.Equal("https://turnip.farm/users/turniplover6969/statuses/70c53e54-3146-42d5-a630-83c8b6c7c042", status.URI)
|
||||||
|
suite.Equal("https://turnip.farm/@turniplover6969/70c53e54-3146-42d5-a630-83c8b6c7c042", status.URL)
|
||||||
|
suite.Equal("", status.Content)
|
||||||
|
suite.Equal("https://turnip.farm/users/turniplover6969", status.AccountURI)
|
||||||
|
suite.False(status.Local)
|
||||||
|
suite.Empty(status.ContentWarning)
|
||||||
|
suite.Equal(gtsmodel.VisibilityPublic, status.Visibility)
|
||||||
|
suite.Equal(ap.ObjectNote, status.ActivityStreamsType)
|
||||||
|
|
||||||
|
// status should be in the database
|
||||||
|
dbStatus, err := suite.db.GetStatusByURI(context.Background(), status.URI)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(status.ID, dbStatus.ID)
|
||||||
|
suite.True(dbStatus.Federated)
|
||||||
|
suite.True(dbStatus.Boostable)
|
||||||
|
suite.True(dbStatus.Replyable)
|
||||||
|
suite.True(dbStatus.Likeable)
|
||||||
|
|
||||||
|
// account should be in the database now too
|
||||||
|
account, err := suite.db.GetAccountByURI(context.Background(), status.AccountURI)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.NotNil(account)
|
||||||
|
suite.True(account.Discoverable)
|
||||||
|
suite.Equal("https://turnip.farm/users/turniplover6969", account.URI)
|
||||||
|
suite.Equal("I just think they're neat", account.Note)
|
||||||
|
suite.Equal("Turnip Lover 6969", account.DisplayName)
|
||||||
|
suite.Equal("turniplover6969", account.Username)
|
||||||
|
suite.NotNil(account.PublicKey)
|
||||||
|
suite.Nil(account.PrivateKey)
|
||||||
|
|
||||||
|
// we should have an attachment in the database
|
||||||
|
a := >smodel.MediaAttachment{}
|
||||||
|
err = suite.db.GetWhere(context.Background(), []db.Where{{Key: "status_id", Value: status.ID}}, a)
|
||||||
|
suite.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestStatusTestSuite(t *testing.T) {
|
func TestStatusTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(StatusTestSuite))
|
suite.Run(t, new(StatusTestSuite))
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,7 @@ func deriveThumbnail(r io.Reader, contentType string, createBlurhash bool) (*ima
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("error decoding image as %s: %s", contentType, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == nil {
|
if i == nil {
|
||||||
|
@ -151,7 +151,7 @@ func deriveThumbnail(r io.Reader, contentType string, createBlurhash bool) (*ima
|
||||||
tiny := resize.Thumbnail(32, 32, thumb, resize.NearestNeighbor)
|
tiny := resize.Thumbnail(32, 32, thumb, resize.NearestNeighbor)
|
||||||
bh, err := blurhash.Encode(4, 3, tiny)
|
bh, err := blurhash.Encode(4, 3, tiny)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("error creating blurhash: %s", err)
|
||||||
}
|
}
|
||||||
im.blurhash = bh
|
im.blurhash = bh
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ func deriveThumbnail(r io.Reader, contentType string, createBlurhash bool) (*ima
|
||||||
// Quality isn't extremely important for thumbnails, so 75 is "good enough"
|
// Quality isn't extremely important for thumbnails, so 75 is "good enough"
|
||||||
Quality: 75,
|
Quality: 75,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("error encoding thumbnail: %s", err)
|
||||||
}
|
}
|
||||||
im.small = out.Bytes()
|
im.small = out.Bytes()
|
||||||
|
|
||||||
|
|
|
@ -260,6 +260,7 @@ func (p *ProcessingMedia) store(ctx context.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("store: error executing data function: %s", err)
|
return fmt.Errorf("store: error executing data function: %s", err)
|
||||||
}
|
}
|
||||||
|
logrus.Tracef("store: reading %d bytes from data function for media %s", fileSize, p.attachment.URL)
|
||||||
|
|
||||||
// defer closing the reader when we're done with it
|
// defer closing the reader when we're done with it
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -342,6 +343,7 @@ func (p *ProcessingMedia) store(ctx context.Context) error {
|
||||||
return p.postData(ctx)
|
return p.postData(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logrus.Tracef("store: finished storing initial data for attachment %s", p.attachment.URL)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
BIN
testrig/media/giant-turnip-world-record.jpg
Normal file
BIN
testrig/media/giant-turnip-world-record.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 75 KiB |
|
@ -1460,7 +1460,7 @@ type ActivityWithSignature struct {
|
||||||
// A struct of accounts needs to be passed in because the activities will also be bundled along with
|
// A struct of accounts needs to be passed in because the activities will also be bundled along with
|
||||||
// their requesting signatures.
|
// their requesting signatures.
|
||||||
func NewTestActivities(accounts map[string]*gtsmodel.Account) map[string]ActivityWithSignature {
|
func NewTestActivities(accounts map[string]*gtsmodel.Account) map[string]ActivityWithSignature {
|
||||||
dmForZork := newNote(
|
dmForZork := newAPNote(
|
||||||
URLMustParse("http://fossbros-anonymous.io/users/foss_satan/statuses/5424b153-4553-4f30-9358-7b92f7cd42f6"),
|
URLMustParse("http://fossbros-anonymous.io/users/foss_satan/statuses/5424b153-4553-4f30-9358-7b92f7cd42f6"),
|
||||||
URLMustParse("http://fossbros-anonymous.io/@foss_satan/5424b153-4553-4f30-9358-7b92f7cd42f6"),
|
URLMustParse("http://fossbros-anonymous.io/@foss_satan/5424b153-4553-4f30-9358-7b92f7cd42f6"),
|
||||||
time.Now(),
|
time.Now(),
|
||||||
|
@ -1470,15 +1470,17 @@ func NewTestActivities(accounts map[string]*gtsmodel.Account) map[string]Activit
|
||||||
[]*url.URL{URLMustParse("http://localhost:8080/users/the_mighty_zork")},
|
[]*url.URL{URLMustParse("http://localhost:8080/users/the_mighty_zork")},
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
[]vocab.ActivityStreamsMention{})
|
[]vocab.ActivityStreamsMention{},
|
||||||
createDmForZork := wrapNoteInCreate(
|
nil,
|
||||||
|
)
|
||||||
|
createDmForZork := wrapAPNoteInCreate(
|
||||||
URLMustParse("http://fossbros-anonymous.io/users/foss_satan/statuses/5424b153-4553-4f30-9358-7b92f7cd42f6/activity"),
|
URLMustParse("http://fossbros-anonymous.io/users/foss_satan/statuses/5424b153-4553-4f30-9358-7b92f7cd42f6/activity"),
|
||||||
URLMustParse("http://fossbros-anonymous.io/users/foss_satan"),
|
URLMustParse("http://fossbros-anonymous.io/users/foss_satan"),
|
||||||
time.Now(),
|
time.Now(),
|
||||||
dmForZork)
|
dmForZork)
|
||||||
createDmForZorkSig, createDmForZorkDigest, creatDmForZorkDate := GetSignatureForActivity(createDmForZork, accounts["remote_account_1"].PublicKeyURI, accounts["remote_account_1"].PrivateKey, URLMustParse(accounts["local_account_1"].InboxURI))
|
createDmForZorkSig, createDmForZorkDigest, creatDmForZorkDate := GetSignatureForActivity(createDmForZork, accounts["remote_account_1"].PublicKeyURI, accounts["remote_account_1"].PrivateKey, URLMustParse(accounts["local_account_1"].InboxURI))
|
||||||
|
|
||||||
forwardedMessage := newNote(
|
forwardedMessage := newAPNote(
|
||||||
URLMustParse("http://example.org/users/some_user/statuses/afaba698-5740-4e32-a702-af61aa543bc1"),
|
URLMustParse("http://example.org/users/some_user/statuses/afaba698-5740-4e32-a702-af61aa543bc1"),
|
||||||
URLMustParse("http://example.org/@some_user/afaba698-5740-4e32-a702-af61aa543bc1"),
|
URLMustParse("http://example.org/@some_user/afaba698-5740-4e32-a702-af61aa543bc1"),
|
||||||
time.Now(),
|
time.Now(),
|
||||||
|
@ -1488,8 +1490,10 @@ func NewTestActivities(accounts map[string]*gtsmodel.Account) map[string]Activit
|
||||||
[]*url.URL{URLMustParse(pub.PublicActivityPubIRI)},
|
[]*url.URL{URLMustParse(pub.PublicActivityPubIRI)},
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
[]vocab.ActivityStreamsMention{})
|
[]vocab.ActivityStreamsMention{},
|
||||||
createForwardedMessage := wrapNoteInCreate(
|
nil,
|
||||||
|
)
|
||||||
|
createForwardedMessage := wrapAPNoteInCreate(
|
||||||
URLMustParse("http://example.org/users/some_user/statuses/afaba698-5740-4e32-a702-af61aa543bc1/activity"),
|
URLMustParse("http://example.org/users/some_user/statuses/afaba698-5740-4e32-a702-af61aa543bc1/activity"),
|
||||||
URLMustParse("http://example.org/users/some_user"),
|
URLMustParse("http://example.org/users/some_user"),
|
||||||
time.Now(),
|
time.Now(),
|
||||||
|
@ -1520,8 +1524,14 @@ func NewTestFediPeople() map[string]vocab.ActivityStreamsPerson {
|
||||||
}
|
}
|
||||||
newPerson1Pub := &newPerson1Priv.PublicKey
|
newPerson1Pub := &newPerson1Priv.PublicKey
|
||||||
|
|
||||||
|
turnipLover6969Priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
turnipLover6969Pub := &turnipLover6969Priv.PublicKey
|
||||||
|
|
||||||
return map[string]vocab.ActivityStreamsPerson{
|
return map[string]vocab.ActivityStreamsPerson{
|
||||||
"https://unknown-instance.com/users/brand_new_person": newPerson(
|
"https://unknown-instance.com/users/brand_new_person": newAPPerson(
|
||||||
URLMustParse("https://unknown-instance.com/users/brand_new_person"),
|
URLMustParse("https://unknown-instance.com/users/brand_new_person"),
|
||||||
URLMustParse("https://unknown-instance.com/users/brand_new_person/following"),
|
URLMustParse("https://unknown-instance.com/users/brand_new_person/following"),
|
||||||
URLMustParse("https://unknown-instance.com/users/brand_new_person/followers"),
|
URLMustParse("https://unknown-instance.com/users/brand_new_person/followers"),
|
||||||
|
@ -1541,6 +1551,26 @@ func NewTestFediPeople() map[string]vocab.ActivityStreamsPerson {
|
||||||
"image/png",
|
"image/png",
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
"https://turnip.farm/users/turniplover6969": newAPPerson(
|
||||||
|
URLMustParse("https://turnip.farm/users/turniplover6969"),
|
||||||
|
URLMustParse("https://turnip.farm/users/turniplover6969/following"),
|
||||||
|
URLMustParse("https://turnip.farm/users/turniplover6969/followers"),
|
||||||
|
URLMustParse("https://turnip.farm/users/turniplover6969/inbox"),
|
||||||
|
URLMustParse("https://turnip.farm/users/turniplover6969/outbox"),
|
||||||
|
URLMustParse("https://turnip.farm/users/turniplover6969/collections/featured"),
|
||||||
|
"turniplover6969",
|
||||||
|
"Turnip Lover 6969",
|
||||||
|
"I just think they're neat",
|
||||||
|
URLMustParse("https://turnip.farm/@turniplover6969"),
|
||||||
|
true,
|
||||||
|
URLMustParse("https://turnip.farm/users/turniplover6969#main-key"),
|
||||||
|
turnipLover6969Pub,
|
||||||
|
nil,
|
||||||
|
"image/jpeg",
|
||||||
|
nil,
|
||||||
|
"image/png",
|
||||||
|
false,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1552,7 +1582,7 @@ func NewTestFediGroups() map[string]vocab.ActivityStreamsGroup {
|
||||||
newGroup1Pub := &newGroup1Priv.PublicKey
|
newGroup1Pub := &newGroup1Priv.PublicKey
|
||||||
|
|
||||||
return map[string]vocab.ActivityStreamsGroup{
|
return map[string]vocab.ActivityStreamsGroup{
|
||||||
"https://unknown-instance.com/groups/some_group": newGroup(
|
"https://unknown-instance.com/groups/some_group": newAPGroup(
|
||||||
URLMustParse("https://unknown-instance.com/groups/some_group"),
|
URLMustParse("https://unknown-instance.com/groups/some_group"),
|
||||||
URLMustParse("https://unknown-instance.com/groups/some_group/following"),
|
URLMustParse("https://unknown-instance.com/groups/some_group/following"),
|
||||||
URLMustParse("https://unknown-instance.com/groups/some_group/followers"),
|
URLMustParse("https://unknown-instance.com/groups/some_group/followers"),
|
||||||
|
@ -1592,6 +1622,11 @@ func NewTestFediAttachments(relativePath string) map[string]RemoteAttachmentFile
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
massiveFuckingTurnipBytes, err := os.ReadFile(fmt.Sprintf("%s/giant-turnip-world-record.jpg", relativePath))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
return map[string]RemoteAttachmentFile{
|
return map[string]RemoteAttachmentFile{
|
||||||
"https://s3-us-west-2.amazonaws.com/plushcity/media_attachments/files/106/867/380/219/163/828/original/88e8758c5f011439.jpg": {
|
"https://s3-us-west-2.amazonaws.com/plushcity/media_attachments/files/106/867/380/219/163/828/original/88e8758c5f011439.jpg": {
|
||||||
Data: beeBytes,
|
Data: beeBytes,
|
||||||
|
@ -1601,12 +1636,16 @@ func NewTestFediAttachments(relativePath string) map[string]RemoteAttachmentFile
|
||||||
Data: thoughtsOfDogBytes,
|
Data: thoughtsOfDogBytes,
|
||||||
ContentType: "image/jpeg",
|
ContentType: "image/jpeg",
|
||||||
},
|
},
|
||||||
|
"https://turnip.farm/attachments/f17843c7-015e-4251-9b5a-91389c49ee57.jpg": {
|
||||||
|
Data: massiveFuckingTurnipBytes,
|
||||||
|
ContentType: "image/jpeg",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestFediStatuses() map[string]vocab.ActivityStreamsNote {
|
func NewTestFediStatuses() map[string]vocab.ActivityStreamsNote {
|
||||||
return map[string]vocab.ActivityStreamsNote{
|
return map[string]vocab.ActivityStreamsNote{
|
||||||
"https://unknown-instance.com/users/brand_new_person/statuses/01FE4NTHKWW7THT67EF10EB839": newNote(
|
"https://unknown-instance.com/users/brand_new_person/statuses/01FE4NTHKWW7THT67EF10EB839": newAPNote(
|
||||||
URLMustParse("https://unknown-instance.com/users/brand_new_person/statuses/01FE4NTHKWW7THT67EF10EB839"),
|
URLMustParse("https://unknown-instance.com/users/brand_new_person/statuses/01FE4NTHKWW7THT67EF10EB839"),
|
||||||
URLMustParse("https://unknown-instance.com/users/@brand_new_person/01FE4NTHKWW7THT67EF10EB839"),
|
URLMustParse("https://unknown-instance.com/users/@brand_new_person/01FE4NTHKWW7THT67EF10EB839"),
|
||||||
time.Now(),
|
time.Now(),
|
||||||
|
@ -1618,9 +1657,10 @@ func NewTestFediStatuses() map[string]vocab.ActivityStreamsNote {
|
||||||
},
|
},
|
||||||
[]*url.URL{},
|
[]*url.URL{},
|
||||||
false,
|
false,
|
||||||
[]vocab.ActivityStreamsMention{},
|
nil,
|
||||||
|
nil,
|
||||||
),
|
),
|
||||||
"https://unknown-instance.com/users/brand_new_person/statuses/01FE5Y30E3W4P7TRE0R98KAYQV": newNote(
|
"https://unknown-instance.com/users/brand_new_person/statuses/01FE5Y30E3W4P7TRE0R98KAYQV": newAPNote(
|
||||||
URLMustParse("https://unknown-instance.com/users/brand_new_person/statuses/01FE5Y30E3W4P7TRE0R98KAYQV"),
|
URLMustParse("https://unknown-instance.com/users/brand_new_person/statuses/01FE5Y30E3W4P7TRE0R98KAYQV"),
|
||||||
URLMustParse("https://unknown-instance.com/users/@brand_new_person/01FE5Y30E3W4P7TRE0R98KAYQV"),
|
URLMustParse("https://unknown-instance.com/users/@brand_new_person/01FE5Y30E3W4P7TRE0R98KAYQV"),
|
||||||
time.Now(),
|
time.Now(),
|
||||||
|
@ -1633,11 +1673,34 @@ func NewTestFediStatuses() map[string]vocab.ActivityStreamsNote {
|
||||||
[]*url.URL{},
|
[]*url.URL{},
|
||||||
false,
|
false,
|
||||||
[]vocab.ActivityStreamsMention{
|
[]vocab.ActivityStreamsMention{
|
||||||
newMention(
|
newAPMention(
|
||||||
URLMustParse("http://localhost:8080/users/the_mighty_zork"),
|
URLMustParse("http://localhost:8080/users/the_mighty_zork"),
|
||||||
"@the_mighty_zork@localhost:8080",
|
"@the_mighty_zork@localhost:8080",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
"https://turnip.farm/users/turniplover6969/statuses/70c53e54-3146-42d5-a630-83c8b6c7c042": newAPNote(
|
||||||
|
URLMustParse("https://turnip.farm/users/turniplover6969/statuses/70c53e54-3146-42d5-a630-83c8b6c7c042"),
|
||||||
|
URLMustParse("https://turnip.farm/@turniplover6969/70c53e54-3146-42d5-a630-83c8b6c7c042"),
|
||||||
|
time.Now(),
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
URLMustParse("https://turnip.farm/users/turniplover6969"),
|
||||||
|
[]*url.URL{
|
||||||
|
URLMustParse(pub.PublicActivityPubIRI),
|
||||||
|
},
|
||||||
|
[]*url.URL{},
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
[]vocab.ActivityStreamsImage{
|
||||||
|
newAPImage(
|
||||||
|
URLMustParse("https://turnip.farm/attachments/f17843c7-015e-4251-9b5a-91389c49ee57.jpg"),
|
||||||
|
"image/jpeg",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
},
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1799,7 +1862,7 @@ func GetSignatureForDereference(pubKeyID string, privkey crypto.PrivateKey, dest
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPerson(
|
func newAPPerson(
|
||||||
profileIDURI *url.URL,
|
profileIDURI *url.URL,
|
||||||
followingURI *url.URL,
|
followingURI *url.URL,
|
||||||
followersURI *url.URL,
|
followersURI *url.URL,
|
||||||
|
@ -1982,7 +2045,7 @@ func newPerson(
|
||||||
return person
|
return person
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGroup(
|
func newAPGroup(
|
||||||
profileIDURI *url.URL,
|
profileIDURI *url.URL,
|
||||||
followingURI *url.URL,
|
followingURI *url.URL,
|
||||||
followersURI *url.URL,
|
followersURI *url.URL,
|
||||||
|
@ -2165,7 +2228,7 @@ func newGroup(
|
||||||
return group
|
return group
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMention(uri *url.URL, namestring string) vocab.ActivityStreamsMention {
|
func newAPMention(uri *url.URL, namestring string) vocab.ActivityStreamsMention {
|
||||||
mention := streams.NewActivityStreamsMention()
|
mention := streams.NewActivityStreamsMention()
|
||||||
|
|
||||||
hrefProp := streams.NewActivityStreamsHrefProperty()
|
hrefProp := streams.NewActivityStreamsHrefProperty()
|
||||||
|
@ -2179,8 +2242,38 @@ func newMention(uri *url.URL, namestring string) vocab.ActivityStreamsMention {
|
||||||
return mention
|
return mention
|
||||||
}
|
}
|
||||||
|
|
||||||
// newNote returns a new activity streams note for the given parameters
|
func newAPImage(url *url.URL, mediaType string, imageDescription string, blurhash string) vocab.ActivityStreamsImage {
|
||||||
func newNote(
|
image := streams.NewActivityStreamsImage()
|
||||||
|
|
||||||
|
if url != nil {
|
||||||
|
urlProp := streams.NewActivityStreamsUrlProperty()
|
||||||
|
urlProp.AppendIRI(url)
|
||||||
|
image.SetActivityStreamsUrl(urlProp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if mediaType != "" {
|
||||||
|
mediaTypeProp := streams.NewActivityStreamsMediaTypeProperty()
|
||||||
|
mediaTypeProp.Set(mediaType)
|
||||||
|
image.SetActivityStreamsMediaType(mediaTypeProp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if imageDescription != "" {
|
||||||
|
nameProp := streams.NewActivityStreamsNameProperty()
|
||||||
|
nameProp.AppendXMLSchemaString(imageDescription)
|
||||||
|
image.SetActivityStreamsName(nameProp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if blurhash != "" {
|
||||||
|
blurhashProp := streams.NewTootBlurhashProperty()
|
||||||
|
blurhashProp.Set(blurhash)
|
||||||
|
image.SetTootBlurhash(blurhashProp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
|
||||||
|
// newAPNote returns a new activity streams note for the given parameters
|
||||||
|
func newAPNote(
|
||||||
noteID *url.URL,
|
noteID *url.URL,
|
||||||
noteURL *url.URL,
|
noteURL *url.URL,
|
||||||
noteCreatedAt time.Time,
|
noteCreatedAt time.Time,
|
||||||
|
@ -2190,7 +2283,8 @@ func newNote(
|
||||||
noteTo []*url.URL,
|
noteTo []*url.URL,
|
||||||
noteCC []*url.URL,
|
noteCC []*url.URL,
|
||||||
noteSensitive bool,
|
noteSensitive bool,
|
||||||
noteMentions []vocab.ActivityStreamsMention) vocab.ActivityStreamsNote {
|
noteMentions []vocab.ActivityStreamsMention,
|
||||||
|
noteAttachments []vocab.ActivityStreamsImage) vocab.ActivityStreamsNote {
|
||||||
|
|
||||||
// create the note itself
|
// create the note itself
|
||||||
note := streams.NewActivityStreamsNote()
|
note := streams.NewActivityStreamsNote()
|
||||||
|
@ -2255,21 +2349,27 @@ func newNote(
|
||||||
note.SetActivityStreamsCc(cc)
|
note.SetActivityStreamsCc(cc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set note tags
|
|
||||||
tag := streams.NewActivityStreamsTagProperty()
|
|
||||||
|
|
||||||
// mentions
|
// mentions
|
||||||
|
tag := streams.NewActivityStreamsTagProperty()
|
||||||
for _, m := range noteMentions {
|
for _, m := range noteMentions {
|
||||||
tag.AppendActivityStreamsMention(m)
|
tag.AppendActivityStreamsMention(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
note.SetActivityStreamsTag(tag)
|
note.SetActivityStreamsTag(tag)
|
||||||
|
|
||||||
|
// append any attachments as ActivityStreamsImage
|
||||||
|
if noteAttachments != nil {
|
||||||
|
attachmentProperty := streams.NewActivityStreamsAttachmentProperty()
|
||||||
|
for _, a := range noteAttachments {
|
||||||
|
attachmentProperty.AppendActivityStreamsImage(a)
|
||||||
|
}
|
||||||
|
note.SetActivityStreamsAttachment(attachmentProperty)
|
||||||
|
}
|
||||||
|
|
||||||
return note
|
return note
|
||||||
}
|
}
|
||||||
|
|
||||||
// wrapNoteInCreate wraps the given activity streams note in a Create activity streams action
|
// wrapAPNoteInCreate wraps the given activity streams note in a Create activity streams action
|
||||||
func wrapNoteInCreate(createID *url.URL, createActor *url.URL, createPublished time.Time, createNote vocab.ActivityStreamsNote) vocab.ActivityStreamsCreate {
|
func wrapAPNoteInCreate(createID *url.URL, createActor *url.URL, createPublished time.Time, createNote vocab.ActivityStreamsNote) vocab.ActivityStreamsCreate {
|
||||||
// create the.... create
|
// create the.... create
|
||||||
create := streams.NewActivityStreamsCreate()
|
create := streams.NewActivityStreamsCreate()
|
||||||
|
|
||||||
|
|
2
vendor/github.com/superseriousbusiness/exif-terminator/jpeg.go
generated
vendored
2
vendor/github.com/superseriousbusiness/exif-terminator/jpeg.go
generated
vendored
|
@ -23,7 +23,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
jpegstructure "github.com/dsoprea/go-jpeg-image-structure/v2"
|
jpegstructure "github.com/superseriousbusiness/go-jpeg-image-structure/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var markerLen = map[byte]int{
|
var markerLen = map[byte]int{
|
||||||
|
|
47
vendor/github.com/superseriousbusiness/exif-terminator/logger.go
generated
vendored
Normal file
47
vendor/github.com/superseriousbusiness/exif-terminator/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
exif-terminator
|
||||||
|
Copyright (C) 2022 SuperSeriousBusiness admin@gotosocial.org
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package terminator
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
var logger ErrorLogger
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
logger = &defaultErrorLogger{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorLogger denotes a generic error logging function.
|
||||||
|
type ErrorLogger interface {
|
||||||
|
Error(args ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultErrorLogger struct{}
|
||||||
|
|
||||||
|
func (d *defaultErrorLogger) Error(args ...interface{}) {
|
||||||
|
fmt.Println(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetErrorLogger allows a user of the exif-terminator library
|
||||||
|
// to set the logger that will be used for error logging.
|
||||||
|
//
|
||||||
|
// If it is not set, the default error logger will be used, which
|
||||||
|
// just prints errors to stdout.
|
||||||
|
func SetErrorLogger(errorLogger ErrorLogger) {
|
||||||
|
logger = errorLogger
|
||||||
|
}
|
7
vendor/github.com/superseriousbusiness/exif-terminator/terminator.go
generated
vendored
7
vendor/github.com/superseriousbusiness/exif-terminator/terminator.go
generated
vendored
|
@ -25,7 +25,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
jpegstructure "github.com/dsoprea/go-jpeg-image-structure/v2"
|
jpegstructure "github.com/superseriousbusiness/go-jpeg-image-structure/v2"
|
||||||
pngstructure "github.com/dsoprea/go-png-image-structure/v2"
|
pngstructure "github.com/dsoprea/go-png-image-structure/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -109,8 +109,11 @@ func scanAndClose(scanner *bufio.Scanner, writer io.WriteCloser) {
|
||||||
// until the pipeReader starts being read by the caller, which
|
// until the pipeReader starts being read by the caller, which
|
||||||
// is why we do this asynchronously
|
// is why we do this asynchronously
|
||||||
go func() {
|
go func() {
|
||||||
|
defer writer.Close()
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
}
|
}
|
||||||
writer.Close()
|
if scanner.Err() != nil {
|
||||||
|
logger.Error(scanner.Err())
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,8 +232,8 @@ func (js *JpegSplitter) readSegment(data []byte) (count int, err error) {
|
||||||
err = binary.Read(b, binary.BigEndian, &l)
|
err = binary.Read(b, binary.BigEndian, &l)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if l <= 2 {
|
if l < 2 {
|
||||||
log.Panicf("length of size read for non-special marker (%02x) is unexpectedly not more than two.", markerId)
|
log.Panicf("length of size read for non-special marker (%02x) is unexpectedly less than two.", markerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// (l includes the bytes of the length itself.)
|
// (l includes the bytes of the length itself.)
|
6
vendor/modules.txt
vendored
6
vendor/modules.txt
vendored
|
@ -57,7 +57,6 @@ github.com/dsoprea/go-exif/v3/undefined
|
||||||
github.com/dsoprea/go-iptc
|
github.com/dsoprea/go-iptc
|
||||||
# github.com/dsoprea/go-jpeg-image-structure/v2 v2.0.0-20210512043942-b434301c6836
|
# github.com/dsoprea/go-jpeg-image-structure/v2 v2.0.0-20210512043942-b434301c6836
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/dsoprea/go-jpeg-image-structure/v2
|
|
||||||
# github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd
|
# github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/dsoprea/go-logging
|
github.com/dsoprea/go-logging
|
||||||
|
@ -471,9 +470,12 @@ github.com/superseriousbusiness/activity/streams/values/rfc2045
|
||||||
github.com/superseriousbusiness/activity/streams/values/rfc5988
|
github.com/superseriousbusiness/activity/streams/values/rfc5988
|
||||||
github.com/superseriousbusiness/activity/streams/values/string
|
github.com/superseriousbusiness/activity/streams/values/string
|
||||||
github.com/superseriousbusiness/activity/streams/vocab
|
github.com/superseriousbusiness/activity/streams/vocab
|
||||||
# github.com/superseriousbusiness/exif-terminator v0.1.0
|
# github.com/superseriousbusiness/exif-terminator v0.2.0
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
github.com/superseriousbusiness/exif-terminator
|
github.com/superseriousbusiness/exif-terminator
|
||||||
|
# github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe
|
||||||
|
## explicit; go 1.17
|
||||||
|
github.com/superseriousbusiness/go-jpeg-image-structure/v2
|
||||||
# github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB
|
# github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/superseriousbusiness/oauth2/v4
|
github.com/superseriousbusiness/oauth2/v4
|
||||||
|
|
Loading…
Reference in a new issue