owns.go (6451B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package federatingdb 19 20 import ( 21 "context" 22 "errors" 23 "fmt" 24 "net/url" 25 26 "codeberg.org/gruf/go-kv" 27 "github.com/superseriousbusiness/gotosocial/internal/config" 28 "github.com/superseriousbusiness/gotosocial/internal/db" 29 "github.com/superseriousbusiness/gotosocial/internal/gtscontext" 30 "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 31 "github.com/superseriousbusiness/gotosocial/internal/log" 32 "github.com/superseriousbusiness/gotosocial/internal/uris" 33 ) 34 35 // Owns returns true if the IRI belongs to this instance, and if 36 // the database has an entry for the IRI. 37 // The library makes this call only after acquiring a lock first. 38 func (f *federatingDB) Owns(ctx context.Context, id *url.URL) (bool, error) { 39 l := log.WithContext(ctx). 40 WithFields(kv.Fields{ 41 {"id", id}, 42 }...) 43 l.Debug("entering Owns") 44 45 // if the id host isn't this instance host, we don't own this IRI 46 if host := config.GetHost(); id.Host != host { 47 l.Tracef("we DO NOT own activity because the host is %s not %s", id.Host, host) 48 return false, nil 49 } 50 51 // todo: refactor the below; make sure we use 52 // proper db functions for everything, and 53 // preferably clean up by calling subfuncs 54 // (like we now do for ownsLike). 55 56 // apparently it belongs to this host, so what *is* it? 57 // check if it's a status, eg /users/example_username/statuses/SOME_UUID_OF_A_STATUS 58 if uris.IsStatusesPath(id) { 59 _, uid, err := uris.ParseStatusesPath(id) 60 if err != nil { 61 return false, fmt.Errorf("error parsing statuses path for url %s: %s", id.String(), err) 62 } 63 status, err := f.state.DB.GetStatusByURI(ctx, uid) 64 if err != nil { 65 if err == db.ErrNoEntries { 66 // there are no entries for this status 67 return false, nil 68 } 69 // an actual error happened 70 return false, fmt.Errorf("database error fetching status with id %s: %s", uid, err) 71 } 72 return *status.Local, nil 73 } 74 75 if uris.IsUserPath(id) { 76 username, err := uris.ParseUserPath(id) 77 if err != nil { 78 return false, fmt.Errorf("error parsing statuses path for url %s: %s", id.String(), err) 79 } 80 if _, err := f.state.DB.GetAccountByUsernameDomain(ctx, username, ""); err != nil { 81 if err == db.ErrNoEntries { 82 // there are no entries for this username 83 return false, nil 84 } 85 // an actual error happened 86 return false, fmt.Errorf("database error fetching account with username %s: %s", username, err) 87 } 88 l.Debugf("we own url %s", id.String()) 89 return true, nil 90 } 91 92 if uris.IsFollowersPath(id) { 93 username, err := uris.ParseFollowersPath(id) 94 if err != nil { 95 return false, fmt.Errorf("error parsing statuses path for url %s: %s", id.String(), err) 96 } 97 if _, err := f.state.DB.GetAccountByUsernameDomain(ctx, username, ""); err != nil { 98 if err == db.ErrNoEntries { 99 // there are no entries for this username 100 return false, nil 101 } 102 // an actual error happened 103 return false, fmt.Errorf("database error fetching account with username %s: %s", username, err) 104 } 105 l.Debugf("we own url %s", id.String()) 106 return true, nil 107 } 108 109 if uris.IsFollowingPath(id) { 110 username, err := uris.ParseFollowingPath(id) 111 if err != nil { 112 return false, fmt.Errorf("error parsing statuses path for url %s: %s", id.String(), err) 113 } 114 if _, err := f.state.DB.GetAccountByUsernameDomain(ctx, username, ""); err != nil { 115 if err == db.ErrNoEntries { 116 // there are no entries for this username 117 return false, nil 118 } 119 // an actual error happened 120 return false, fmt.Errorf("database error fetching account with username %s: %s", username, err) 121 } 122 l.Debugf("we own url %s", id.String()) 123 return true, nil 124 } 125 126 if uris.IsLikePath(id) { 127 return f.ownsLike(ctx, id) 128 } 129 130 if uris.IsBlockPath(id) { 131 username, blockID, err := uris.ParseBlockPath(id) 132 if err != nil { 133 return false, fmt.Errorf("error parsing block path for url %s: %s", id.String(), err) 134 } 135 if _, err := f.state.DB.GetAccountByUsernameDomain(ctx, username, ""); err != nil { 136 if err == db.ErrNoEntries { 137 // there are no entries for this username 138 return false, nil 139 } 140 // an actual error happened 141 return false, fmt.Errorf("database error fetching account with username %s: %s", username, err) 142 } 143 if err := f.state.DB.GetByID(ctx, blockID, >smodel.Block{}); err != nil { 144 if err == db.ErrNoEntries { 145 // there are no entries 146 return false, nil 147 } 148 // an actual error happened 149 return false, fmt.Errorf("database error fetching block with id %s: %s", blockID, err) 150 } 151 l.Debugf("we own url %s", id.String()) 152 return true, nil 153 } 154 155 return false, fmt.Errorf("could not match activityID: %s", id.String()) 156 } 157 158 func (f *federatingDB) ownsLike(ctx context.Context, uri *url.URL) (bool, error) { 159 username, id, err := uris.ParseLikedPath(uri) 160 if err != nil { 161 return false, fmt.Errorf("error parsing Like path for url %s: %w", uri.String(), err) 162 } 163 164 // We're only checking for existence, 165 // so use barebones context. 166 bbCtx := gtscontext.SetBarebones(ctx) 167 168 if _, err := f.state.DB.GetAccountByUsernameDomain(bbCtx, username, ""); err != nil { 169 if errors.Is(err, db.ErrNoEntries) { 170 // No entries for this acct, 171 // we don't own this item. 172 return false, nil 173 } 174 175 // Actual error. 176 return false, fmt.Errorf("database error fetching account with username %s: %w", username, err) 177 } 178 179 if _, err := f.state.DB.GetStatusFaveByID(bbCtx, id); err != nil { 180 if errors.Is(err, db.ErrNoEntries) { 181 // No entries for this ID, 182 // we don't own this item. 183 return false, nil 184 } 185 186 // Actual error. 187 return false, fmt.Errorf("database error fetching status fave with id %s: %w", id, err) 188 } 189 190 log.Tracef(ctx, "we own Like %s", uri.String()) 191 return true, nil 192 }