| 
									
										
										
										
											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" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-13 17:29:43 +01:00
										 |  |  | 	"github.com/superseriousbusiness/activity/streams/vocab" | 
					
						
							| 
									
										
										
										
											2021-08-31 15:59:12 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/ap" | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/db" | 
					
						
							|  |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | 
					
						
							|  |  |  | 	"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-12-20 15:19:53 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/uris" | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/util" | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | func (f *federatingDB) GetAccept( | 
					
						
							|  |  |  | 	ctx context.Context, | 
					
						
							|  |  |  | 	acceptIRI *url.URL, | 
					
						
							|  |  |  | ) (vocab.ActivityStreamsAccept, error) { | 
					
						
							| 
									
										
										
										
											2024-08-24 11:49:37 +02:00
										 |  |  | 	approval, err := f.state.DB.GetInteractionRequestByURI(ctx, acceptIRI.String()) | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-08-24 11:49:37 +02:00
										 |  |  | 	return f.converter.InteractionReqToASAccept(ctx, approval) | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsAccept) error { | 
					
						
							| 
									
										
										
										
											2024-09-17 19:35:47 +00:00
										 |  |  | 	log.DebugKV(ctx, "accept", serialize{accept}) | 
					
						
							| 
									
										
										
										
											2021-10-04 15:24:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-17 15:20:39 +01:00
										 |  |  | 	requestingAcct := activityContext.requestingAcct | 
					
						
							|  |  |  | 	receivingAcct := activityContext.receivingAcct | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-12 15:34:08 +01:00
										 |  |  | 	if requestingAcct.IsMoving() { | 
					
						
							|  |  |  | 		// A Moving account | 
					
						
							|  |  |  | 		// can't do this. | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 	acceptID := ap.GetJSONLDId(accept) | 
					
						
							|  |  |  | 	if acceptID == nil { | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 		// We need an ID. | 
					
						
							|  |  |  | 		const text = "Accept had no id property" | 
					
						
							|  |  |  | 		return gtserror.NewErrorBadRequest(errors.New(text), text) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 	// Ensure requester is the same as the | 
					
						
							|  |  |  | 	// Actor of the Accept; you can't Accept | 
					
						
							|  |  |  | 	// something on someone else's behalf. | 
					
						
							|  |  |  | 	actorURI, err := ap.ExtractActorURI(accept) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		const text = "Accept had empty or invalid actor property" | 
					
						
							|  |  |  | 		return gtserror.NewErrorBadRequest(errors.New(text), text) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if requestingAcct.URI != actorURI.String() { | 
					
						
							|  |  |  | 		const text = "Accept actor and requesting account were not the same" | 
					
						
							|  |  |  | 		return gtserror.NewErrorBadRequest(errors.New(text), text) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	// Iterate all provided objects in the activity, | 
					
						
							|  |  |  | 	// handling the ones we know how to handle. | 
					
						
							|  |  |  | 	for _, object := range ap.ExtractObjects(accept) { | 
					
						
							|  |  |  | 		if asType := object.GetType(); asType != nil { | 
					
						
							| 
									
										
										
										
											2024-09-17 19:35:47 +00:00
										 |  |  | 			// Check and handle any vocab.Type objects. | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 			switch name := asType.GetTypeName(); { | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 			// ACCEPT FOLLOW | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 			case name == ap.ActivityFollow: | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 				if err := f.acceptFollowType( | 
					
						
							|  |  |  | 					ctx, | 
					
						
							|  |  |  | 					asType, | 
					
						
							|  |  |  | 					receivingAcct, | 
					
						
							|  |  |  | 					requestingAcct, | 
					
						
							|  |  |  | 				); err != nil { | 
					
						
							|  |  |  | 					return err | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-09-17 19:35:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 			// ACCEPT TYPE-HINTED LIKE | 
					
						
							|  |  |  | 			// | 
					
						
							|  |  |  | 			// ie., a Like with just `id` | 
					
						
							|  |  |  | 			// and `type` properties set. | 
					
						
							|  |  |  | 			case name == ap.ActivityLike: | 
					
						
							|  |  |  | 				objIRI := ap.GetJSONLDId(asType) | 
					
						
							|  |  |  | 				if objIRI == nil { | 
					
						
							|  |  |  | 					log.Debugf(ctx, "could not retrieve id of inlined Accept object %s", name) | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if err := f.acceptLikeIRI( | 
					
						
							|  |  |  | 					ctx, | 
					
						
							|  |  |  | 					acceptID, | 
					
						
							|  |  |  | 					accept, | 
					
						
							|  |  |  | 					objIRI.String(), | 
					
						
							|  |  |  | 					receivingAcct, | 
					
						
							|  |  |  | 					requestingAcct, | 
					
						
							|  |  |  | 				); err != nil { | 
					
						
							|  |  |  | 					return err | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// ACCEPT TYPE-HINTED REPLY OR ANNOUNCE. | 
					
						
							|  |  |  | 			// | 
					
						
							|  |  |  | 			// ie., a statusable or Announce with | 
					
						
							|  |  |  | 			// just `id` and `type` properties set. | 
					
						
							|  |  |  | 			case name == ap.ActivityAnnounce || ap.IsStatusable(name): | 
					
						
							|  |  |  | 				objIRI := ap.GetJSONLDId(asType) | 
					
						
							|  |  |  | 				if objIRI == nil { | 
					
						
							|  |  |  | 					log.Debugf(ctx, "could not retrieve id of inlined Accept object %s", name) | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if err := f.acceptOtherIRI( | 
					
						
							|  |  |  | 					ctx, | 
					
						
							|  |  |  | 					acceptID, | 
					
						
							|  |  |  | 					accept, | 
					
						
							|  |  |  | 					objIRI, | 
					
						
							|  |  |  | 					receivingAcct, | 
					
						
							|  |  |  | 					requestingAcct, | 
					
						
							|  |  |  | 				); err != nil { | 
					
						
							|  |  |  | 					return err | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-17 19:35:47 +00:00
										 |  |  | 			// UNHANDLED | 
					
						
							|  |  |  | 			default: | 
					
						
							|  |  |  | 				log.Debugf(ctx, "unhandled object type: %s", name) | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 		} else if object.IsIRI() { | 
					
						
							|  |  |  | 			// Check and handle any | 
					
						
							|  |  |  | 			// IRI type objects. | 
					
						
							|  |  |  | 			switch objIRI := object.GetIRI(); { | 
					
						
							| 
									
										
										
										
											2024-02-06 12:59:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 			// ACCEPT FOLLOW | 
					
						
							|  |  |  | 			case uris.IsFollowPath(objIRI): | 
					
						
							|  |  |  | 				if err := f.acceptFollowIRI( | 
					
						
							|  |  |  | 					ctx, | 
					
						
							|  |  |  | 					objIRI.String(), | 
					
						
							|  |  |  | 					receivingAcct, | 
					
						
							|  |  |  | 					requestingAcct, | 
					
						
							|  |  |  | 				); err != nil { | 
					
						
							|  |  |  | 					return err | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 			// ACCEPT LIKE | 
					
						
							|  |  |  | 			case uris.IsLikePath(objIRI): | 
					
						
							|  |  |  | 				if err := f.acceptLikeIRI( | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 					ctx, | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 					acceptID, | 
					
						
							|  |  |  | 					accept, | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 					objIRI.String(), | 
					
						
							|  |  |  | 					receivingAcct, | 
					
						
							|  |  |  | 					requestingAcct, | 
					
						
							|  |  |  | 				); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 					return err | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 			// ACCEPT OTHER (reply? announce?) | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 			// | 
					
						
							|  |  |  | 			// Don't check on IsStatusesPath | 
					
						
							|  |  |  | 			// as this may be a remote status. | 
					
						
							|  |  |  | 			default: | 
					
						
							|  |  |  | 				if err := f.acceptOtherIRI( | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 					ctx, | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 					acceptID, | 
					
						
							|  |  |  | 					accept, | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 					objIRI, | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 					receivingAcct, | 
					
						
							|  |  |  | 					requestingAcct, | 
					
						
							|  |  |  | 				); err != nil { | 
					
						
							|  |  |  | 					return err | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | func (f *federatingDB) acceptFollowType( | 
					
						
							|  |  |  | 	ctx context.Context, | 
					
						
							|  |  |  | 	asType vocab.Type, | 
					
						
							|  |  |  | 	receivingAcct *gtsmodel.Account, | 
					
						
							|  |  |  | 	requestingAcct *gtsmodel.Account, | 
					
						
							|  |  |  | ) error { | 
					
						
							|  |  |  | 	// Cast the vocab.Type object to known AS type. | 
					
						
							|  |  |  | 	asFollow := asType.(vocab.ActivityStreamsFollow) | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	// Reconstruct the follow. | 
					
						
							|  |  |  | 	follow, err := f.converter.ASFollowToFollow(ctx, asFollow) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		err := gtserror.Newf("error converting Follow to *gtsmodel.Follow: %w", err) | 
					
						
							|  |  |  | 		return gtserror.NewErrorInternalError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	// Lock on the Follow URI | 
					
						
							|  |  |  | 	// as we may be updating it. | 
					
						
							|  |  |  | 	unlock := f.state.FedLocks.Lock(follow.URI) | 
					
						
							|  |  |  | 	defer unlock() | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	// Make sure the creator of the original follow | 
					
						
							|  |  |  | 	// is the same as whatever inbox this landed in. | 
					
						
							|  |  |  | 	if follow.AccountID != receivingAcct.ID { | 
					
						
							|  |  |  | 		const text = "Follow account and inbox account were not the same" | 
					
						
							|  |  |  | 		return gtserror.NewErrorUnprocessableEntity(errors.New(text), text) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-06 12:59:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	// Make sure the target of the original follow | 
					
						
							|  |  |  | 	// is the same as the account making the request. | 
					
						
							|  |  |  | 	if follow.TargetAccountID != requestingAcct.ID { | 
					
						
							|  |  |  | 		const text = "Follow target account and requesting account were not the same" | 
					
						
							|  |  |  | 		return gtserror.NewErrorForbidden(errors.New(text), text) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	// Accept and get the populated follow back. | 
					
						
							|  |  |  | 	follow, err = f.state.DB.AcceptFollowRequest( | 
					
						
							|  |  |  | 		ctx, | 
					
						
							|  |  |  | 		follow.AccountID, | 
					
						
							|  |  |  | 		follow.TargetAccountID, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							|  |  |  | 		err := gtserror.Newf("db error accepting follow request: %w", err) | 
					
						
							|  |  |  | 		return gtserror.NewErrorInternalError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if follow == nil { | 
					
						
							|  |  |  | 		// There was no follow request | 
					
						
							|  |  |  | 		// to accept, just return 202. | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	// Send the accepted follow through | 
					
						
							|  |  |  | 	// the processor to do side effects. | 
					
						
							|  |  |  | 	f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{ | 
					
						
							|  |  |  | 		APObjectType:   ap.ActivityFollow, | 
					
						
							|  |  |  | 		APActivityType: ap.ActivityAccept, | 
					
						
							|  |  |  | 		GTSModel:       follow, | 
					
						
							|  |  |  | 		Receiving:      receivingAcct, | 
					
						
							|  |  |  | 		Requesting:     requestingAcct, | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (f *federatingDB) acceptFollowIRI( | 
					
						
							|  |  |  | 	ctx context.Context, | 
					
						
							|  |  |  | 	objectIRI string, | 
					
						
							|  |  |  | 	receivingAcct *gtsmodel.Account, | 
					
						
							|  |  |  | 	requestingAcct *gtsmodel.Account, | 
					
						
							|  |  |  | ) error { | 
					
						
							|  |  |  | 	// Lock on this potential Follow | 
					
						
							|  |  |  | 	// URI as we may be updating it. | 
					
						
							|  |  |  | 	unlock := f.state.FedLocks.Lock(objectIRI) | 
					
						
							|  |  |  | 	defer unlock() | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	// Get the follow req from the db. | 
					
						
							|  |  |  | 	followReq, err := f.state.DB.GetFollowRequestByURI(ctx, objectIRI) | 
					
						
							|  |  |  | 	if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							|  |  |  | 		err := gtserror.Newf("db error getting follow request: %w", err) | 
					
						
							|  |  |  | 		return gtserror.NewErrorInternalError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if followReq == nil { | 
					
						
							|  |  |  | 		// We didn't have a follow request | 
					
						
							|  |  |  | 		// with this URI, so nothing to do. | 
					
						
							|  |  |  | 		// Just return. | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make sure the creator of the original follow | 
					
						
							|  |  |  | 	// is the same as whatever inbox this landed in. | 
					
						
							|  |  |  | 	if followReq.AccountID != receivingAcct.ID { | 
					
						
							|  |  |  | 		const text = "Follow account and inbox account were not the same" | 
					
						
							|  |  |  | 		return gtserror.NewErrorUnprocessableEntity(errors.New(text), text) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make sure the target of the original follow | 
					
						
							|  |  |  | 	// is the same as the account making the request. | 
					
						
							|  |  |  | 	if followReq.TargetAccountID != requestingAcct.ID { | 
					
						
							|  |  |  | 		const text = "Follow target account and requesting account were not the same" | 
					
						
							|  |  |  | 		return gtserror.NewErrorForbidden(errors.New(text), text) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Accept and get the populated follow back. | 
					
						
							|  |  |  | 	follow, err := f.state.DB.AcceptFollowRequest( | 
					
						
							|  |  |  | 		ctx, | 
					
						
							|  |  |  | 		followReq.AccountID, | 
					
						
							|  |  |  | 		followReq.TargetAccountID, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							|  |  |  | 		err := gtserror.Newf("db error accepting follow request: %w", err) | 
					
						
							|  |  |  | 		return gtserror.NewErrorInternalError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if follow == nil { | 
					
						
							|  |  |  | 		// There was no follow request | 
					
						
							|  |  |  | 		// to accept, just return 202. | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	// Send the accepted follow through | 
					
						
							|  |  |  | 	// the processor to do side effects. | 
					
						
							|  |  |  | 	f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{ | 
					
						
							|  |  |  | 		APObjectType:   ap.ActivityFollow, | 
					
						
							|  |  |  | 		APActivityType: ap.ActivityAccept, | 
					
						
							|  |  |  | 		GTSModel:       follow, | 
					
						
							|  |  |  | 		Receiving:      receivingAcct, | 
					
						
							|  |  |  | 		Requesting:     requestingAcct, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | func (f *federatingDB) acceptOtherIRI( | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	ctx context.Context, | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 	acceptID *url.URL, | 
					
						
							|  |  |  | 	accept vocab.ActivityStreamsAccept, | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 	objectIRI *url.URL, | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	receivingAcct *gtsmodel.Account, | 
					
						
							|  |  |  | 	requestingAcct *gtsmodel.Account, | 
					
						
							|  |  |  | ) error { | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 	// See if we can get a status from the db. | 
					
						
							|  |  |  | 	status, err := f.state.DB.GetStatusByURI(ctx, objectIRI.String()) | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							|  |  |  | 		err := gtserror.Newf("db error getting status: %w", err) | 
					
						
							|  |  |  | 		return gtserror.NewErrorInternalError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 	if status != nil { | 
					
						
							|  |  |  | 		// We had a status stored with this | 
					
						
							|  |  |  | 		// objectIRI, proceed to accept it. | 
					
						
							|  |  |  | 		return f.acceptStoredStatus( | 
					
						
							|  |  |  | 			ctx, | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 			acceptID, | 
					
						
							|  |  |  | 			accept, | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 			status, | 
					
						
							|  |  |  | 			receivingAcct, | 
					
						
							|  |  |  | 			requestingAcct, | 
					
						
							|  |  |  | 		) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if objectIRI.Host == config.GetHost() || | 
					
						
							|  |  |  | 		objectIRI.Host == config.GetAccountDomain() { | 
					
						
							|  |  |  | 		// Claims to be Accepting something of ours, | 
					
						
							|  |  |  | 		// but we don't have a status stored for this | 
					
						
							|  |  |  | 		// URI, so most likely it's been deleted in | 
					
						
							|  |  |  | 		// the meantime, just bail. | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 	// This must be an Accept of a remote Activity | 
					
						
							|  |  |  | 	// or Object. Ensure relevance of this message | 
					
						
							|  |  |  | 	// by checking that receiver follows requester. | 
					
						
							|  |  |  | 	following, err := f.state.DB.IsFollowing( | 
					
						
							|  |  |  | 		ctx, | 
					
						
							|  |  |  | 		receivingAcct.ID, | 
					
						
							|  |  |  | 		requestingAcct.ID, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		err := gtserror.Newf("db error checking following: %w", err) | 
					
						
							|  |  |  | 		return gtserror.NewErrorInternalError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !following { | 
					
						
							|  |  |  | 		// If we don't follow this person, and | 
					
						
							|  |  |  | 		// they're not Accepting something we know | 
					
						
							|  |  |  | 		// about, then we don't give a good goddamn. | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 	// This may be a reply, or it may be a boost, | 
					
						
							|  |  |  | 	// we can't know yet without dereferencing it, | 
					
						
							|  |  |  | 	// but let the processor worry about that. | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 	// | 
					
						
							|  |  |  | 	// TODO: do something with type hinting here. | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 	apObjectType := ap.ObjectUnknown | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 	// Extract appropriate approvedByURI from the Accept. | 
					
						
							|  |  |  | 	approvedByURI, err := approvedByURI(acceptID, accept) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return gtserror.NewErrorForbidden(err, err.Error()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 	// Pass to the processor and let them handle side effects. | 
					
						
							|  |  |  | 	f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{ | 
					
						
							|  |  |  | 		APObjectType:   apObjectType, | 
					
						
							|  |  |  | 		APActivityType: ap.ActivityAccept, | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 		APIRI:          approvedByURI, | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 		APObject:       objectIRI, | 
					
						
							|  |  |  | 		Receiving:      receivingAcct, | 
					
						
							|  |  |  | 		Requesting:     requestingAcct, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (f *federatingDB) acceptStoredStatus( | 
					
						
							|  |  |  | 	ctx context.Context, | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 	acceptID *url.URL, | 
					
						
							|  |  |  | 	accept vocab.ActivityStreamsAccept, | 
					
						
							| 
									
										
										
										
											2024-10-08 10:51:13 +02:00
										 |  |  | 	status *gtsmodel.Status, | 
					
						
							|  |  |  | 	receivingAcct *gtsmodel.Account, | 
					
						
							|  |  |  | 	requestingAcct *gtsmodel.Account, | 
					
						
							|  |  |  | ) error { | 
					
						
							|  |  |  | 	// Lock on this status URI | 
					
						
							|  |  |  | 	// as we may be updating it. | 
					
						
							|  |  |  | 	unlock := f.state.FedLocks.Lock(status.URI) | 
					
						
							|  |  |  | 	defer unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-17 21:35:13 +02:00
										 |  |  | 	pendingApproval := util.PtrOrValue(status.PendingApproval, false) | 
					
						
							|  |  |  | 	if !pendingApproval { | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 		// Status doesn't need approval or it's | 
					
						
							|  |  |  | 		// already been approved by an Accept. | 
					
						
							|  |  |  | 		// Just return. | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make sure the target of the interaction (reply/boost) | 
					
						
							|  |  |  | 	// is the same as the account doing the Accept. | 
					
						
							|  |  |  | 	if status.BoostOfAccountID != requestingAcct.ID && | 
					
						
							|  |  |  | 		status.InReplyToAccountID != requestingAcct.ID { | 
					
						
							|  |  |  | 		const text = "status reply to or boost of account and requesting account were not the same" | 
					
						
							|  |  |  | 		return gtserror.NewErrorForbidden(errors.New(text), text) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 	// Extract appropriate approvedByURI from the Accept. | 
					
						
							|  |  |  | 	approvedByURI, err := approvedByURI(acceptID, accept) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return gtserror.NewErrorForbidden(err, err.Error()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Mark the status as approved by this URI. | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	status.PendingApproval = util.Ptr(false) | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 	status.ApprovedByURI = approvedByURI.String() | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	if err := f.state.DB.UpdateStatus( | 
					
						
							|  |  |  | 		ctx, | 
					
						
							|  |  |  | 		status, | 
					
						
							|  |  |  | 		"pending_approval", | 
					
						
							|  |  |  | 		"approved_by_uri", | 
					
						
							|  |  |  | 	); err != nil { | 
					
						
							|  |  |  | 		err := gtserror.Newf("db error accepting status: %w", err) | 
					
						
							|  |  |  | 		return gtserror.NewErrorInternalError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var apObjectType string | 
					
						
							|  |  |  | 	if status.InReplyToID != "" { | 
					
						
							|  |  |  | 		// Accepting a Reply. | 
					
						
							|  |  |  | 		apObjectType = ap.ObjectNote | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		// Accepting an Announce. | 
					
						
							|  |  |  | 		apObjectType = ap.ActivityAnnounce | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Send the now-approved status through to the | 
					
						
							|  |  |  | 	// fedi worker again to process side effects. | 
					
						
							|  |  |  | 	f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{ | 
					
						
							|  |  |  | 		APObjectType:   apObjectType, | 
					
						
							|  |  |  | 		APActivityType: ap.ActivityAccept, | 
					
						
							|  |  |  | 		GTSModel:       status, | 
					
						
							|  |  |  | 		Receiving:      receivingAcct, | 
					
						
							|  |  |  | 		Requesting:     requestingAcct, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (f *federatingDB) acceptLikeIRI( | 
					
						
							|  |  |  | 	ctx context.Context, | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 	acceptID *url.URL, | 
					
						
							|  |  |  | 	accept vocab.ActivityStreamsAccept, | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	objectIRI string, | 
					
						
							|  |  |  | 	receivingAcct *gtsmodel.Account, | 
					
						
							|  |  |  | 	requestingAcct *gtsmodel.Account, | 
					
						
							|  |  |  | ) error { | 
					
						
							|  |  |  | 	// Lock on this potential Like | 
					
						
							|  |  |  | 	// URI as we may be updating it. | 
					
						
							|  |  |  | 	unlock := f.state.FedLocks.Lock(objectIRI) | 
					
						
							|  |  |  | 	defer unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get the fave from the db. | 
					
						
							|  |  |  | 	fave, err := f.state.DB.GetStatusFaveByURI(ctx, objectIRI) | 
					
						
							|  |  |  | 	if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							|  |  |  | 		err := gtserror.Newf("db error getting fave: %w", err) | 
					
						
							|  |  |  | 		return gtserror.NewErrorInternalError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if fave == nil { | 
					
						
							|  |  |  | 		// We didn't have a fave with | 
					
						
							|  |  |  | 		// this URI, so nothing to do. | 
					
						
							|  |  |  | 		// Just return. | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !fave.Account.IsLocal() { | 
					
						
							|  |  |  | 		// We don't process Accepts of Likes | 
					
						
							|  |  |  | 		// that weren't created on our instance. | 
					
						
							|  |  |  | 		// Just return. | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-17 21:35:13 +02:00
										 |  |  | 	pendingApproval := util.PtrOrValue(fave.PendingApproval, false) | 
					
						
							|  |  |  | 	if !pendingApproval { | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 		// Like doesn't need approval or it's | 
					
						
							|  |  |  | 		// already been approved by an Accept. | 
					
						
							|  |  |  | 		// Just return. | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make sure the creator of the original Like | 
					
						
							|  |  |  | 	// is the same as the inbox processing the Accept; | 
					
						
							|  |  |  | 	// this also ensures the Like is local. | 
					
						
							|  |  |  | 	if fave.AccountID != receivingAcct.ID { | 
					
						
							|  |  |  | 		const text = "fave creator account and inbox account were not the same" | 
					
						
							|  |  |  | 		return gtserror.NewErrorUnprocessableEntity(errors.New(text), text) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make sure the target of the Like is the | 
					
						
							|  |  |  | 	// same as the account doing the Accept. | 
					
						
							|  |  |  | 	if fave.TargetAccountID != requestingAcct.ID { | 
					
						
							|  |  |  | 		const text = "status fave target account and requesting account were not the same" | 
					
						
							|  |  |  | 		return gtserror.NewErrorForbidden(errors.New(text), text) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 	// Extract appropriate approvedByURI from the Accept. | 
					
						
							|  |  |  | 	approvedByURI, err := approvedByURI(acceptID, accept) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return gtserror.NewErrorForbidden(err, err.Error()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Mark the fave as approved by this URI. | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	fave.PendingApproval = util.Ptr(false) | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 	fave.ApprovedByURI = approvedByURI.String() | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 	if err := f.state.DB.UpdateStatusFave( | 
					
						
							|  |  |  | 		ctx, | 
					
						
							|  |  |  | 		fave, | 
					
						
							|  |  |  | 		"pending_approval", | 
					
						
							|  |  |  | 		"approved_by_uri", | 
					
						
							|  |  |  | 	); err != nil { | 
					
						
							|  |  |  | 		err := gtserror.Newf("db error accepting status: %w", err) | 
					
						
							|  |  |  | 		return gtserror.NewErrorInternalError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Send the now-approved fave through to the | 
					
						
							|  |  |  | 	// fedi worker again to process side effects. | 
					
						
							|  |  |  | 	f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{ | 
					
						
							|  |  |  | 		APObjectType:   ap.ActivityLike, | 
					
						
							|  |  |  | 		APActivityType: ap.ActivityAccept, | 
					
						
							|  |  |  | 		GTSModel:       fave, | 
					
						
							|  |  |  | 		Receiving:      receivingAcct, | 
					
						
							|  |  |  | 		Requesting:     requestingAcct, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-27 16:06:24 +02:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-02-19 18:09:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | // approvedByURI extracts the appropriate *url.URL | 
					
						
							|  |  |  | // to use as an interaction's approvedBy value by | 
					
						
							|  |  |  | // checking to see if the Accept has a result URL set. | 
					
						
							|  |  |  | // If that result URL exists, is an IRI (not a type), | 
					
						
							|  |  |  | // and is on the same host as the Accept ID, then the | 
					
						
							|  |  |  | // result URI will be returned. In all other cases, | 
					
						
							|  |  |  | // the Accept ID is returned unchanged. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Error is only returned if the result URI is set | 
					
						
							|  |  |  | // but the host differs from the Accept ID host. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // TODO: This function should be updated at some point | 
					
						
							|  |  |  | // to check for inlined result type, and see if type is | 
					
						
							|  |  |  | // a LikeApproval, ReplyApproval, or AnnounceApproval, | 
					
						
							|  |  |  | // and check the attributedTo, object, and target of | 
					
						
							|  |  |  | // the approval as well. But this'll do for now. | 
					
						
							|  |  |  | func approvedByURI( | 
					
						
							|  |  |  | 	acceptID *url.URL, | 
					
						
							|  |  |  | 	accept vocab.ActivityStreamsAccept, | 
					
						
							|  |  |  | ) (*url.URL, error) { | 
					
						
							|  |  |  | 	// Check if the Accept has a `result` property | 
					
						
							|  |  |  | 	// set on it (which should be an approval). | 
					
						
							|  |  |  | 	resultProp := accept.GetActivityStreamsResult() | 
					
						
							|  |  |  | 	if resultProp == nil { | 
					
						
							|  |  |  | 		// No result, | 
					
						
							|  |  |  | 		// use AcceptID. | 
					
						
							|  |  |  | 		return acceptID, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if resultProp.Len() != 1 { | 
					
						
							|  |  |  | 		// Result was unexpected | 
					
						
							|  |  |  | 		// length, can't use this. | 
					
						
							|  |  |  | 		return acceptID, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	result := resultProp.At(0) | 
					
						
							|  |  |  | 	if result == nil { | 
					
						
							|  |  |  | 		// Result entry | 
					
						
							|  |  |  | 		// was nil, huh! | 
					
						
							|  |  |  | 		return acceptID, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !result.IsIRI() { | 
					
						
							|  |  |  | 		// Can't handle | 
					
						
							|  |  |  | 		// inlined yet. | 
					
						
							|  |  |  | 		return acceptID, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resultIRI := result.GetIRI() | 
					
						
							|  |  |  | 	if resultIRI == nil { | 
					
						
							|  |  |  | 		// Result entry | 
					
						
							|  |  |  | 		// was nil, huh! | 
					
						
							|  |  |  | 		return acceptID, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if resultIRI.Host != acceptID.Host { | 
					
						
							|  |  |  | 		// What the boobs is this? | 
					
						
							|  |  |  | 		err := fmt.Errorf( | 
					
						
							|  |  |  | 			"host of result %s differed from host of Accept %s", | 
					
						
							|  |  |  | 			resultIRI, accept, | 
					
						
							|  |  |  | 		) | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Use the result IRI we've been | 
					
						
							|  |  |  | 	// given instead of the acceptID. | 
					
						
							|  |  |  | 	return resultIRI, nil | 
					
						
							|  |  |  | } |