diff --git a/internal/processing/account/delete.go b/internal/processing/account/delete.go index 9dfccd1be..61d21eb81 100644 --- a/internal/processing/account/delete.go +++ b/internal/processing/account/delete.go @@ -18,28 +18,104 @@ package account -import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +import ( + "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/oauth" +) func (p *processor) Delete(account *gtsmodel.Account) error { + l := p.log.WithFields(logrus.Fields{ + "func": "Delete", + "username": account.Username, + }) + + l.Debug("beginning account delete process") + // TODO in this function: // 1. Delete account's application(s), clients, and oauth tokens // 2. Delete account's blocks // 3. Delete account's emoji // 4. Delete account's follow requests // 5. Delete account's follows - // 6. Delete account's follow requests - // 7. Delete account's media attachments - // 8. Delete account's mentions - // 9. Delete account's notifications - // 10. Delete account's polls - // 11. Delete account's statuses - // 12. Delete account's bookmarks - // 13. Delete account's faves - // 14. Delete account's mutes - // 15. Delete account's streams - // 16. Delete account's tags - // 17. Delete account's user - // 18. Delete account's timeline + // 6. Delete account's media attachments + // 7. Delete account's mentions + // 8. Delete account's notifications + // 9. Delete account's polls + // 10. Delete account's statuses + // 11. Delete account's bookmarks + // 12. Delete account's faves + // 13. Delete account's mutes + // 14. Delete account's streams + // 15. Delete account's tags + // 16. Delete account's user + // 17. Delete account's timeline + // 18. Delete account itself + + // 1. Delete account's application(s), clients, and oauth tokens + // we only need to do this step for local account since remote ones won't have any tokens or applications on our server + if account.Domain == "" { + // see if we can get a user for this account + u := >smodel.User{} + if err := p.db.GetWhere([]db.Where{{Key: "account_id", Value: account.ID}}, u); err == nil { + // we got one! select all tokens with the user's ID + tokens := []*oauth.Token{} + if err := p.db.GetWhere([]db.Where{{Key: "user_id", Value: u.ID}}, &tokens); err == nil { + // we have some tokens to delete + for _, t := range tokens { + // delete client(s) associated with this token + if err := p.db.DeleteByID(t.ClientID, &oauth.Client{}); err != nil { + l.Errorf("error deleting oauth client: %s", err) + } + // delete application(s) associated with this token + if err := p.db.DeleteWhere([]db.Where{{Key: "client_id", Value: t.ClientID}}, >smodel.Application{}); err != nil { + l.Errorf("error deleting application: %s", err) + } + // delete the token itself + if err := p.db.DeleteByID(t.ID, t); err != nil { + l.Errorf("error deleting oauth token: %s", err) + } + } + } + } + } + + // 2. Delete account's blocks + // first delete any blocks that this account created + if err := p.db.DeleteWhere([]db.Where{{Key: "account_id", Value: account.ID}}, &[]*gtsmodel.Block{}); err != nil { + l.Errorf("error deleting blocks created by account: %s", err) + } + + // now delete any blocks that target this account + if err := p.db.DeleteWhere([]db.Where{{Key: "target_account_id", Value: account.ID}}, &[]*gtsmodel.Block{}); err != nil { + l.Errorf("error deleting blocks targeting account: %s", err) + } + + // 3. Delete account's emoji + // nothing to do here + + // 4. Delete account's follow requests + // first delete any follow requests that this account created + if err := p.db.DeleteWhere([]db.Where{{Key: "account_id", Value: account.ID}}, &[]*gtsmodel.FollowRequest{}); err != nil { + l.Errorf("error deleting follow requests created by account: %s", err) + } + + // now delete any follow requests that target this account + if err := p.db.DeleteWhere([]db.Where{{Key: "target_account_id", Value: account.ID}}, &[]*gtsmodel.FollowRequest{}); err != nil { + l.Errorf("error deleting follow requests targeting account: %s", err) + } + + // 5. Delete account's follows + // first delete any follows that this account created + if err := p.db.DeleteWhere([]db.Where{{Key: "account_id", Value: account.ID}}, &[]*gtsmodel.Follow{}); err != nil { + l.Errorf("error deleting follows created by account: %s", err) + } + + // now delete any follows that target this account + if err := p.db.DeleteWhere([]db.Where{{Key: "target_account_id", Value: account.ID}}, &[]*gtsmodel.Follow{}); err != nil { + l.Errorf("error deleting follows targeting account: %s", err) + } return nil } diff --git a/internal/visibility/statusvisible.go b/internal/visibility/statusvisible.go index e0b3a8d9e..dc6b74702 100644 --- a/internal/visibility/statusvisible.go +++ b/internal/visibility/statusvisible.go @@ -22,7 +22,7 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount return false, fmt.Errorf("error pulling relevant accounts for status %s: %s", targetStatus.ID, err) } - domainBlocked, err := f.blockedRelevant(relevantAccounts) + domainBlocked, err := f.domainBlockedRelevant(relevantAccounts) if err != nil { l.Debugf("error checking domain block: %s", err) return false, fmt.Errorf("error checking domain block: %s", err) @@ -134,8 +134,8 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount } // status boosts accounts id - if relevantAccounts.BoostedAccount != nil { - if blocked, err := f.db.Blocked(relevantAccounts.BoostedAccount.ID, requestingAccount.ID); err != nil { + if relevantAccounts.BoostedStatusAuthor != nil { + if blocked, err := f.db.Blocked(relevantAccounts.BoostedStatusAuthor.ID, requestingAccount.ID); err != nil { return false, err } else if blocked { l.Trace("a block exists between requesting account and boosted account") @@ -163,6 +163,16 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount } } + // boost mentions accounts + for _, a := range relevantAccounts.BoostedMentionedAccounts { + if blocked, err := f.db.Blocked(a.ID, requestingAccount.ID); err != nil { + return false, err + } else if blocked { + l.Trace("a block exists between requesting account and a boosted mentioned account") + return false, nil + } + } + // if the requesting account is mentioned in the status it should always be visible for _, acct := range relevantAccounts.MentionedAccounts { if acct.ID == requestingAccount.ID { diff --git a/internal/visibility/util.go b/internal/visibility/util.go index dd4470f11..ca5334122 100644 --- a/internal/visibility/util.go +++ b/internal/visibility/util.go @@ -10,6 +10,7 @@ import ( func (f *filter) pullRelevantAccountsFromStatus(targetStatus *gtsmodel.Status) (*relevantAccounts, error) { accounts := &relevantAccounts{ MentionedAccounts: []*gtsmodel.Account{}, + BoostedMentionedAccounts: []*gtsmodel.Account{}, } // get the author account @@ -31,29 +32,6 @@ func (f *filter) pullRelevantAccountsFromStatus(targetStatus *gtsmodel.Status) ( accounts.ReplyToAccount = repliedToAccount } - // get the boosted account from the status and add it to the pile - if targetStatus.BoostOfID != "" { - // retrieve the boosted status first - boostedStatus := >smodel.Status{} - if err := f.db.GetByID(targetStatus.BoostOfID, boostedStatus); err != nil { - return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boostedStatus with id %s: %s", targetStatus.BoostOfID, err) - } - boostedAccount := >smodel.Account{} - if err := f.db.GetByID(boostedStatus.AccountID, boostedAccount); err != nil { - return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boostedAccount with id %s: %s", boostedStatus.AccountID, err) - } - accounts.BoostedAccount = boostedAccount - - // the boosted status might be a reply to another account so we should get that too - if boostedStatus.InReplyToAccountID != "" { - boostedStatusRepliedToAccount := >smodel.Account{} - if err := f.db.GetByID(boostedStatus.InReplyToAccountID, boostedStatusRepliedToAccount); err != nil { - return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boostedStatusRepliedToAccount with id %s: %s", boostedStatus.InReplyToAccountID, err) - } - accounts.BoostedReplyToAccount = boostedStatusRepliedToAccount - } - } - // now get all accounts with IDs that are mentioned in the status for _, mentionID := range targetStatus.Mentions { @@ -69,16 +47,60 @@ func (f *filter) pullRelevantAccountsFromStatus(targetStatus *gtsmodel.Status) ( accounts.MentionedAccounts = append(accounts.MentionedAccounts, mentionedAccount) } + // get the boosted account from the status and add it to the pile + if targetStatus.BoostOfID != "" { + // retrieve the boosted status first + boostedStatus := >smodel.Status{} + if err := f.db.GetByID(targetStatus.BoostOfID, boostedStatus); err != nil { + return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boostedStatus with id %s: %s", targetStatus.BoostOfID, err) + } + boostedAccount := >smodel.Account{} + if err := f.db.GetByID(boostedStatus.AccountID, boostedAccount); err != nil { + return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boostedAccount with id %s: %s", boostedStatus.AccountID, err) + } + accounts.BoostedStatusAuthor = boostedAccount + + // the boosted status might be a reply to another account so we should get that too + if boostedStatus.InReplyToAccountID != "" { + boostedStatusRepliedToAccount := >smodel.Account{} + if err := f.db.GetByID(boostedStatus.InReplyToAccountID, boostedStatusRepliedToAccount); err != nil { + return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boostedStatusRepliedToAccount with id %s: %s", boostedStatus.InReplyToAccountID, err) + } + accounts.BoostedReplyToAccount = boostedStatusRepliedToAccount + } + + // now get all accounts with IDs that are mentioned in the status + for _, mentionID := range boostedStatus.Mentions { + mention := >smodel.Mention{} + if err := f.db.GetByID(mentionID, mention); err != nil { + return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boosted mention with id %s: %s", mentionID, err) + } + + mentionedAccount := >smodel.Account{} + if err := f.db.GetByID(mention.TargetAccountID, mentionedAccount); err != nil { + return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boosted mentioned account: %s", err) + } + accounts.BoostedMentionedAccounts = append(accounts.BoostedMentionedAccounts, mentionedAccount) + } + } + return accounts, nil } // relevantAccounts denotes accounts that are replied to, boosted by, or mentioned in a status. type relevantAccounts struct { - StatusAuthor *gtsmodel.Account - ReplyToAccount *gtsmodel.Account - BoostedAccount *gtsmodel.Account + // Who wrote the status + StatusAuthor *gtsmodel.Account + // Who is the status replying to + ReplyToAccount *gtsmodel.Account + // Which accounts are mentioned (tagged) in the status + MentionedAccounts []*gtsmodel.Account + // Who authed the boosted status + BoostedStatusAuthor *gtsmodel.Account + // If the boosted status replies to another account, who does it reply to? BoostedReplyToAccount *gtsmodel.Account - MentionedAccounts []*gtsmodel.Account + // Who is mentioned (tagged) in the boosted status + BoostedMentionedAccounts []*gtsmodel.Account } // blockedDomain checks whether the given domain is blocked by us or not @@ -99,9 +121,12 @@ func (f *filter) blockedDomain(host string) (bool, error) { return false, err } -// blockedRelevant checks through all relevant accounts attached to a status +// domainBlockedRelevant checks through all relevant accounts attached to a status // to make sure none of them are domain blocked by this instance. -func (f *filter) blockedRelevant(r *relevantAccounts) (bool, error) { +// +// Will return true+nil if there's a block, false+nil if there's no block, or +// an error if something goes wrong. +func (f *filter) domainBlockedRelevant(r *relevantAccounts) (bool, error) { if r.StatusAuthor != nil { b, err := f.blockedDomain(r.StatusAuthor.Domain) if err != nil { @@ -122,8 +147,18 @@ func (f *filter) blockedRelevant(r *relevantAccounts) (bool, error) { } } - if r.BoostedAccount != nil { - b, err := f.blockedDomain(r.BoostedAccount.Domain) + for _, a := range r.MentionedAccounts { + b, err := f.blockedDomain(a.Domain) + if err != nil { + return false, err + } + if b { + return true, nil + } + } + + if r.BoostedStatusAuthor != nil { + b, err := f.blockedDomain(r.BoostedStatusAuthor.Domain) if err != nil { return false, err } @@ -142,7 +177,7 @@ func (f *filter) blockedRelevant(r *relevantAccounts) (bool, error) { } } - for _, a := range r.MentionedAccounts { + for _, a := range r.BoostedMentionedAccounts { b, err := f.blockedDomain(a.Domain) if err != nil { return false, err