public async Task <APEntity> ClientToServer(APEntity outbox, ASObject activity) { var stagingStore = new StagingEntityStore(_mainStore); var userId = outbox.Data["attributedTo"].Single().Id; var user = await _mainStore.GetEntity(userId, false); if (!EntityData.IsActivity(activity)) { #pragma warning disable CS0618 // Type or member is obsolete if (EntityData.IsActivity(activity.Type.FirstOrDefault())) #pragma warning restore CS0618 // Type or member is obsolete { throw new UnauthorizedAccessException("Sending an Activity without an actor isn't allowed. Are you sure you wanted to do that?"); } var createActivity = new ASObject(); createActivity.Type.Add("https://www.w3.org/ns/activitystreams#Create"); createActivity["to"].AddRange(activity["to"]); createActivity["bto"].AddRange(activity["bto"]); createActivity["cc"].AddRange(activity["cc"]); createActivity["bcc"].AddRange(activity["bcc"]); createActivity["audience"].AddRange(activity["audience"]); createActivity["actor"].Add(ASTerm.MakeId(userId)); createActivity["object"].Add(ASTerm.MakeSubObject(activity)); activity = createActivity; } if (activity.Type.Contains("https://www.w3.org/ns/activitystreams#Create")) { activity.Id = null; if (activity["object"].SingleOrDefault()?.SubObject != null) { activity["object"].Single().SubObject.Id = null; } } var flattened = await _flattener.FlattenAndStore(stagingStore, activity); IEntityStore store = stagingStore; foreach (var type in ServerConfig.ClientToServerHandlers) { var handler = (BaseHandler)ActivatorUtilities.CreateInstance(_serviceProvider, type, store, flattened, user, outbox, _user); var handled = await handler.Handle(); flattened = handler.MainObject; if (!handled) { break; } if (type == typeof(CommitChangesHandler)) { store = _mainStore; } } return(flattened); }
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(); foreach (var item in relevant) { relevantObject["relevant"].Add(ASTerm.MakeSubObject(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 async Task <IActionResult> MakeNewActor(NewActorModel model) { var data = await _getUserInfo(); if (string.IsNullOrWhiteSpace(model.Username)) { return(View("NewActor", model)); } var user = model.Username; var obj = new ASObject(); obj.Type.Add("https://www.w3.org/ns/activitystreams#Person"); obj["preferredUsername"].Add(ASTerm.MakePrimitive(user)); obj["name"].Add(ASTerm.MakePrimitive(string.IsNullOrWhiteSpace(model.Name) ? "Unnamed" : model.Name)); if (!string.IsNullOrWhiteSpace(model.Summary)) { obj["summary"].Add(ASTerm.MakePrimitive(model.Summary)); } var create = new ASObject(); create.Type.Add("https://www.w3.org/ns/activitystreams#Create"); create["object"].Add(ASTerm.MakeSubObject(obj)); create["to"].Add(ASTerm.MakeId("https://www.w3.org/ns/activitystreams#Public")); var stagingStore = new StagingEntityStore(_entityStore); var apo = await _flattener.FlattenAndStore(stagingStore, create); var handler = new CreateActorHandler(stagingStore, apo, null, null, User, _collectionTools, _entityData, _connection); await handler.Handle(); var resultUser = await _entityStore.GetEntity(handler.MainObject.Data["object"].First().Id, false); var outbox = await _entityStore.GetEntity(resultUser.Data["outbox"].First().Id, false); var delivery = new DeliveryHandler(stagingStore, handler.MainObject, resultUser, outbox, User, _collectionTools, _provider.GetRequiredService <DeliveryService>()); await delivery.Handle(); return(RedirectToAction("Edit", new { id = resultUser.Id })); }
public async Task FindTransparentPredicates(Dictionary <string, APEntity> objects, string actor) { var allObjectIds = _entityStore.FindAttributes(objects.Values.Select(a => a.Id).ToList()).Values.ToList(); var actorId = (await _entityStore.ReverseAttribute(actor, true)).Value; var objectAttr = (await _entityStore.ReverseAttribute("https://www.w3.org/ns/activitystreams#object", true)).Value; var inReplyToAttr = (await _entityStore.ReverseAttribute("https://www.w3.org/ns/activitystreams#inReplyTo", true)).Value; var actorAttr = (await _entityStore.ReverseAttribute("https://www.w3.org/ns/activitystreams#actor", true)).Value; var typeAttr = (await _entityStore.ReverseAttribute("rdf:type", true)).Value; var undoAttr = (await _entityStore.ReverseAttribute("https://www.w3.org/ns/activitystreams#Undo", true)).Value; var query = "select a.* from \"TripleEntities\" a where a.\"IsOwner\" = TRUE" + $" and exists(select 1 from \"Triples\" where \"PredicateId\" = @ActorAttr and \"AttributeId\" = @ActorId and \"SubjectId\" = a.\"IdId\" and \"SubjectEntityId\" = a.\"EntityId\")" + $" and exists(select 1 from \"Triples\" where \"PredicateId\" = @ObjectAttr and \"AttributeId\" = any(@Ids) and \"SubjectId\" = a.\"IdId\" and \"SubjectEntityId\" = a.\"EntityId\")"; var replyQuery = "select a.* from \"TripleEntities\" a where" + $" exists(select 1 from \"Triples\" where \"PredicateId\" = @ObjectAttr and \"AttributeId\" = any(@Ids) and \"SubjectId\" = a.\"IdId\" and \"SubjectEntityId\" = a.\"EntityId\")"; var entityShapes = await _connection.QueryAsync <APTripleEntity>(query, new { ActorAttr = actorAttr, ActorId = actorId, ObjectAttr = objectAttr, Ids = allObjectIds }); var undoneShapes = await _connection.QueryAsync <APTripleEntity>(query, new { ActorAttr = typeAttr, ActorId = undoAttr, ObjectAttr = objectAttr, Ids = entityShapes.Select(a => a.IdId).ToList() }); var replyShapes = await _connection.QueryAsync <APTripleEntity>(replyQuery, new { ObjectAttr = inReplyToAttr, Ids = allObjectIds }); Console.WriteLine(string.Join(", ", replyShapes.Select(a => a.EntityId.ToString()))); var properEntities = await _entityStore.GetEntities(entityShapes.Concat(undoneShapes).Concat(replyShapes).Select(a => a.EntityId).ToList()); var undoneObjects = properEntities.Where(a => a.Data.Type.Contains("https://www.w3.org/ns/activitystreams#Undo")).Select(a => a.Data["object"].First().Id).ToList(); var intactObjects = properEntities.Where(a => !undoneObjects.Contains(a.Id)).ToList(); foreach (var obj in intactObjects) { var idToUse = obj.Data["actor"].Any() ? obj.Data["object"].First().Id : obj.Data["inReplyTo"].First().Id; if (!objects.ContainsKey(idToUse)) { continue; } var target = objects[idToUse]; if (obj.Data.Type.Contains("https://www.w3.org/ns/activitystreams#Like")) { target.Data["c2s:likes"].Add(ASTerm.MakeSubObject(obj.Data)); } if (obj.Data.Type.Contains("https://www.w3.org/ns/activitystreams#Follow")) { target.Data["c2s:follows"].Add(ASTerm.MakeSubObject(obj.Data)); } if (obj.Data.Type.Contains("https://www.w3.org/ns/activitystreams#Announce")) { target.Data["c2s:announces"].Add(ASTerm.MakeSubObject(obj.Data)); } if (obj.Data.Type.Contains("https://www.w3.org/ns/activitystreams#Accept")) { target.Data["c2s:accepts"].Add(ASTerm.MakeSubObject(obj.Data)); } if (obj.Data.Type.Contains("https://www.w3.org/ns/activitystreams#Reject")) { target.Data["c2s:rejects"].Add(ASTerm.MakeSubObject(obj.Data)); } if (obj.Data["inReplyTo"].Any()) { target.Data["c2s:replies"].Add(ASTerm.MakeSubObject(obj.Data)); } } }
private ASObject _parseAuthor(XElement element) { var ao = new ASObject(); ao.Type.Add("https://www.w3.org/ns/activitystreams#Person"); // set preferredUsername and name { var atomName = element.Element(Atom + "name")?.Value; var pocoDisplayName = element.Element(PortableContacts + "displayName")?.Value; var pocoPreferredUsername = element.Element(PortableContacts + "preferredUsername")?.Value; ao.Replace("preferredUsername", ASTerm.MakePrimitive(pocoPreferredUsername ?? atomName)); ao.Replace("name", ASTerm.MakePrimitive(pocoDisplayName ?? pocoPreferredUsername ?? atomName)); } // set summary { var atomSummary = element.Element(Atom + "summary")?.Value; var pocoNote = element.Element(PortableContacts + "note")?.Value; ao.Replace("summary", ASTerm.MakePrimitive(pocoNote ?? atomSummary)); } 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; switch (rel) { case "avatar": var avatarObject = new ASObject(); avatarObject.Type.Add("https://www.w3.org/ns/activitystreams#Image"); avatarObject.Replace("mediaType", ASTerm.MakePrimitive(type)); var width = link.Attribute(AtomMedia + "width")?.Value; var height = link.Attribute(AtomMedia + "height")?.Value; if (width != null && height != null) { avatarObject.Replace("width", ASTerm.MakePrimitive(int.Parse(width))); avatarObject.Replace("height", ASTerm.MakePrimitive(int.Parse(height))); } avatarObject.Replace("url", ASTerm.MakePrimitive(href)); ao["icon"].Add(ASTerm.MakeSubObject(avatarObject)); break; case "alternate": if (type == "text/html") { if (retrievalUrl == null) { retrievalUrl = href; } ao["atomUri"].Add(ASTerm.MakePrimitive(href)); } break; case "self": if (type == "application/atom+xml") { retrievalUrl = href; } break; } } } // should be Mastodon *and* GNU social compatible: Mastodon uses uri as id if (element.Element(Atom + "id") != null) { ao.Id = element.Element(Atom + "id")?.Value; } else { ao.Id = element.Element(Atom + "uri")?.Value; } if (element.Element(Atom + "uri") != null) { ao["url"].Add(ASTerm.MakePrimitive(element.Element(Atom + "uri")?.Value)); } if (element.Element(Atom + "email") != null) { ao["email"].Add(ASTerm.MakePrimitive(element.Element(Atom + "email")?.Value)); } foreach (var url in element.Elements(PortableContacts + "urls")) { ao["url"].Add(ASTerm.MakePrimitive(url.Element(PortableContacts + "value")?.Value)); } if (retrievalUrl != null) { ao.Replace("atomUri", ASTerm.MakePrimitive(retrievalUrl)); } return(ao); }
private async Task <ASObject> _parseFeed(XElement element, string targetUser) { var ao = new ASObject(); ao.Type.Add("https://www.w3.org/ns/activitystreams#OrderedCollectionPage"); ao.Replace("_:origin", ASTerm.MakePrimitive("atom")); ao.Id = element.Element(Atom + "id").Value; if (element.Element(Atom + "title") != null) { ao.Replace("summary", ASTerm.MakePrimitive(element.Element(Atom + "title").Value)); } if (element.Element(Atom + "updated") != null) { ao.Replace("updated", ASTerm.MakePrimitive(element.Element(Atom + "updated").Value)); } var author = _parseAuthor(element.Element(Atom + "author")); ao.Replace("attributedTo", ASTerm.MakeSubObject(author)); var authorId = author.Id; foreach (var entry in element.Elements(Atom + "entry")) { ao["orderedItems"].Add(await _parseActivity(entry, authorId, targetUser)); } 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 == "alternate" && type == "text/html") { ao["url"].Add(ASTerm.MakePrimitive(href)); } else if (rel == "self" && type == "application/atom+xml") { author.Replace("atomUri", ASTerm.MakePrimitive(href)); } else { switch (rel) { case "salmon": ao["_:salmonUrl"].Add(ASTerm.MakePrimitive(href)); break; case "hub": ao["_:hubUrl"].Add(ASTerm.MakePrimitive(href)); break; case "prev": ao["prev"].Add(ASTerm.MakeId(href)); break; case "next": ao["next"].Add(ASTerm.MakeId(href)); break; } } } author["_:salmonUrl"].AddRange(ao["_:salmonUrl"]); author["_:hubUrl"].AddRange(ao["_:hubUrl"]); return(ao); }
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)); }
private async Task <ASTerm> _parseActivityObject(XElement element, string authorId, string targetUser, bool isActivity = false) { if (!isActivity && element.Element(ActivityStreams + "verb") != null) { return(await _parseActivity(element, authorId, targetUser)); } var entity = await _entityStore.GetEntity(element.Element(Atom + "id")?.Value, true); if (entity != null) { if (entity.Type.Contains("https://www.w3.org/ns/activitystreams#Create")) { return(ASTerm.MakeId(entity.Data["object"].First().Id)); } return(ASTerm.MakeId(element.Element(Atom + "id")?.Value)); } var ao = new ASObject(); ao.Id = element.Element(Atom + "id")?.Value + (isActivity ? "#object" : ""); ao.Replace("kroeg:origin", ASTerm.MakePrimitive("atom")); var objectType = _objectTypeToType(element.Element(ActivityStreams + "object-type")?.Value); if (objectType == "https://www.w3.org/ns/activitystreams#Person") { return(ASTerm.MakeSubObject(_parseAuthor(element))); } ao.Type.Add(objectType); ao.Replace("attributedTo", ASTerm.MakeId(authorId)); if (element.Element(Atom + "summary") != null) { ao.Replace("summary", ASTerm.MakePrimitive(element.Element(Atom + "summary")?.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)); } ao.Replace("content", ASTerm.MakePrimitive(element.Element(Atom + "content")?.Value)); var mediaType = element.Element(Atom + "content")?.Attribute(NoNamespace + "type")?.Value; if (mediaType != null) { if (mediaType == "text") { mediaType = "text/plain"; } if (mediaType.Contains("/")) { ao.Replace("mediaType", ASTerm.MakePrimitive(mediaType)); } } if (element.Element(OStatus + "conversation") != null) { ao.Replace("conversation", ASTerm.MakePrimitive(element.Element(OStatus + "conversation").Attribute(NoNamespace + "ref")?.Value ?? element.Element(OStatus + "conversation").Value)); } if (element.Element(AtomThreading + "in-reply-to") != null) { var elm = element.Element(AtomThreading + "in-reply-to"); var @ref = await _findInReplyTo(elm.Attribute(NoNamespace + "ref").Value); var hrel = elm.Attribute(NoNamespace + "href")?.Value; if (hrel == null) { ao.Replace("inReplyTo", ASTerm.MakeId(@ref)); } else if (await _entityStore.GetEntity(@ref, false) != null) { ao.Replace("inReplyTo", ASTerm.MakeId(@ref)); } else { var lazyLoad = new ASObject(); lazyLoad.Id = @ref; lazyLoad.Type.Add("_:LazyLoad"); lazyLoad.Replace("href", ASTerm.MakePrimitive(hrel)); ao.Replace("inReplyTo", ASTerm.MakeSubObject(lazyLoad)); } } foreach (var tag in element.Elements(Atom + "category")) { var val = tag.Attribute(NoNamespace + "term").Value; var tagao = new ASObject(); tagao.Id = $"{_entityConfiguration.BaseUri}/tag/{val}"; tagao["name"].Add(ASTerm.MakePrimitive("#" + val)); tagao.Type.Add("Hashtag"); ao["tag"].Add(ASTerm.MakeSubObject(tagao)); } 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)); } else if (rel == "enclosure") { // image var subAo = new ASObject(); subAo["url"].Add(ASTerm.MakePrimitive(href)); subAo["mediaType"].Add(ASTerm.MakePrimitive(type)); switch (type.Split('/')[0]) { case "image": subAo.Type.Add("https://www.w3.org/ns/activitystreams#Image"); break; case "video": subAo.Type.Add("https://www.w3.org/ns/activitystreams#Video"); break; default: continue; } if (link.Attribute(NoNamespace + "length") != null) { subAo["fileSize"].Add(ASTerm.MakePrimitive(int.Parse(link.Attribute(NoNamespace + "length").Value))); } ao["attachment"].Add(ASTerm.MakeSubObject(subAo)); } } return(ASTerm.MakeSubObject(ao)); }
public async Task <IActionResult> DoRegister(RegisterViewModel model) { if (!_configuration.GetSection("Kroeg").GetValue <bool>("CanRegister")) { return(NotFound()); } var apuser = new APUser { Username = model.Username, Email = model.Email }; if ((await _relevantEntities.FindEntitiesWithPreferredUsername(model.Username)).Count > 0) { ModelState.AddModelError("", "Username is already in use!"); } if (model.Password != model.VerifyPassword) { ModelState.AddModelError("", "Passwords don't match!"); } if (!ModelState.IsValid) { return(View("Register", model)); } await _connection.OpenAsync(); using (var trans = _connection.BeginTransaction()) { var result = await _userManager.CreateAsync(apuser, model.Password); if (!result.Succeeded) { ModelState.AddModelError("", result.Errors.First().Description); } if (!ModelState.IsValid) { return(View("Register", model)); } if (await _connection.ExecuteAsync("select count(*) from \"Users\"") == 1) { // await _userManager.AddClaimAsync(apuser, new Claim("admin", "true")); } await _signInManager.SignInAsync(apuser, false); var user = model.Username; var obj = new ASObject(); obj.Type.Add("https://www.w3.org/ns/activitystreams#Person"); obj["preferredUsername"].Add(ASTerm.MakePrimitive(user)); obj["name"].Add(ASTerm.MakePrimitive(user)); var create = new ASObject(); create.Type.Add("https://www.w3.org/ns/activitystreams#Create"); create["object"].Add(ASTerm.MakeSubObject(obj)); create["to"].Add(ASTerm.MakeId("https://www.w3.org/ns/activitystreams#Public")); Console.WriteLine($"--- creating actor. Unflattened:\n{create.Serialize().ToString(Formatting.Indented)}"); var apo = await _entityFlattener.FlattenAndStore(_entityStore, create); Console.WriteLine($"Flat: {apo.Data.Serialize().ToString(Formatting.Indented)}\n----"); var handler = new CreateActorHandler(_entityStore, apo, null, null, User, _collectionTools, _entityConfiguration, _connection); handler.UserOverride = apuser.Id; await handler.Handle(); var resultUser = await _entityStore.GetEntity(handler.MainObject.Data["object"].First().Id, false); var outbox = await _entityStore.GetEntity(resultUser.Data["outbox"].First().Id, false); var delivery = new DeliveryHandler(_entityStore, handler.MainObject, resultUser, outbox, User, _collectionTools, _provider.GetRequiredService <DeliveryService>()); await delivery.Handle(); trans.Commit(); return(RedirectToActionPermanent("Index", "Settings")); } }