123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221(*********************************************************************************)(* OCaml-ActivityPub *)(* *)(* Copyright (C) 2023-2024 INRIA All rights reserved. *)(* Author: Maxence Guesdon, INRIA Saclay *)(* *)(* This program is free software; you can redistribute it and/or modify *)(* it under the terms of the GNU Lesser General Public License version *)(* 3 as published by the Free Software Foundation. *)(* *)(* 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 General Public License for more details. *)(* *)(* You should have received a copy of the GNU General Public License *)(* along with this program; if not, write to the Free Software *)(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA *)(* 02111-1307 USA *)(* *)(* Contact: maxence.guesdon@inria.fr *)(* *)(*********************************************************************************)(** Posting activities *)moduleAP=ActivitypubmoduleLog=AP.LogmoduleAS=Rdf.ActivitypubtypeAP.E.error+=|Accept_no_actorofIri.t(** When an Accept activity is POSTed without an actor object *)let()=AP.E.register_string_of_error(functionAccept_no_actoriri->letmsg=Printf.sprintf"Accepting %s: no actor to send to"(Iri.to_stringiri)inSomemsg|_->None)typepost_result=(Iri.toption,AP.E.error)resultLwt.ttypeaudience_post_fun=?to_:Iri.tlist->?bto:Iri.tlist->?cc:Iri.tlist->?bcc:Iri.tlist->?audience:Iri.t->?public:bool->unit->post_resulttypeaccept_fun=AP.Types.activity->post_resulttypeannounce_fun=Iri.t->audience_post_funtypecreate_fun=Rdf.Graph.graph->Rdf.Term.term->?in_reply_to:Iri.tlist->audience_post_funtypedelete_fun=Iri.t->post_resulttypedislike_fun=Iri.t->post_resulttypefollow_fun=Iri.t->post_resulttypelike_fun=Iri.t->post_resulttypeundo_fun=Iri.t->post_resulttypeupdate_fun=Iri.t->Rdf.Graph.graph->post_result(** This is the signature of the module we get after applying the
{!Make} functor on an {!Object.T}: functions to POST activities, using
module [O] to act as a given actor. *)moduletypeT=sigmoduleO:Object.Tvalaccept:accept_funvalannounce:announce_funvalcreate:create_funvaldelete:delete_funvaldislike:dislike_funvalfollow:follow_funvallike:like_funvalundo:undo_funvalupdate:update_funendmoduleMake(O:Object.T):T=structmoduleO=Oletactor()=leta=O.of_iriO.actor_iriinlet%lwt()=a#dereferenceinLwt.returna#as_actorletoutboxactor=let%lwt()=actor#dereferenceinLwt.returnactor#outboxletpost_activity~actorgroot=let%lwtob=outboxactorinLog.debug(funm->m"Posting activity to %a:\n%s"Iri.ppob#iri(Rdf.Nq.graph_to_stringg));((* add published date if not present for root *)matchRdf.Graph.literal_objects_ofg~sub:root~pred:AS.publishedwith|_::_->()|[]->letd=Rdf.Term.term_of_datetime()ing.add_triple~sub:root~pred:AS.published~obj:d);List.iterg.rem_triple_t(g.find~sub:root~pred:AS.actor());g.add_triple~sub:root~pred:AS.actor~obj:actor#id;O.postob#irigletacceptobj=Log.debug(funm->m"Acti.accept iri=%a"Iri.ppobj#iri);let%lwt()=obj#dereferenceinmatchobj#actorwith|None->Lwt.return_error(Accept_no_actorobj#iri)|Someto_->letto_=AP.Types.iri_of_loto_inlet%lwtactor=actor()inletg=Rdf.Graph.open_graph(Iri.of_string"")inletsub=Rdf.Term.blank_(g.new_blank_id())ing.add_triple~sub~pred:Rdf.Rdf_.type_~obj:(Rdf.Term.IriAS.c_Accept);g.add_triple~sub~pred:AS.actor~obj:actor#id;g.add_triple~sub~pred:AS.object_~obj:(Rdf.Term.Iriobj#iri);g.add_triple~sub~pred:AS.to_~obj:(Rdf.Term.Irito_);post_activity~actorgsubletannounceiri?(to_=[])?(bto=[])?(cc=[])?(bcc=[])?audience?(public=true)()=let%lwtactor=actor()inletg=Rdf.Graph.open_graph(Iri.of_string"")inletsub=Rdf.Term.blank_(g.new_blank_id())ing.add_triple~sub~pred:Rdf.Rdf_.type_~obj:(Rdf.Term.IriAS.c_Announce);g.add_triple~sub~pred:AS.actor~obj:actor#id;g.add_triple~sub~pred:AS.object_~obj:(Rdf.Term.Iriiri);letaddprediri=g.add_triple~sub~pred~obj:(Rdf.Term.Iriiri)inifpublicthenaddAS.ccAS.c_Public;List.iter(addAS.to_)to_;List.iter(addAS.bto)bto;List.iter(addAS.cc)cc;List.iter(addAS.bcc)bcc;Option.iter(addAS.audience)audience;post_activity~actorgsubletcreategroot?(in_reply_to=[])?(to_=[])?(bto=[])?(cc=[])?(bcc=[])?audience?(public=true)()=let%lwtactor=actor()inletg=g.Rdf.Graph.copy()inletsub=Rdf.Term.blank_(g.new_blank_id())ing.add_triple~sub~pred:Rdf.Rdf_.type_~obj:(Rdf.Term.IriAS.c_Create);g.add_triple~sub~pred:AS.actor~obj:actor#id;g.add_triple~sub~pred:AS.object_~obj:root;letadd_bothpredobj_iri=letobj=Rdf.Term.Iriobj_iriing.add_triple~sub~pred~obj;g.add_triple~sub:root~pred~obj;inifpublicthenadd_bothAS.ccAS.c_Public;List.iter(add_bothAS.to_)to_;List.iter(add_bothAS.bto)bto;List.iter(add_bothAS.cc)cc;List.iter(add_bothAS.bcc)bcc;Option.iter(add_bothAS.audience)audience;List.iter(funiri->g.add_triple~sub:root~pred:AS.inReplyTo~obj:(Rdf.Term.Iriiri))in_reply_to;letpublished=matchRdf.Graph.literal_objects_ofg~sub:root~pred:AS.publishedwith|t::_->Rdf.Term.Literalt|[]->letd=Rdf.Term.term_of_datetime()ing.add_triple~sub:root~pred:AS.published~obj:d;ding.add_triple~sub~pred:AS.published~obj:published;post_activity~actorgsubletdeleteiri=let%lwtactor=actor()inletg=Rdf.Graph.open_graph(Iri.of_string"")inletsub=Rdf.Term.blank_(g.new_blank_id())ing.add_triple~sub~pred:Rdf.Rdf_.type_~obj:(Rdf.Term.IriAS.c_Delete);g.add_triple~sub~pred:AS.actor~obj:actor#id;g.add_triple~sub~pred:AS.object_~obj:(Rdf.Term.Iriiri);post_activity~actorgsubletdislikeiri=let%lwtactor=actor()inletg=Rdf.Graph.open_graph(Iri.of_string"")inletsub=Rdf.Term.blank_(g.new_blank_id())ing.add_triple~sub~pred:Rdf.Rdf_.type_~obj:(Rdf.Term.IriAS.c_Dislike);g.add_triple~sub~pred:AS.actor~obj:actor#id;g.add_triple~sub~pred:AS.object_~obj:(Rdf.Term.Iriiri);post_activity~actorgsubletfollowiri=let%lwtactor=actor()inletg=Rdf.Graph.open_graph(Iri.of_string"")inletsub=Rdf.Term.blank_(g.new_blank_id())ing.add_triple~sub~pred:Rdf.Rdf_.type_~obj:(Rdf.Term.IriAS.c_Follow);g.add_triple~sub~pred:AS.actor~obj:actor#id;g.add_triple~sub~pred:AS.object_~obj:(Rdf.Term.Iriiri);post_activity~actorgsubletlikeiri=let%lwtactor=actor()inletg=Rdf.Graph.open_graph(Iri.of_string"")inletsub=Rdf.Term.blank_(g.new_blank_id())ing.add_triple~sub~pred:Rdf.Rdf_.type_~obj:(Rdf.Term.IriAS.c_Like);g.add_triple~sub~pred:AS.actor~obj:actor#id;g.add_triple~sub~pred:AS.object_~obj:(Rdf.Term.Iriiri);post_activity~actorgsubletundoiri=let%lwtactor=actor()inletg=Rdf.Graph.open_graph(Iri.of_string"")inletsub=Rdf.Term.blank_(g.new_blank_id())ing.add_triple~sub~pred:Rdf.Rdf_.type_~obj:(Rdf.Term.IriAS.c_Undo);g.add_triple~sub~pred:AS.actor~obj:actor#id;g.add_triple~sub~pred:AS.object_~obj:(Rdf.Term.Iriiri);post_activity~actorgsubletupdateirig=let%lwtactor=actor()inletg=g.Rdf.Graph.copy()inletsub=Rdf.Term.blank_(g.new_blank_id())ing.add_triple~sub~pred:Rdf.Rdf_.type_~obj:(Rdf.Term.IriAS.c_Update);g.add_triple~sub~pred:AS.actor~obj:actor#id;g.add_triple~sub~pred:AS.object_~obj:(Rdf.Term.Iriiri);post_activity~actorgsubend