diff options
Diffstat (limited to 'internal/visibility')
-rw-r--r-- | internal/visibility/filter.go | 18 | ||||
-rw-r--r-- | internal/visibility/relevantaccounts.go | 229 | ||||
-rw-r--r-- | internal/visibility/statushometimelineable.go | 43 | ||||
-rw-r--r-- | internal/visibility/statuspublictimelineable.go | 18 | ||||
-rw-r--r-- | internal/visibility/statusvisible.go | 67 | ||||
-rw-r--r-- | internal/visibility/util.go | 191 |
6 files changed, 349 insertions, 217 deletions
diff --git a/internal/visibility/filter.go b/internal/visibility/filter.go index 181eb8ee7..2c43fa4ee 100644 --- a/internal/visibility/filter.go +++ b/internal/visibility/filter.go @@ -1,3 +1,21 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors 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 visibility import ( diff --git a/internal/visibility/relevantaccounts.go b/internal/visibility/relevantaccounts.go new file mode 100644 index 000000000..5957d3111 --- /dev/null +++ b/internal/visibility/relevantaccounts.go @@ -0,0 +1,229 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors 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 visibility + +import ( + "errors" + "fmt" + + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +) + +// relevantAccounts denotes accounts that are replied to, boosted by, or mentioned in a status. +type relevantAccounts struct { + // Who wrote the status + Account *gtsmodel.Account + // Who is the status replying to + InReplyToAccount *gtsmodel.Account + // Which accounts are mentioned (tagged) in the status + MentionedAccounts []*gtsmodel.Account + // Who authed the boosted status + BoostedAccount *gtsmodel.Account + // If the boosted status replies to another account, who does it reply to? + BoostedInReplyToAccount *gtsmodel.Account + // Who is mentioned (tagged) in the boosted status + BoostedMentionedAccounts []*gtsmodel.Account +} + +func (f *filter) relevantAccounts(status *gtsmodel.Status, getBoosted bool) (*relevantAccounts, error) { + relAccts := &relevantAccounts{ + MentionedAccounts: []*gtsmodel.Account{}, + BoostedMentionedAccounts: []*gtsmodel.Account{}, + } + + /* + Here's what we need to try and extract from the status: + + // 1. Who wrote the status + Account *gtsmodel.Account + + // 2. Who is the status replying to + InReplyToAccount *gtsmodel.Account + + // 3. Which accounts are mentioned (tagged) in the status + MentionedAccounts []*gtsmodel.Account + + if getBoosted: + // 4. Who wrote the boosted status + BoostedAccount *gtsmodel.Account + + // 5. If the boosted status replies to another account, who does it reply to? + BoostedInReplyToAccount *gtsmodel.Account + + // 6. Who is mentioned (tagged) in the boosted status + BoostedMentionedAccounts []*gtsmodel.Account + */ + + // 1. Account. + // Account might be set on the status already + if status.Account != nil { + // it was set + relAccts.Account = status.Account + } else { + // it wasn't set, so get it from the db + account, err := f.db.GetAccountByID(status.AccountID) + if err != nil { + return nil, fmt.Errorf("relevantAccounts: error getting account with id %s: %s", status.AccountID, err) + } + // set it on the status in case we need it further along + status.Account = account + // set it on relevant accounts + relAccts.Account = account + } + + // 2. InReplyToAccount + // only get this if InReplyToAccountID is set + if status.InReplyToAccountID != "" { + // InReplyToAccount might be set on the status already + if status.InReplyToAccount != nil { + // it was set + relAccts.InReplyToAccount = status.InReplyToAccount + } else { + // it wasn't set, so get it from the db + inReplyToAccount, err := f.db.GetAccountByID(status.InReplyToAccountID) + if err != nil { + return nil, fmt.Errorf("relevantAccounts: error getting inReplyToAccount with id %s: %s", status.InReplyToAccountID, err) + } + // set it on the status in case we need it further along + status.InReplyToAccount = inReplyToAccount + // set it on relevant accounts + relAccts.InReplyToAccount = inReplyToAccount + } + } + + // 3. MentionedAccounts + // First check if status.Mentions is populated with all mentions that correspond to status.MentionIDs + for _, mID := range status.MentionIDs { + if mID == "" { + continue + } + if !idIn(mID, status.Mentions) { + // mention with ID isn't in status.Mentions + mention, err := f.db.GetMention(mID) + if err != nil { + return nil, fmt.Errorf("relevantAccounts: error getting mention with id %s: %s", mID, err) + } + if mention == nil { + return nil, fmt.Errorf("relevantAccounts: mention with id %s was nil", mID) + } + status.Mentions = append(status.Mentions, mention) + } + } + // now filter mentions to make sure we only have mentions with a corresponding ID + nm := []*gtsmodel.Mention{} + for _, m := range status.Mentions { + if m == nil { + continue + } + if mentionIn(m, status.MentionIDs) { + nm = append(nm, m) + } + } + status.Mentions = nm + + if len(status.Mentions) != len(status.MentionIDs) { + return nil, errors.New("relevantAccounts: mentions length did not correspond with mentionIDs length") + } + + // if getBoosted is set, we should check the same properties on the boosted account as well + if getBoosted { + // 4, 5, 6. Boosted status items + // get the boosted status if it's not set on the status already + if status.BoostOfID != "" && status.BoostOf == nil { + boostedStatus, err := f.db.GetStatusByID(status.BoostOfID) + if err != nil { + return nil, fmt.Errorf("relevantAccounts: error getting boosted status with id %s: %s", status.BoostOfID, err) + } + status.BoostOf = boostedStatus + } + + if status.BoostOf != nil { + // return relevant accounts for the boosted status + boostedRelAccts, err := f.relevantAccounts(status.BoostOf, false) // false because we don't want to recurse + if err != nil { + return nil, fmt.Errorf("relevantAccounts: error getting relevant accounts of boosted status %s: %s", status.BoostOf.ID, err) + } + relAccts.BoostedAccount = boostedRelAccts.Account + relAccts.BoostedInReplyToAccount = boostedRelAccts.InReplyToAccount + relAccts.BoostedMentionedAccounts = boostedRelAccts.MentionedAccounts + } + } + + return relAccts, nil +} + +// 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) domainBlockedRelevant(r *relevantAccounts) (bool, error) { + domains := []string{} + + if r.Account != nil { + domains = append(domains, r.Account.Domain) + } + + if r.InReplyToAccount != nil { + domains = append(domains, r.InReplyToAccount.Domain) + } + + for _, a := range r.MentionedAccounts { + if a != nil { + domains = append(domains, a.Domain) + } + } + + if r.BoostedAccount != nil { + domains = append(domains, r.BoostedAccount.Domain) + } + + if r.BoostedInReplyToAccount != nil { + domains = append(domains, r.BoostedInReplyToAccount.Domain) + } + + for _, a := range r.BoostedMentionedAccounts { + if a != nil { + domains = append(domains, a.Domain) + } + } + + return f.db.AreDomainsBlocked(domains) +} + +func idIn(id string, mentions []*gtsmodel.Mention) bool { + for _, m := range mentions { + if m == nil { + continue + } + if m.ID == id { + return true + } + } + return false +} + +func mentionIn(mention *gtsmodel.Mention, ids []string) bool { + if mention == nil { + return false + } + for _, i := range ids { + if mention.ID == i { + return true + } + } + return false +} diff --git a/internal/visibility/statushometimelineable.go b/internal/visibility/statushometimelineable.go index bc5f7bcb8..a3ca62fb3 100644 --- a/internal/visibility/statushometimelineable.go +++ b/internal/visibility/statushometimelineable.go @@ -1,3 +1,21 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors 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 visibility import ( @@ -28,6 +46,13 @@ func (f *filter) StatusHometimelineable(targetStatus *gtsmodel.Status, timelineO return false, nil } + for _, m := range targetStatus.Mentions { + if m.TargetAccountID == timelineOwnerAccount.ID { + // if we're mentioned we should be able to see the post + return true, nil + } + } + // Don't timeline a status whose parent hasn't been dereferenced yet or can't be dereferenced. // If we have the reply to URI but don't have an ID for the replied-to account or the replied-to status in our database, we haven't dereferenced it yet. if targetStatus.InReplyToURI != "" && (targetStatus.InReplyToID == "" || targetStatus.InReplyToAccountID == "") { @@ -37,21 +62,21 @@ func (f *filter) StatusHometimelineable(targetStatus *gtsmodel.Status, timelineO // if a status replies to an ID we know in the database, we need to make sure we also follow the replied-to status owner account if targetStatus.InReplyToID != "" { // pin the reply to status on to this status if it hasn't been done already - if targetStatus.GTSReplyToStatus == nil { - rs := >smodel.Status{} - if err := f.db.GetByID(targetStatus.InReplyToID, rs); err != nil { + if targetStatus.InReplyTo == nil { + rs, err := f.db.GetStatusByID(targetStatus.InReplyToID) + if err != nil { return false, fmt.Errorf("StatusHometimelineable: error getting replied to status with id %s: %s", targetStatus.InReplyToID, err) } - targetStatus.GTSReplyToStatus = rs + targetStatus.InReplyTo = rs } // pin the reply to account on to this status if it hasn't been done already - if targetStatus.GTSReplyToAccount == nil { - ra := >smodel.Account{} - if err := f.db.GetByID(targetStatus.InReplyToAccountID, ra); err != nil { + if targetStatus.InReplyToAccount == nil { + ra, err := f.db.GetAccountByID(targetStatus.InReplyToAccountID) + if err != nil { return false, fmt.Errorf("StatusHometimelineable: error getting replied to account with id %s: %s", targetStatus.InReplyToAccountID, err) } - targetStatus.GTSReplyToAccount = ra + targetStatus.InReplyToAccount = ra } // if it's a reply to the timelineOwnerAccount, we don't need to check if the timelineOwnerAccount follows itself, just return true, they can see it @@ -60,7 +85,7 @@ func (f *filter) StatusHometimelineable(targetStatus *gtsmodel.Status, timelineO } // the replied-to account != timelineOwnerAccount, so make sure the timelineOwnerAccount follows the replied-to account - follows, err := f.db.Follows(timelineOwnerAccount, targetStatus.GTSReplyToAccount) + follows, err := f.db.IsFollowing(timelineOwnerAccount, targetStatus.InReplyToAccount) if err != nil { return false, fmt.Errorf("StatusHometimelineable: error checking follow from account %s to account %s: %s", timelineOwnerAccount.ID, targetStatus.InReplyToAccountID, err) } diff --git a/internal/visibility/statuspublictimelineable.go b/internal/visibility/statuspublictimelineable.go index d7f68faee..f07e06aae 100644 --- a/internal/visibility/statuspublictimelineable.go +++ b/internal/visibility/statuspublictimelineable.go @@ -1,3 +1,21 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors 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 visibility import ( diff --git a/internal/visibility/statusvisible.go b/internal/visibility/statusvisible.go index dc6b74702..15e545881 100644 --- a/internal/visibility/statusvisible.go +++ b/internal/visibility/statusvisible.go @@ -1,3 +1,21 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors 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 visibility import ( @@ -16,10 +34,11 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount "statusID": targetStatus.ID, }) - relevantAccounts, err := f.pullRelevantAccountsFromStatus(targetStatus) + getBoosted := true + relevantAccounts, err := f.relevantAccounts(targetStatus, getBoosted) if err != nil { l.Debugf("error pulling relevant accounts for status %s: %s", targetStatus.ID, err) - return false, fmt.Errorf("error pulling relevant accounts for status %s: %s", targetStatus.ID, err) + return false, fmt.Errorf("StatusVisible: error pulling relevant accounts for status %s: %s", targetStatus.ID, err) } domainBlocked, err := f.domainBlockedRelevant(relevantAccounts) @@ -32,7 +51,12 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount return false, nil } - targetAccount := relevantAccounts.StatusAuthor + targetAccount := relevantAccounts.Account + if targetAccount == nil { + l.Trace("target account is not set") + return false, nil + } + // if target account is suspended then don't show the status if !targetAccount.SuspendedAt.IsZero() { l.Trace("target account suspended at is not zero") @@ -45,7 +69,7 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount targetUser := >smodel.User{} if err := f.db.GetWhere([]db.Where{{Key: "account_id", Value: targetAccount.ID}}, targetUser); err != nil { l.Debug("target user could not be selected") - if _, ok := err.(db.ErrNoEntries); ok { + if err == db.ErrNoEntries { return false, nil } return false, fmt.Errorf("StatusVisible: db error selecting user for local target account %s: %s", targetAccount.ID, err) @@ -76,7 +100,7 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount if err := f.db.GetWhere([]db.Where{{Key: "account_id", Value: requestingAccount.ID}}, requestingUser); err != nil { // if the requesting account is local but doesn't have a corresponding user in the db this is a problem l.Debug("requesting user could not be selected") - if _, ok := err.(db.ErrNoEntries); ok { + if err == db.ErrNoEntries { return false, nil } return false, fmt.Errorf("StatusVisible: db error selecting user for local requesting account %s: %s", requestingAccount.ID, err) @@ -102,7 +126,7 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount // At this point we have a populated targetAccount, targetStatus, and requestingAccount, so we can check for blocks and whathaveyou // First check if a block exists directly between the target account (which authored the status) and the requesting account. - if blocked, err := f.db.Blocked(targetAccount.ID, requestingAccount.ID); err != nil { + if blocked, err := f.db.IsBlocked(targetAccount.ID, requestingAccount.ID, true); err != nil { l.Debugf("something went wrong figuring out if the accounts have a block: %s", err) return false, err } else if blocked { @@ -112,8 +136,8 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount } // status replies to account id - if relevantAccounts.ReplyToAccount != nil && relevantAccounts.ReplyToAccount.ID != requestingAccount.ID { - if blocked, err := f.db.Blocked(relevantAccounts.ReplyToAccount.ID, requestingAccount.ID); err != nil { + if relevantAccounts.InReplyToAccount != nil && relevantAccounts.InReplyToAccount.ID != requestingAccount.ID { + if blocked, err := f.db.IsBlocked(relevantAccounts.InReplyToAccount.ID, requestingAccount.ID, true); err != nil { return false, err } else if blocked { l.Trace("a block exists between requesting account and reply to account") @@ -122,7 +146,7 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount // check reply to ID if targetStatus.InReplyToID != "" && (targetStatus.Visibility == gtsmodel.VisibilityFollowersOnly || targetStatus.Visibility == gtsmodel.VisibilityDirect) { - followsRepliedAccount, err := f.db.Follows(requestingAccount, relevantAccounts.ReplyToAccount) + followsRepliedAccount, err := f.db.IsFollowing(requestingAccount, relevantAccounts.InReplyToAccount) if err != nil { return false, err } @@ -134,8 +158,8 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount } // status boosts accounts id - if relevantAccounts.BoostedStatusAuthor != nil { - if blocked, err := f.db.Blocked(relevantAccounts.BoostedStatusAuthor.ID, requestingAccount.ID); err != nil { + if relevantAccounts.BoostedAccount != nil { + if blocked, err := f.db.IsBlocked(relevantAccounts.BoostedAccount.ID, requestingAccount.ID, true); err != nil { return false, err } else if blocked { l.Trace("a block exists between requesting account and boosted account") @@ -144,8 +168,8 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount } // status boosts a reply to account id - if relevantAccounts.BoostedReplyToAccount != nil { - if blocked, err := f.db.Blocked(relevantAccounts.BoostedReplyToAccount.ID, requestingAccount.ID); err != nil { + if relevantAccounts.BoostedInReplyToAccount != nil { + if blocked, err := f.db.IsBlocked(relevantAccounts.BoostedInReplyToAccount.ID, requestingAccount.ID, true); err != nil { return false, err } else if blocked { l.Trace("a block exists between requesting account and boosted reply to account") @@ -155,7 +179,10 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount // status mentions accounts for _, a := range relevantAccounts.MentionedAccounts { - if blocked, err := f.db.Blocked(a.ID, requestingAccount.ID); err != nil { + if a == nil { + continue + } + if blocked, err := f.db.IsBlocked(a.ID, requestingAccount.ID, true); err != nil { return false, err } else if blocked { l.Trace("a block exists between requesting account and a mentioned account") @@ -165,7 +192,10 @@ 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 { + if a == nil { + continue + } + if blocked, err := f.db.IsBlocked(a.ID, requestingAccount.ID, true); err != nil { return false, err } else if blocked { l.Trace("a block exists between requesting account and a boosted mentioned account") @@ -175,6 +205,9 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount // if the requesting account is mentioned in the status it should always be visible for _, acct := range relevantAccounts.MentionedAccounts { + if acct == nil { + continue + } if acct.ID == requestingAccount.ID { return true, nil // yep it's mentioned! } @@ -188,7 +221,7 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount return true, nil case gtsmodel.VisibilityFollowersOnly: // check one-way follow - follows, err := f.db.Follows(requestingAccount, targetAccount) + follows, err := f.db.IsFollowing(requestingAccount, targetAccount) if err != nil { return false, err } @@ -199,7 +232,7 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount return true, nil case gtsmodel.VisibilityMutualsOnly: // check mutual follow - mutuals, err := f.db.Mutuals(requestingAccount, targetAccount) + mutuals, err := f.db.IsMutualFollowing(requestingAccount, targetAccount) if err != nil { return false, err } diff --git a/internal/visibility/util.go b/internal/visibility/util.go deleted file mode 100644 index a12dd555f..000000000 --- a/internal/visibility/util.go +++ /dev/null @@ -1,191 +0,0 @@ -package visibility - -import ( - "fmt" - - "github.com/superseriousbusiness/gotosocial/internal/db" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" -) - -func (f *filter) pullRelevantAccountsFromStatus(targetStatus *gtsmodel.Status) (*relevantAccounts, error) { - accounts := &relevantAccounts{ - MentionedAccounts: []*gtsmodel.Account{}, - BoostedMentionedAccounts: []*gtsmodel.Account{}, - } - - // get the author account - if targetStatus.GTSAuthorAccount == nil { - statusAuthor := >smodel.Account{} - if err := f.db.GetByID(targetStatus.AccountID, statusAuthor); err != nil { - return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting statusAuthor with id %s: %s", targetStatus.AccountID, err) - } - targetStatus.GTSAuthorAccount = statusAuthor - } - accounts.StatusAuthor = targetStatus.GTSAuthorAccount - - // get the replied to account from the status and add it to the pile - if targetStatus.InReplyToAccountID != "" { - repliedToAccount := >smodel.Account{} - if err := f.db.GetByID(targetStatus.InReplyToAccountID, repliedToAccount); err != nil { - return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting repliedToAcount with id %s: %s", targetStatus.InReplyToAccountID, err) - } - accounts.ReplyToAccount = repliedToAccount - } - - // now get all accounts with IDs that are mentioned in the status - for _, mentionID := range targetStatus.Mentions { - - mention := >smodel.Mention{} - if err := f.db.GetByID(mentionID, mention); err != nil { - return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting 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 mentioned account: %s", err) - } - 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 { - // 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 - // Who is mentioned (tagged) in the boosted status - BoostedMentionedAccounts []*gtsmodel.Account -} - -// blockedDomain checks whether the given domain is blocked by us or not -func (f *filter) blockedDomain(host string) (bool, error) { - b := >smodel.DomainBlock{} - err := f.db.GetWhere([]db.Where{{Key: "domain", Value: host, CaseInsensitive: true}}, b) - if err == nil { - // block exists - return true, nil - } - - if _, ok := err.(db.ErrNoEntries); ok { - // there are no entries so there's no block - return false, nil - } - - // there's an actual error - return false, err -} - -// domainBlockedRelevant checks through all relevant accounts attached to a status -// to make sure none of them are domain blocked by this instance. -// -// 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 { - return false, err - } - if b { - return true, nil - } - } - - if r.ReplyToAccount != nil { - b, err := f.blockedDomain(r.ReplyToAccount.Domain) - if err != nil { - return false, err - } - if b { - return true, nil - } - } - - 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 - } - if b { - return true, nil - } - } - - if r.BoostedReplyToAccount != nil { - b, err := f.blockedDomain(r.BoostedReplyToAccount.Domain) - if err != nil { - return false, err - } - if b { - return true, nil - } - } - - for _, a := range r.BoostedMentionedAccounts { - b, err := f.blockedDomain(a.Domain) - if err != nil { - return false, err - } - if b { - return true, nil - } - } - - return false, nil -} |