| 
									
										
										
										
											2023-03-12 16:00:57 +01:00
										 |  |  | // GoToSocial | 
					
						
							|  |  |  | // Copyright (C) GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | // SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // 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/>. | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | package federatingdb | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2023-11-04 20:21:20 +00:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	"codeberg.org/gruf/go-logger/v2/level" | 
					
						
							| 
									
										
										
										
											2021-11-13 17:29:43 +01:00
										 |  |  | 	"github.com/superseriousbusiness/activity/streams/vocab" | 
					
						
							| 
									
										
										
										
											2021-08-10 13:32:39 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/ap" | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | 
					
						
							| 
									
										
										
										
											2023-11-04 20:21:20 +00:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/db" | 
					
						
							| 
									
										
										
										
											2023-07-12 13:20:15 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/log" | 
					
						
							| 
									
										
										
										
											2021-08-31 15:59:12 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/messages" | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Update sets an existing entry to the database based on the value's | 
					
						
							|  |  |  | // id. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Note that Activity values received from federated peers may also be | 
					
						
							|  |  |  | // updated in the database this way if the Federating Protocol is | 
					
						
							|  |  |  | // enabled. The client may freely decide to store only the id instead of | 
					
						
							|  |  |  | // the entire value. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The library makes this call only after acquiring a lock first. | 
					
						
							|  |  |  | func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error { | 
					
						
							| 
									
										
										
										
											2023-07-12 13:20:15 +02:00
										 |  |  | 	l := log.WithContext(ctx) | 
					
						
							| 
									
										
										
										
											2021-10-04 15:24:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	if log.Level() >= level.DEBUG { | 
					
						
							| 
									
										
										
										
											2021-10-04 15:24:19 +02:00
										 |  |  | 		i, err := marshalItem(asType) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		l = l.WithField("update", i) | 
					
						
							|  |  |  | 		l.Debug("entering Update") | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-04 15:24:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | 	receivingAccount, requestingAccount, internal := extractFromCtx(ctx) | 
					
						
							|  |  |  | 	if internal { | 
					
						
							|  |  |  | 		return nil // Already processed. | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 	if accountable, ok := ap.ToAccountable(asType); ok { | 
					
						
							|  |  |  | 		return f.updateAccountable(ctx, receivingAccount, requestingAccount, accountable) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if statusable, ok := ap.ToStatusable(asType); ok { | 
					
						
							|  |  |  | 		return f.updateStatusable(ctx, receivingAccount, requestingAccount, statusable) | 
					
						
							| 
									
										
										
										
											2023-05-09 12:16:10 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-09 12:16:10 +02:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | func (f *federatingDB) updateAccountable(ctx context.Context, receivingAcct *gtsmodel.Account, requestingAcct *gtsmodel.Account, accountable ap.Accountable) error { | 
					
						
							| 
									
										
										
										
											2023-07-12 13:20:15 +02:00
										 |  |  | 	// Extract AP URI of the updated Accountable model. | 
					
						
							|  |  |  | 	idProp := accountable.GetJSONLDId() | 
					
						
							|  |  |  | 	if idProp == nil || !idProp.IsIRI() { | 
					
						
							| 
									
										
										
										
											2023-11-04 20:21:20 +00:00
										 |  |  | 		return gtserror.New("invalid id prop") | 
					
						
							| 
									
										
										
										
											2023-05-09 12:16:10 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-04 20:21:20 +00:00
										 |  |  | 	// Get the account URI string for checks | 
					
						
							|  |  |  | 	accountURI := idProp.GetIRI() | 
					
						
							|  |  |  | 	accountURIStr := accountURI.String() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Don't try to update local accounts. | 
					
						
							|  |  |  | 	if accountURI.Host == config.GetHost() { | 
					
						
							| 
									
										
										
										
											2023-05-09 12:16:10 +02:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-04 20:21:20 +00:00
										 |  |  | 	// Check that update was by the account themselves. | 
					
						
							|  |  |  | 	if accountURIStr != requestingAcct.URI { | 
					
						
							|  |  |  | 		return gtserror.Newf("update for %s was not requested by owner", accountURIStr) | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-12 13:20:15 +02:00
										 |  |  | 	// Pass in to the processor the existing version of the requesting | 
					
						
							|  |  |  | 	// account that we have, plus the Accountable representation that | 
					
						
							|  |  |  | 	// was delivered along with the Update, for further asynchronous | 
					
						
							|  |  |  | 	// updating of eg., avatar/header, emojis, etc. The actual db | 
					
						
							|  |  |  | 	// inserts/updates will take place there. | 
					
						
							| 
									
										
										
										
											2023-08-09 19:14:33 +02:00
										 |  |  | 	f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ | 
					
						
							| 
									
										
										
										
											2023-05-09 12:16:10 +02:00
										 |  |  | 		APObjectType:     ap.ObjectProfile, | 
					
						
							|  |  |  | 		APActivityType:   ap.ActivityUpdate, | 
					
						
							| 
									
										
										
										
											2023-07-12 13:20:15 +02:00
										 |  |  | 		GTSModel:         requestingAcct, | 
					
						
							| 
									
										
										
										
											2023-05-09 12:16:10 +02:00
										 |  |  | 		APObjectModel:    accountable, | 
					
						
							|  |  |  | 		ReceivingAccount: receivingAcct, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (f *federatingDB) updateStatusable(ctx context.Context, receivingAcct *gtsmodel.Account, requestingAcct *gtsmodel.Account, statusable ap.Statusable) error { | 
					
						
							|  |  |  | 	// Extract AP URI of the updated model. | 
					
						
							|  |  |  | 	idProp := statusable.GetJSONLDId() | 
					
						
							|  |  |  | 	if idProp == nil || !idProp.IsIRI() { | 
					
						
							|  |  |  | 		return gtserror.New("invalid id prop") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get the status URI string for lookups. | 
					
						
							|  |  |  | 	statusURI := idProp.GetIRI() | 
					
						
							|  |  |  | 	statusURIStr := statusURI.String() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Don't try to update local statuses. | 
					
						
							|  |  |  | 	if statusURI.Host == config.GetHost() { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-04 20:21:20 +00:00
										 |  |  | 	// Check if this is a forwarded object, i.e. did | 
					
						
							|  |  |  | 	// the account making the request also create this? | 
					
						
							|  |  |  | 	forwarded := !isSender(statusable, requestingAcct) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 	// Get the status we have on file for this URI string. | 
					
						
							|  |  |  | 	status, err := f.state.DB.GetStatusByURI(ctx, statusURIStr) | 
					
						
							| 
									
										
										
										
											2023-11-04 20:21:20 +00:00
										 |  |  | 	if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 		return gtserror.Newf("error fetching status from db: %w", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-04 20:21:20 +00:00
										 |  |  | 	if status == nil { | 
					
						
							|  |  |  | 		// We haven't seen this status before, be | 
					
						
							|  |  |  | 		// lenient and handle as a CREATE event. | 
					
						
							|  |  |  | 		return f.createStatusable(ctx, | 
					
						
							|  |  |  | 			receivingAcct, | 
					
						
							|  |  |  | 			requestingAcct, | 
					
						
							|  |  |  | 			statusable, | 
					
						
							|  |  |  | 			forwarded, | 
					
						
							|  |  |  | 		) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if forwarded { | 
					
						
							|  |  |  | 		// For forwarded updates, set a nil AS | 
					
						
							|  |  |  | 		// status to force refresh from remote. | 
					
						
							|  |  |  | 		statusable = nil | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Queue an UPDATE NOTE activity to our fedi API worker, | 
					
						
							|  |  |  | 	// this will handle necessary database insertions, etc. | 
					
						
							|  |  |  | 	f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ | 
					
						
							|  |  |  | 		APObjectType:     ap.ObjectNote, | 
					
						
							|  |  |  | 		APActivityType:   ap.ActivityUpdate, | 
					
						
							|  |  |  | 		GTSModel:         status, // original status | 
					
						
							| 
									
										
										
										
											2023-11-04 20:21:20 +00:00
										 |  |  | 		APObjectModel:    (ap.Statusable)(statusable), | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 		ReceivingAccount: receivingAcct, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |