public override async Task <bool> Handle() { if (MainObject.Type != "Accept") { return(true); } var followObject = await EntityStore.GetEntity((string)MainObject.Data["object"].Single().Primitive, false); if (followObject.Type != "Follow") { return(true); } if ((string)followObject.Data["object"].First().Primitive != (string)MainObject.Data["actor"].First().Primitive) { throw new InvalidOperationException("I won't let you do that, Starfox!"); } var followUser = (string)followObject.Data["object"].First().Primitive; if (Actor.Id != (string)followObject.Data["object"].First().Primitive) { return(true); // doesn't involve us, so meh } if (!followObject.IsOwner) { throw new InvalidOperationException("Follow isn't made on this server?"); } var relevant = await _relevantEntities.FindRelevantObject(followUser, "Reject", followObject.Id); if (relevant != null) { throw new InvalidOperationException("Follow has already been Rejected before!"); } if (MainObject.Type == "Accept") { relevant = await _relevantEntities.FindRelevantObject(followUser, "Accept", followObject.Id); if (relevant != null) { throw new InvalidOperationException("Follow has already been Accepted before!"); } } var following = await EntityStore.GetEntity((string)Actor.Data["following"].Single().Primitive, false); var user = await EntityStore.GetEntity((string)MainObject.Data["actor"].Single().Primitive, true); if (MainObject.Type == "Accept" && !await _collection.Contains(following.Id, user.Id)) { await _collection.AddToCollection(following, user); } else if (MainObject.Type == "Reject") { await _collection.RemoveFromCollection(following, user); } return(true); }
public async Task <IActionResult> RelevantEntities(RelevantObjectsModel model) { var user = User.FindFirstValue(JwtTokenSettings.ActorClaim); if (user == null) { return(Json(new List <ASObject>())); } var relevant = await _relevantEntities.FindRelevantObject(user, null, model.id); ASObject relevantObject = new ASObject(); relevantObject["id"].Add(new ASTerm((string)null)); foreach (var item in relevant) { relevantObject["relevant"].Add(new ASTerm(item.Data)); } return(Content((await _flattener.Unflatten(_entityStore, APEntity.From(relevantObject), 5)).Serialize().ToString(), "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")); }
public override async Task <bool> Handle() { if (MainObject.Type != "https://www.w3.org/ns/activitystreams#Accept") { return(true); } var followObject = await EntityStore.GetEntity(MainObject.Data["object"].Single().Id, false); if (followObject == null || !followObject.IsOwner) { followObject = (await _relevantEntities.FindRelevantObject(Actor.Id, "https://www.w3.org/ns/activitystreams#Follow", MainObject.Data["actor"].First().Id)).FirstOrDefault(); if (followObject != null) { MainObject.Data.Replace("object", ASTerm.MakeId(followObject.Id)); await EntityStore.StoreEntity(MainObject); } } if (followObject == null || followObject.Type != "https://www.w3.org/ns/activitystreams#Follow") { return(true); } if (followObject.Data["object"].First().Id != MainObject.Data["actor"].First().Id) { throw new InvalidOperationException("I won't let you do that, Starfox!"); } var followUser = followObject.Data["object"].First().Id; if (Actor.Id != followObject.Data["actor"].First().Id) { return(true); // doesn't involve us, so meh } if (!followObject.IsOwner) { throw new InvalidOperationException("Follow isn't made on this server?"); } var relevant = await _relevantEntities.FindRelevantObject(followUser, "Reject", followObject.Id); if (relevant.Count != 0) { throw new InvalidOperationException("Follow has already been Rejected before!"); } if (MainObject.Type == "https://www.w3.org/ns/activitystreams#Accept") { relevant = await _relevantEntities.FindRelevantObject(followUser, "Accept", followObject.Id); if (relevant.Count > 0) { throw new InvalidOperationException("Follow has already been Accepted before!"); } } var following = await EntityStore.GetEntity(Actor.Data["following"].Single().Id, false); var user = await EntityStore.GetEntity(MainObject.Data["actor"].Single().Id, true); if (MainObject.Type == "https://www.w3.org/ns/activitystreams#Accept" && !await _collection.Contains(following, user.Id)) { await _collection.AddToCollection(following, user); } else if (MainObject.Type == "https://www.w3.org/ns/activitystreams#Reject") { await _collection.RemoveFromCollection(following, user); } return(true); }
public override async Task <bool> Handle() { if (MainObject.Type != "https://www.w3.org/ns/activitystreams#Update" && MainObject.Type != "https://www.w3.org/ns/activitystreams#Delete") { return(true); } var activityData = MainObject.Data; var oldObject = await EntityStore.Bypass.GetEntity(activityData["object"].Single().Id, false); if (oldObject == null) { throw new InvalidOperationException("Cannot remove or update a non-existant object!"); } if (!oldObject.IsOwner) { throw new InvalidOperationException("Cannot remove or update an object not on this server!"); } var originatingCreate = (await _relevantEntities.FindRelevantObject("https://www.w3.org/ns/activitystreams#Create", oldObject.Id)).FirstOrDefault(); if (originatingCreate.Data["actor"].Single().Id != Actor.Id) { throw new InvalidOperationException("Cannot remove or update objects that weren't made by you!"); } if (MainObject.Type == "https://www.w3.org/ns/activitystreams#Update") { var newObject = await EntityStore.GetEntity(activityData["object"].Single().Id, false); if (newObject == oldObject) { throw new InvalidOperationException("No new object passed!"); } var data = oldObject.Data; foreach (var item in newObject.Data) { // SequenceEqual ensures that clients doing full-object updates won't cause this exception on e.g. type, attributedTo, etc if (UpdateBlacklist.Contains(item.Key) && (data[item.Key].Count != item.Value.Count || data[item.Key].Zip(item.Value, _isEqual).Any(a => !a))) { throw new InvalidOperationException($"Cannot update key {item.Key} as it's on the blacklist!"); } data[item.Key].Clear(); data[item.Key].AddRange(item.Value); } data.Replace("updated", ASTerm.MakePrimitive(DateTime.Now.ToString("o"))); newObject.Data = data; newObject.Type = oldObject.Type; await EntityStore.StoreEntity(newObject); } else { var newData = oldObject.Data; foreach (var kv in newData) { if (!DeleteWhitelist.Contains(kv.Key)) { kv.Value.Clear(); } } newData.Type.Clear(); newData.Type.Add("https://www.w3.org/ns/activitystreams#Tombstone"); newData.Replace("deleted", ASTerm.MakePrimitive(DateTime.Now.ToString("o"))); var newObject = APEntity.From(newData, true); await EntityStore.StoreEntity(newObject); } return(true); }
private async Task <ASTerm> _parseActivity(XElement element, string authorId, string targetUser) { if (await _isSelf(element.Element(Atom + "id").Value)) { return(ASTerm.MakeId(await _fixActivityToObjectId(element.Element(Atom + "id").Value))); } var ao = new ASObject(); ao.Id = element.Element(Atom + "id").Value; ao.Replace("_:origin", ASTerm.MakePrimitive("atom")); var verb = _objectTypeToType(element.Element(ActivityStreams + "verb")?.Value) ?? "Post"; var originalVerb = verb; if (verb == "Unfollow" && (await _entityStore.GetEntity(element.Element(Atom + "id").Value, false))?.Type == "Follow") // egh egh egh, why, mastodon { ao.Id += "#unfollow"; } if (verb == "Unfavorite") { verb = "Undo"; } if (verb == "Unfollow") { verb = "Undo"; } if (verb == "Request-friend") { return(null); } if (verb == "Post") { verb = "Create"; } else if (verb == "Share") { verb = "Announce"; } else if (verb == "Favorite") { verb = "Like"; } #pragma warning disable 618 if (!_entityConfiguration.IsActivity(verb)) { return(null); } #pragma warning restore 618 ao.Type.Add("https://www.w3.org/ns/activitystreams#" + verb); if (element.Element(Atom + "title") != null) { ao.Replace("summary", ASTerm.MakePrimitive(element.Element(Atom + "title").Value)); } if (element.Element(Atom + "published") != null) { ao.Replace("published", ASTerm.MakePrimitive(element.Element(Atom + "published").Value)); } if (element.Element(Atom + "updated") != null) { ao.Replace("updated", ASTerm.MakePrimitive(element.Element(Atom + "updated").Value)); } if (element.Element(Atom + "author") != null) { var newAuthor = _parseAuthor(element.Element(Atom + "author")); authorId = newAuthor.Id; } if (authorId != null) { ao.Replace("actor", ASTerm.MakeId(authorId)); } string retrievalUrl = null; foreach (var link in element.Elements(Atom + "link")) { var rel = link.Attribute(NoNamespace + "rel").Value; var type = link.Attribute(NoNamespace + "type")?.Value; var href = link.Attribute(NoNamespace + "href").Value; if (rel == "self" && type == "application/atom+xml") { retrievalUrl = href; } else if (rel == "alternate" && type == "text/html") { ao["url"].Add(ASTerm.MakePrimitive(href)); if (retrievalUrl == null) { retrievalUrl = href; } } else if (rel == "mentioned") { if (href == "http://activityschema.org/collection/public") { href = "https://www.w3.org/ns/activitystreams#Public"; } ao["to"].Add(ASTerm.MakeId(href)); } } if (targetUser != null) { ao["cc"].Add(ASTerm.MakeId(targetUser)); } if (retrievalUrl != null) { ao.Replace("atomUri", ASTerm.MakePrimitive(retrievalUrl)); } if (element.Element(ActivityStreams + "object") != null) { var parsedActivityObject = await _parseActivityObject(element.Element(ActivityStreams + "object"), authorId, targetUser); if (verb == "Undo" && originalVerb == "Unfavorite") { parsedActivityObject = ASTerm.MakeId((await _relevantEntities.FindRelevantObject(authorId, "https://www.w3.org/ns/activitystreams#Like", _getId(parsedActivityObject))).FirstOrDefault()?.Id); } else if (verb == "Undo" && originalVerb == "Unfollow") { parsedActivityObject = ASTerm.MakeId((await _relevantEntities.FindRelevantObject(authorId, "https://www.w3.org/ns/activitystreams#Follow", _getId(parsedActivityObject))).FirstOrDefault()?.Id); } ao.Replace("object", parsedActivityObject); } else if (element.Element(ActivityStreams + "object-type") == null && originalVerb == "Unfollow") { // you thought Mastodon was bad? // GNU Social doesn't send an object in an unfollow. // .. what ao.Replace("object", ASTerm.MakeId((await _relevantEntities.FindRelevantObject(authorId, "https://www.w3.org/ns/activitystreams#Follow", targetUser)).FirstOrDefault()?.Id)); } else { ao.Replace("object", await _parseActivityObject(element, authorId, targetUser, true)); } return(ASTerm.MakeSubObject(ao)); }