| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-25 15:15:36 +02:00
										 |  |  | 	"code.superseriousbusiness.org/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 { | 
					
						
							| 
									
										
										
										
											2024-09-17 19:35:47 +00:00
										 |  |  | 	log.DebugKV(ctx, "update", serialize{asType}) | 
					
						
							| 
									
										
										
										
											2021-10-04 15:24:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-28 20:22:23 +00:00
										 |  |  | 	// Mark activity as handled. | 
					
						
							|  |  |  | 	f.storeActivityID(asType) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Extract relevant values from passed ctx. | 
					
						
							| 
									
										
										
										
											2024-02-17 15:20:39 +01:00
										 |  |  | 	activityContext := getActivityContext(ctx) | 
					
						
							|  |  |  | 	if activityContext.internal { | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | 		return nil // Already processed. | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-28 20:22:23 +00:00
										 |  |  | 	requesting := activityContext.requestingAcct | 
					
						
							|  |  |  | 	receiving := activityContext.receivingAcct | 
					
						
							| 
									
										
										
										
											2024-02-17 15:20:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 	if accountable, ok := ap.ToAccountable(asType); ok { | 
					
						
							| 
									
										
										
										
											2025-01-28 20:22:23 +00:00
										 |  |  | 		return f.updateAccountable(ctx, receiving, requesting, accountable) | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if statusable, ok := ap.ToStatusable(asType); ok { | 
					
						
							| 
									
										
										
										
											2025-01-28 20:22:23 +00:00
										 |  |  | 		return f.updateStatusable(ctx, receiving, requesting, statusable) | 
					
						
							| 
									
										
										
										
											2023-05-09 12:16:10 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-17 19:35:47 +00:00
										 |  |  | 	log.Debugf(ctx, "unhandled object type: %T", asType) | 
					
						
							| 
									
										
										
										
											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. | 
					
						
							| 
									
										
										
										
											2024-04-26 13:50:46 +01:00
										 |  |  | 	f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{ | 
					
						
							| 
									
										
										
										
											2024-06-06 15:43:25 +02:00
										 |  |  | 		APObjectType:   ap.ActorPerson, | 
					
						
							| 
									
										
										
										
											2024-04-26 13:50:46 +01:00
										 |  |  | 		APActivityType: ap.ActivityUpdate, | 
					
						
							|  |  |  | 		GTSModel:       requestingAcct, | 
					
						
							|  |  |  | 		APObject:       accountable, | 
					
						
							|  |  |  | 		Receiving:      receivingAcct, | 
					
						
							|  |  |  | 		Requesting:     requestingAcct, | 
					
						
							| 
									
										
										
										
											2023-05-09 12:16:10 +02:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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. | 
					
						
							| 
									
										
										
										
											2024-04-26 13:50:46 +01:00
										 |  |  | 	f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{ | 
					
						
							|  |  |  | 		APObjectType:   ap.ObjectNote, | 
					
						
							|  |  |  | 		APActivityType: ap.ActivityUpdate, | 
					
						
							|  |  |  | 		GTSModel:       status, // original status | 
					
						
							|  |  |  | 		APObject:       (ap.Statusable)(statusable), | 
					
						
							|  |  |  | 		Receiving:      receivingAcct, | 
					
						
							|  |  |  | 		Requesting:     requestingAcct, | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |