diff options
Diffstat (limited to 'internal/federation')
| -rw-r--r-- | internal/federation/dereferencing/account.go | 193 | ||||
| -rw-r--r-- | internal/federation/dereferencing/account_test.go | 200 | ||||
| -rw-r--r-- | internal/federation/dereferencing/dereferencer_test.go | 2 | ||||
| -rw-r--r-- | internal/federation/dereferencing/emoji.go | 58 | ||||
| -rw-r--r-- | internal/federation/dereferencing/status.go | 57 | ||||
| -rw-r--r-- | internal/federation/federatingdb/update.go | 11 | 
6 files changed, 436 insertions, 85 deletions
diff --git a/internal/federation/dereferencing/account.go b/internal/federation/dereferencing/account.go index 6a633a54a..41a8aa8a9 100644 --- a/internal/federation/dereferencing/account.go +++ b/internal/federation/dereferencing/account.go @@ -76,6 +76,11 @@ type GetRemoteAccountParams struct {  	// quickly fetch a remote account from the database or fail, and don't want to cause  	// http requests to go flying around.  	SkipResolve bool +	// PartialAccount can be used if the GetRemoteAccount call results from a federated/ap +	// account update. In this case, we will already have a partial representation of the account, +	// derived from converting the AP representation to a gtsmodel representation. If this field +	// is provided, then GetRemoteAccount will use this as a basis for building the full account. +	PartialAccount *gtsmodel.Account  }  // GetRemoteAccount completely dereferences a remote account, converts it to a GtS model account, @@ -107,8 +112,16 @@ func (d *deref) GetRemoteAccount(ctx context.Context, params GetRemoteAccountPar  	skipResolve := params.SkipResolve  	// this first step checks if we have the -	// account in the database somewhere already +	// account in the database somewhere already, +	// or if we've been provided it as a partial  	switch { +	case params.PartialAccount != nil: +		foundAccount = params.PartialAccount +		if foundAccount.Domain == "" || foundAccount.Domain == config.GetHost() || foundAccount.Domain == config.GetAccountDomain() { +			// this is actually a local account, +			// make sure we don't try to resolve +			skipResolve = true +		}  	case params.RemoteAccountID != nil:  		uri := params.RemoteAccountID  		host := uri.Host @@ -163,7 +176,7 @@ func (d *deref) GetRemoteAccount(ctx context.Context, params GetRemoteAccountPar  		params.RemoteAccountHost = params.RemoteAccountID.Host  		// ... but we still need the username so we can do a finger for the accountDomain -		// check if we had the account stored already and got it earlier +		// check if we got the account earlier  		if foundAccount != nil {  			params.RemoteAccountUsername = foundAccount.Username  		} else { @@ -201,9 +214,10 @@ func (d *deref) GetRemoteAccount(ctx context.Context, params GetRemoteAccountPar  	// to save on remote calls, only webfinger if:  	// - we don't know the remote account ActivityPub ID yet OR  	// - we haven't found the account yet in some other way OR +	// - we were passed a partial account in params OR  	// - we haven't webfingered the account for two days AND the account isn't an instance account  	var fingered time.Time -	if params.RemoteAccountID == nil || foundAccount == nil || (foundAccount.LastWebfingeredAt.Before(time.Now().Add(webfingerInterval)) && !instanceAccount(foundAccount)) { +	if params.RemoteAccountID == nil || foundAccount == nil || params.PartialAccount != nil || (foundAccount.LastWebfingeredAt.Before(time.Now().Add(webfingerInterval)) && !instanceAccount(foundAccount)) {  		accountDomain, params.RemoteAccountID, err = d.fingerRemoteAccount(ctx, params.RequestingUsername, params.RemoteAccountUsername, params.RemoteAccountHost)  		if err != nil {  			err = fmt.Errorf("GetRemoteAccount: error while fingering: %s", err) @@ -263,7 +277,7 @@ func (d *deref) GetRemoteAccount(ctx context.Context, params GetRemoteAccountPar  		foundAccount.LastWebfingeredAt = fingered  		foundAccount.UpdatedAt = time.Now() -		err = d.db.Put(ctx, foundAccount) +		foundAccount, err = d.db.PutAccount(ctx, foundAccount)  		if err != nil {  			err = fmt.Errorf("GetRemoteAccount: error putting new account: %s", err)  			return @@ -273,13 +287,10 @@ func (d *deref) GetRemoteAccount(ctx context.Context, params GetRemoteAccountPar  	}  	// we had the account already, but now we know the account domain, so update it if it's different +	var accountDomainChanged bool  	if !strings.EqualFold(foundAccount.Domain, accountDomain) { +		accountDomainChanged = true  		foundAccount.Domain = accountDomain -		foundAccount, err = d.db.UpdateAccount(ctx, foundAccount) -		if err != nil { -			err = fmt.Errorf("GetRemoteAccount: error updating account: %s", err) -			return -		}  	}  	// if SharedInboxURI is nil, that means we don't know yet if this account has @@ -327,8 +338,7 @@ func (d *deref) GetRemoteAccount(ctx context.Context, params GetRemoteAccountPar  		foundAccount.LastWebfingeredAt = fingered  	} -	if fieldsChanged || fingeredChanged || sharedInboxChanged { -		foundAccount.UpdatedAt = time.Now() +	if accountDomainChanged || sharedInboxChanged || fieldsChanged || fingeredChanged {  		foundAccount, err = d.db.UpdateAccount(ctx, foundAccount)  		if err != nil {  			return nil, fmt.Errorf("GetRemoteAccount: error updating remoteAccount: %s", err) @@ -423,15 +433,20 @@ func (d *deref) populateAccountFields(ctx context.Context, account *gtsmodel.Acc  		return false, fmt.Errorf("populateAccountFields: domain %s is blocked", accountURI.Host)  	} -	t, err := d.transportController.NewTransportForUsername(ctx, requestingUsername) -	if err != nil { -		return false, fmt.Errorf("populateAccountFields: error getting transport for user: %s", err) -	} +	var changed bool  	// fetch the header and avatar -	changed, err := d.fetchRemoteAccountMedia(ctx, account, t, blocking) -	if err != nil { +	if mediaChanged, err := d.fetchRemoteAccountMedia(ctx, account, requestingUsername, blocking); err != nil {  		return false, fmt.Errorf("populateAccountFields: error fetching header/avi for account: %s", err) +	} else if mediaChanged { +		changed = mediaChanged +	} + +	// fetch any emojis used in note, fields, display name, etc +	if emojisChanged, err := d.fetchRemoteAccountEmojis(ctx, account, requestingUsername); err != nil { +		return false, fmt.Errorf("populateAccountFields: error fetching emojis for account: %s", err) +	} else if emojisChanged { +		changed = emojisChanged  	}  	return changed, nil @@ -449,17 +464,11 @@ func (d *deref) populateAccountFields(ctx context.Context, account *gtsmodel.Acc  //  // If blocking is true, then the calls to the media manager made by this function will be blocking:  // in other words, the function won't return until the header and the avatar have been fully processed. -func (d *deref) fetchRemoteAccountMedia(ctx context.Context, targetAccount *gtsmodel.Account, t transport.Transport, blocking bool) (bool, error) { -	changed := false - -	accountURI, err := url.Parse(targetAccount.URI) -	if err != nil { -		return changed, fmt.Errorf("fetchRemoteAccountMedia: couldn't parse account URI %s: %s", targetAccount.URI, err) -	} - -	if blocked, err := d.db.IsDomainBlocked(ctx, accountURI.Host); blocked || err != nil { -		return changed, fmt.Errorf("fetchRemoteAccountMedia: domain %s is blocked", accountURI.Host) -	} +func (d *deref) fetchRemoteAccountMedia(ctx context.Context, targetAccount *gtsmodel.Account, requestingUsername string, blocking bool) (bool, error) { +	var ( +		changed bool +		t       transport.Transport +	)  	if targetAccount.AvatarRemoteURL != "" && (targetAccount.AvatarMediaAttachmentID == "") {  		var processingMedia *media.ProcessingMedia @@ -479,6 +488,14 @@ func (d *deref) fetchRemoteAccountMedia(ctx context.Context, targetAccount *gtsm  				return changed, err  			} +			if t == nil { +				var err error +				t, err = d.transportController.NewTransportForUsername(ctx, requestingUsername) +				if err != nil { +					return false, fmt.Errorf("fetchRemoteAccountMedia: error getting transport for user: %s", err) +				} +			} +  			data := func(innerCtx context.Context) (io.Reader, int, error) {  				return t.DereferenceMedia(innerCtx, avatarIRI)  			} @@ -537,6 +554,14 @@ func (d *deref) fetchRemoteAccountMedia(ctx context.Context, targetAccount *gtsm  				return changed, err  			} +			if t == nil { +				var err error +				t, err = d.transportController.NewTransportForUsername(ctx, requestingUsername) +				if err != nil { +					return false, fmt.Errorf("fetchRemoteAccountMedia: error getting transport for user: %s", err) +				} +			} +  			data := func(innerCtx context.Context) (io.Reader, int, error) {  				return t.DereferenceMedia(innerCtx, headerIRI)  			} @@ -580,6 +605,118 @@ func (d *deref) fetchRemoteAccountMedia(ctx context.Context, targetAccount *gtsm  	return changed, nil  } +func (d *deref) fetchRemoteAccountEmojis(ctx context.Context, targetAccount *gtsmodel.Account, requestingUsername string) (bool, error) { +	maybeEmojis := targetAccount.Emojis +	maybeEmojiIDs := targetAccount.EmojiIDs + +	// It's possible that the account had emoji IDs set on it, but not Emojis +	// themselves, depending on how it was fetched before being passed to us. +	// +	// If we only have IDs, fetch the emojis from the db. We know they're in +	// there or else they wouldn't have IDs. +	if len(maybeEmojiIDs) > len(maybeEmojis) { +		maybeEmojis = []*gtsmodel.Emoji{} +		for _, emojiID := range maybeEmojiIDs { +			maybeEmoji, err := d.db.GetEmojiByID(ctx, emojiID) +			if err != nil { +				return false, err +			} +			maybeEmojis = append(maybeEmojis, maybeEmoji) +		} +	} + +	// For all the maybe emojis we have, we either fetch them from the database +	// (if we haven't already), or dereference them from the remote instance. +	gotEmojis, err := d.populateEmojis(ctx, maybeEmojis, requestingUsername) +	if err != nil { +		return false, err +	} + +	// Extract the ID of each fetched or dereferenced emoji, so we can attach +	// this to the account if necessary. +	gotEmojiIDs := make([]string, 0, len(gotEmojis)) +	for _, e := range gotEmojis { +		gotEmojiIDs = append(gotEmojiIDs, e.ID) +	} + +	var ( +		changed  = false // have the emojis for this account changed? +		maybeLen = len(maybeEmojis) +		gotLen   = len(gotEmojis) +	) + +	// if the length of everything is zero, this is simple: +	// nothing has changed and there's nothing to do +	if maybeLen == 0 && gotLen == 0 { +		return changed, nil +	} + +	// if the *amount* of emojis on the account has changed, then the got emojis +	// are definitely different from the previous ones (if there were any) -- +	// the account has either more or fewer emojis set on it now, so take the +	// discovered emojis as the new correct ones. +	if maybeLen != gotLen { +		changed = true +		targetAccount.Emojis = gotEmojis +		targetAccount.EmojiIDs = gotEmojiIDs +		return changed, nil +	} + +	// if the lengths are the same but not all of the slices are +	// zero, something *might* have changed, so we have to check + +	// 1. did we have emojis before that we don't have now? +	for _, maybeEmoji := range maybeEmojis { +		var stillPresent bool + +		for _, gotEmoji := range gotEmojis { +			if maybeEmoji.URI == gotEmoji.URI { +				// the emoji we maybe had is still present now, +				// so we can stop checking gotEmojis +				stillPresent = true +				break +			} +		} + +		if !stillPresent { +			// at least one maybeEmoji is no longer present in +			// the got emojis, so we can stop checking now +			changed = true +			targetAccount.Emojis = gotEmojis +			targetAccount.EmojiIDs = gotEmojiIDs +			return changed, nil +		} +	} + +	// 2. do we have emojis now that we didn't have before? +	for _, gotEmoji := range gotEmojis { +		var wasPresent bool + +		for _, maybeEmoji := range maybeEmojis { +			// check emoji IDs here as well, because unreferenced +			// maybe emojis we didn't already have would not have +			// had IDs set on them yet +			if gotEmoji.URI == maybeEmoji.URI && gotEmoji.ID == maybeEmoji.ID { +				// this got emoji was present already in the maybeEmoji, +				// so we can stop checking through maybeEmojis +				wasPresent = true +				break +			} +		} + +		if !wasPresent { +			// at least one gotEmojis was not present in +			// the maybeEmojis, so we can stop checking now +			changed = true +			targetAccount.Emojis = gotEmojis +			targetAccount.EmojiIDs = gotEmojiIDs +			return changed, nil +		} +	} + +	return changed, nil +} +  func lockAndLoad(ctx context.Context, lock *sync.Mutex, processing *media.ProcessingMedia, processingMap map[string]*media.ProcessingMedia, accountID string) error {  	// whatever happens, remove the in-process media from the map  	defer func() { diff --git a/internal/federation/dereferencing/account_test.go b/internal/federation/dereferencing/account_test.go index 4f1a83a96..aec612ac8 100644 --- a/internal/federation/dereferencing/account_test.go +++ b/internal/federation/dereferencing/account_test.go @@ -27,6 +27,7 @@ import (  	"github.com/superseriousbusiness/gotosocial/internal/ap"  	"github.com/superseriousbusiness/gotosocial/internal/config"  	"github.com/superseriousbusiness/gotosocial/internal/federation/dereferencing" +	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"  	"github.com/superseriousbusiness/gotosocial/testrig"  ) @@ -195,6 +196,205 @@ func (suite *AccountTestSuite) TestDereferenceLocalAccountWithUnknownUserURI() {  	suite.Nil(fetchedAccount)  } +func (suite *AccountTestSuite) TestDereferenceRemoteAccountWithPartial() { +	fetchingAccount := suite.testAccounts["local_account_1"] + +	remoteAccount := suite.testAccounts["remote_account_1"] +	remoteAccountPartial := >smodel.Account{ +		ID:                    remoteAccount.ID, +		ActorType:             remoteAccount.ActorType, +		Language:              remoteAccount.Language, +		CreatedAt:             remoteAccount.CreatedAt, +		UpdatedAt:             remoteAccount.UpdatedAt, +		Username:              remoteAccount.Username, +		Domain:                remoteAccount.Domain, +		DisplayName:           remoteAccount.DisplayName, +		URI:                   remoteAccount.URI, +		InboxURI:              remoteAccount.URI, +		SharedInboxURI:        remoteAccount.SharedInboxURI, +		PublicKeyURI:          remoteAccount.PublicKeyURI, +		URL:                   remoteAccount.URL, +		FollowingURI:          remoteAccount.FollowingURI, +		FollowersURI:          remoteAccount.FollowersURI, +		OutboxURI:             remoteAccount.OutboxURI, +		FeaturedCollectionURI: remoteAccount.FeaturedCollectionURI, +		Emojis: []*gtsmodel.Emoji{ +			// dereference an emoji we don't have stored yet +			{ +				URI:             "http://fossbros-anonymous.io/emoji/01GD5HCC2YECT012TK8PAGX4D1", +				Shortcode:       "kip_van_den_bos", +				UpdatedAt:       testrig.TimeMustParse("2022-09-13T12:13:12+02:00"), +				ImageRemoteURL:  "http://fossbros-anonymous.io/emoji/kip.gif", +				Disabled:        testrig.FalseBool(), +				VisibleInPicker: testrig.FalseBool(), +				Domain:          "fossbros-anonymous.io", +			}, +		}, +	} + +	fetchedAccount, err := suite.dereferencer.GetRemoteAccount(context.Background(), dereferencing.GetRemoteAccountParams{ +		RequestingUsername:    fetchingAccount.Username, +		RemoteAccountID:       testrig.URLMustParse(remoteAccount.URI), +		RemoteAccountHost:     remoteAccount.Domain, +		RemoteAccountUsername: remoteAccount.Username, +		PartialAccount:        remoteAccountPartial, +		Blocking:              true, +	}) +	suite.NoError(err) +	suite.NotNil(fetchedAccount) +	suite.NotNil(fetchedAccount.EmojiIDs) +	suite.NotNil(fetchedAccount.Emojis) +} + +func (suite *AccountTestSuite) TestDereferenceRemoteAccountWithPartial2() { +	fetchingAccount := suite.testAccounts["local_account_1"] + +	knownEmoji := suite.testEmojis["yell"] + +	remoteAccount := suite.testAccounts["remote_account_1"] +	remoteAccountPartial := >smodel.Account{ +		ID:                    remoteAccount.ID, +		ActorType:             remoteAccount.ActorType, +		Language:              remoteAccount.Language, +		CreatedAt:             remoteAccount.CreatedAt, +		UpdatedAt:             remoteAccount.UpdatedAt, +		Username:              remoteAccount.Username, +		Domain:                remoteAccount.Domain, +		DisplayName:           remoteAccount.DisplayName, +		URI:                   remoteAccount.URI, +		InboxURI:              remoteAccount.URI, +		SharedInboxURI:        remoteAccount.SharedInboxURI, +		PublicKeyURI:          remoteAccount.PublicKeyURI, +		URL:                   remoteAccount.URL, +		FollowingURI:          remoteAccount.FollowingURI, +		FollowersURI:          remoteAccount.FollowersURI, +		OutboxURI:             remoteAccount.OutboxURI, +		FeaturedCollectionURI: remoteAccount.FeaturedCollectionURI, +		Emojis: []*gtsmodel.Emoji{ +			// an emoji we already have +			{ +				URI:             knownEmoji.URI, +				Shortcode:       knownEmoji.Shortcode, +				UpdatedAt:       knownEmoji.CreatedAt, +				ImageRemoteURL:  knownEmoji.ImageRemoteURL, +				Disabled:        knownEmoji.Disabled, +				VisibleInPicker: knownEmoji.VisibleInPicker, +			}, +		}, +	} + +	fetchedAccount, err := suite.dereferencer.GetRemoteAccount(context.Background(), dereferencing.GetRemoteAccountParams{ +		RequestingUsername:    fetchingAccount.Username, +		RemoteAccountID:       testrig.URLMustParse(remoteAccount.URI), +		RemoteAccountHost:     remoteAccount.Domain, +		RemoteAccountUsername: remoteAccount.Username, +		PartialAccount:        remoteAccountPartial, +		Blocking:              true, +	}) +	suite.NoError(err) +	suite.NotNil(fetchedAccount) +	suite.NotNil(fetchedAccount.EmojiIDs) +	suite.NotNil(fetchedAccount.Emojis) +} + +func (suite *AccountTestSuite) TestDereferenceRemoteAccountWithPartial3() { +	fetchingAccount := suite.testAccounts["local_account_1"] + +	knownEmoji := suite.testEmojis["yell"] + +	remoteAccount := suite.testAccounts["remote_account_1"] +	remoteAccountPartial := >smodel.Account{ +		ID:                    remoteAccount.ID, +		ActorType:             remoteAccount.ActorType, +		Language:              remoteAccount.Language, +		CreatedAt:             remoteAccount.CreatedAt, +		UpdatedAt:             remoteAccount.UpdatedAt, +		Username:              remoteAccount.Username, +		Domain:                remoteAccount.Domain, +		DisplayName:           remoteAccount.DisplayName, +		URI:                   remoteAccount.URI, +		InboxURI:              remoteAccount.URI, +		SharedInboxURI:        remoteAccount.SharedInboxURI, +		PublicKeyURI:          remoteAccount.PublicKeyURI, +		URL:                   remoteAccount.URL, +		FollowingURI:          remoteAccount.FollowingURI, +		FollowersURI:          remoteAccount.FollowersURI, +		OutboxURI:             remoteAccount.OutboxURI, +		FeaturedCollectionURI: remoteAccount.FeaturedCollectionURI, +		Emojis: []*gtsmodel.Emoji{ +			// an emoji we already have +			{ +				URI:             knownEmoji.URI, +				Shortcode:       knownEmoji.Shortcode, +				UpdatedAt:       knownEmoji.CreatedAt, +				ImageRemoteURL:  knownEmoji.ImageRemoteURL, +				Disabled:        knownEmoji.Disabled, +				VisibleInPicker: knownEmoji.VisibleInPicker, +			}, +		}, +	} + +	fetchedAccount, err := suite.dereferencer.GetRemoteAccount(context.Background(), dereferencing.GetRemoteAccountParams{ +		RequestingUsername:    fetchingAccount.Username, +		RemoteAccountID:       testrig.URLMustParse(remoteAccount.URI), +		RemoteAccountHost:     remoteAccount.Domain, +		RemoteAccountUsername: remoteAccount.Username, +		PartialAccount:        remoteAccountPartial, +		Blocking:              true, +	}) +	suite.NoError(err) +	suite.NotNil(fetchedAccount) +	suite.NotNil(fetchedAccount.EmojiIDs) +	suite.NotNil(fetchedAccount.Emojis) +	suite.Equal(knownEmoji.URI, fetchedAccount.Emojis[0].URI) + +	remoteAccountPartial2 := >smodel.Account{ +		ID:                    remoteAccount.ID, +		ActorType:             remoteAccount.ActorType, +		Language:              remoteAccount.Language, +		CreatedAt:             remoteAccount.CreatedAt, +		UpdatedAt:             remoteAccount.UpdatedAt, +		Username:              remoteAccount.Username, +		Domain:                remoteAccount.Domain, +		DisplayName:           remoteAccount.DisplayName, +		URI:                   remoteAccount.URI, +		InboxURI:              remoteAccount.URI, +		SharedInboxURI:        remoteAccount.SharedInboxURI, +		PublicKeyURI:          remoteAccount.PublicKeyURI, +		URL:                   remoteAccount.URL, +		FollowingURI:          remoteAccount.FollowingURI, +		FollowersURI:          remoteAccount.FollowersURI, +		OutboxURI:             remoteAccount.OutboxURI, +		FeaturedCollectionURI: remoteAccount.FeaturedCollectionURI, +		Emojis: []*gtsmodel.Emoji{ +			// dereference an emoji we don't have stored yet +			{ +				URI:             "http://fossbros-anonymous.io/emoji/01GD5HCC2YECT012TK8PAGX4D1", +				Shortcode:       "kip_van_den_bos", +				UpdatedAt:       testrig.TimeMustParse("2022-09-13T12:13:12+02:00"), +				ImageRemoteURL:  "http://fossbros-anonymous.io/emoji/kip.gif", +				Disabled:        testrig.FalseBool(), +				VisibleInPicker: testrig.FalseBool(), +				Domain:          "fossbros-anonymous.io", +			}, +		}, +	} + +	fetchedAccount2, err := suite.dereferencer.GetRemoteAccount(context.Background(), dereferencing.GetRemoteAccountParams{ +		RequestingUsername:    fetchingAccount.Username, +		RemoteAccountID:       testrig.URLMustParse(remoteAccount.URI), +		RemoteAccountHost:     remoteAccount.Domain, +		RemoteAccountUsername: remoteAccount.Username, +		PartialAccount:        remoteAccountPartial2, +		Blocking:              true, +	}) +	suite.NoError(err) +	suite.NotNil(fetchedAccount2) +	suite.NotNil(fetchedAccount2.EmojiIDs) +	suite.NotNil(fetchedAccount2.Emojis) +	suite.Equal("http://fossbros-anonymous.io/emoji/01GD5HCC2YECT012TK8PAGX4D1", fetchedAccount2.Emojis[0].URI) +} +  func TestAccountTestSuite(t *testing.T) {  	suite.Run(t, new(AccountTestSuite))  } diff --git a/internal/federation/dereferencing/dereferencer_test.go b/internal/federation/dereferencing/dereferencer_test.go index c0343a6b8..1bf11d668 100644 --- a/internal/federation/dereferencing/dereferencer_test.go +++ b/internal/federation/dereferencing/dereferencer_test.go @@ -41,6 +41,7 @@ type DereferencerStandardTestSuite struct {  	testRemoteServices    map[string]vocab.ActivityStreamsService  	testRemoteAttachments map[string]testrig.RemoteAttachmentFile  	testAccounts          map[string]*gtsmodel.Account +	testEmojis            map[string]*gtsmodel.Emoji  	dereferencer dereferencing.Dereferencer  } @@ -55,6 +56,7 @@ func (suite *DereferencerStandardTestSuite) SetupTest() {  	suite.testRemoteGroups = testrig.NewTestFediGroups()  	suite.testRemoteServices = testrig.NewTestFediServices()  	suite.testRemoteAttachments = testrig.NewTestFediAttachments("../../../testrig/media") +	suite.testEmojis = testrig.NewTestEmojis()  	suite.db = testrig.NewTestDB()  	suite.storage = testrig.NewInMemoryStorage() diff --git a/internal/federation/dereferencing/emoji.go b/internal/federation/dereferencing/emoji.go index 49811b131..87d0bd515 100644 --- a/internal/federation/dereferencing/emoji.go +++ b/internal/federation/dereferencing/emoji.go @@ -24,6 +24,10 @@ import (  	"io"  	"net/url" +	"github.com/superseriousbusiness/gotosocial/internal/db" +	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +	"github.com/superseriousbusiness/gotosocial/internal/id" +	"github.com/superseriousbusiness/gotosocial/internal/log"  	"github.com/superseriousbusiness/gotosocial/internal/media"  ) @@ -49,3 +53,57 @@ func (d *deref) GetRemoteEmoji(ctx context.Context, requestingUsername string, r  	return processingMedia, nil  } + +func (d *deref) populateEmojis(ctx context.Context, rawEmojis []*gtsmodel.Emoji, requestingUsername string) ([]*gtsmodel.Emoji, error) { +	// At this point we should know: +	// * the AP uri of the emoji +	// * the domain of the emoji +	// * the shortcode of the emoji +	// * the remote URL of the image +	// This should be enough to dereference the emoji + +	gotEmojis := make([]*gtsmodel.Emoji, 0, len(rawEmojis)) + +	for _, e := range rawEmojis { +		var gotEmoji *gtsmodel.Emoji +		var err error + +		// check if we've already got this emoji in the db +		if gotEmoji, err = d.db.GetEmojiByURI(ctx, e.URI); err != nil && err != db.ErrNoEntries { +			log.Errorf("populateEmojis: error checking database for emoji %s: %s", e.URI, err) +			continue +		} + +		if gotEmoji == nil { +			// it's new! go get it! +			newEmojiID, err := id.NewRandomULID() +			if err != nil { +				log.Errorf("populateEmojis: error generating id for remote emoji %s: %s", e.URI, err) +				continue +			} + +			processingEmoji, err := d.GetRemoteEmoji(ctx, requestingUsername, e.ImageRemoteURL, e.Shortcode, newEmojiID, e.URI, &media.AdditionalEmojiInfo{ +				Domain:               &e.Domain, +				ImageRemoteURL:       &e.ImageRemoteURL, +				ImageStaticRemoteURL: &e.ImageRemoteURL, +				Disabled:             e.Disabled, +				VisibleInPicker:      e.VisibleInPicker, +			}) + +			if err != nil { +				log.Errorf("populateEmojis: couldn't get remote emoji %s: %s", e.URI, err) +				continue +			} + +			if gotEmoji, err = processingEmoji.LoadEmoji(ctx); err != nil { +				log.Errorf("populateEmojis: couldn't load remote emoji %s: %s", e.URI, err) +				continue +			} +		} + +		// if we get here, we either had the emoji already or we successfully fetched it +		gotEmojis = append(gotEmojis, gotEmoji) +	} + +	return gotEmojis, nil +} diff --git a/internal/federation/dereferencing/status.go b/internal/federation/dereferencing/status.go index 645910d19..bfbc790d8 100644 --- a/internal/federation/dereferencing/status.go +++ b/internal/federation/dereferencing/status.go @@ -406,58 +406,17 @@ func (d *deref) populateStatusAttachments(ctx context.Context, status *gtsmodel.  }  func (d *deref) populateStatusEmojis(ctx context.Context, status *gtsmodel.Status, requestingUsername string) error { -	// At this point we should know: -	// * the AP uri of the emoji -	// * the domain of the emoji -	// * the shortcode of the emoji -	// * the remote URL of the image -	// This should be enough to dereference the emoji - -	gotEmojis := make([]*gtsmodel.Emoji, 0, len(status.Emojis)) -	emojiIDs := make([]string, 0, len(status.Emojis)) - -	for _, e := range status.Emojis { -		var gotEmoji *gtsmodel.Emoji -		var err error - -		// check if we've already got this emoji in the db -		if gotEmoji, err = d.db.GetEmojiByURI(ctx, e.URI); err != nil && err != db.ErrNoEntries { -			log.Errorf("populateStatusEmojis: error checking database for emoji %s: %s", e.URI, err) -			continue -		} - -		if gotEmoji == nil { -			// it's new! go get it! -			newEmojiID, err := id.NewRandomULID() -			if err != nil { -				log.Errorf("populateStatusEmojis: error generating id for remote emoji %s: %s", e.URI, err) -				continue -			} - -			processingEmoji, err := d.GetRemoteEmoji(ctx, requestingUsername, e.ImageRemoteURL, e.Shortcode, newEmojiID, e.URI, &media.AdditionalEmojiInfo{ -				Domain:               &e.Domain, -				ImageRemoteURL:       &e.ImageRemoteURL, -				ImageStaticRemoteURL: &e.ImageRemoteURL, -				Disabled:             e.Disabled, -				VisibleInPicker:      e.VisibleInPicker, -			}) -			if err != nil { -				log.Errorf("populateStatusEmojis: couldn't get remote emoji %s: %s", e.URI, err) -				continue -			} - -			if gotEmoji, err = processingEmoji.LoadEmoji(ctx); err != nil { -				log.Errorf("populateStatusEmojis: couldn't load remote emoji %s: %s", e.URI, err) -				continue -			} -		} +	emojis, err := d.populateEmojis(ctx, status.Emojis, requestingUsername) +	if err != nil { +		return err +	} -		// if we get here, we either had the emoji already or we successfully fetched it -		gotEmojis = append(gotEmojis, gotEmoji) -		emojiIDs = append(emojiIDs, gotEmoji.ID) +	emojiIDs := make([]string, 0, len(emojis)) +	for _, e := range emojis { +		emojiIDs = append(emojiIDs, e.ID)  	} -	status.Emojis = gotEmojis +	status.Emojis = emojis  	status.EmojiIDs = emojiIDs  	return nil  } diff --git a/internal/federation/federatingdb/update.go b/internal/federation/federatingdb/update.go index 599544e34..f3a04cbcc 100644 --- a/internal/federation/federatingdb/update.go +++ b/internal/federation/federatingdb/update.go @@ -121,7 +121,7 @@ func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error {  			return fmt.Errorf("UPDATE: error converting to account: %s", err)  		} -		if updatedAcct.Domain == config.GetHost() { +		if updatedAcct.Domain == config.GetHost() || updatedAcct.Domain == config.GetAccountDomain() {  			// no need to update local accounts  			// in fact, if we do this will break the shit out of things so do NOT  			return nil @@ -136,13 +136,8 @@ func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error {  		updatedAcct.ID = requestingAcct.ID  		updatedAcct.Language = requestingAcct.Language -		// do the update -		updatedAcct, err = f.db.UpdateAccount(ctx, updatedAcct) -		if err != nil { -			return fmt.Errorf("UPDATE: database error inserting updated account: %s", err) -		} - -		// pass to the processor for further processing of eg., avatar/header +		// pass to the processor for further updating of eg., avatar/header, emojis +		// the actual db insert/update will take place a bit later  		f.fedWorker.Queue(messages.FromFederator{  			APObjectType:     ap.ObjectProfile,  			APActivityType:   ap.ActivityUpdate,  | 
