123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- (* has an alloc, but part of ocaml >= 4.13 anyway *)
- let starts_with ~prefix s =
- let lp = prefix |> String.length in
- lp <= (s |> String.length)
- && (prefix |> String.equal (String.sub s 0 lp))
- let string_prefix ~pre = starts_with ~prefix:pre
- open Common
- let decode_string enc vl = D.decode_string enc vl
- |> Result.map_error D.string_of_error
- let uri dec =
- Result.bind
- (D.string dec)
- (fun s -> D.succeed (s |> Uri.of_string) dec)
- let collection_page obj =
- let open D in
- let* () = field "type" @@ constant ~msg:"Expected OrderedCollectionPage (received %s)" "OrderedCollectionPage"
- and* id = field "id" uri
- and* next = field_opt "next" uri
- and* first = field_opt "first" uri
- and* last = field_opt "last" uri
- and* current = field_opt "current" uri
- and* prev = field_opt "prev" uri
- and* part_of = field_opt "partOf" uri
- and* total_items = field_opt "totalItems" int
- and* (is_ordered, items) = items obj in
- succeed ({id;
- current;
- first;
- is_ordered;
- items;
- last;
- next;
- part_of;
- prev;
- total_items;
- }: _ Types.collection_page)
- let collection obj =
- let open D in
- let* () = field "type" @@ constant ~msg:"Expected OrderedCollection (received %s)" "OrderedCollection"
- and* id = field "id" uri
- and* first = field_opt "first" uri
- and* last = field_opt "last" uri
- and* current = field_opt "current" uri
- and* total_items = field_opt "totalItems" int
- and* items' = items_opt obj in
- let (is_ordered,items) = match items' with
- | Some (b,l) -> (b,Some l)
- | None -> (false,None) in
- succeed ({id;
- current;
- first;
- is_ordered;
- items;
- last;
- total_items;
- }: _ Types.collection)
- let mention =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected Mention (received %s)" "Mention"
- and* href = field "href" uri
- and* name = field "name" string in
- succeed ({ty=`Mention; href;name} : Types.tag)
- let hashtag =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected Hashtag (received %s)" "Hashtag"
- and* href = field "href" uri
- and* name = field "name" string in
- succeed ({ty=`Hashtag; href;name}: Types.tag)
- let tag =
- let open D in
- let* ty = field "type" string in
- match ty with
- | "Mention" -> mention
- | "Hashtag" -> hashtag
- | _ -> fail (Printf.sprintf "unknown tag %s" ty)
- let undo obj =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected Undo (received %s)" "Undo"
- and* id = field "id" uri
- and* actor = field "actor" uri
- and* published = field_opt "published" rfc3339
- and* obj = field "object" obj
- (* and* raw = value *) in
- succeed ({id;published;actor;obj(*;raw*)}: _ Types.undo)
- let like =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected Like (received %s)" "Like"
- and* id = field "id" uri
- and* actor = field "actor" uri
- and* published = field_opt "published" rfc3339
- and* obj = field "object" uri
- (* and* raw = value *) in
- succeed ({id; actor; published; obj (*; raw*)}: Types.like)
- let tombstone =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected Tombstone (received %s)" "Tombstone"
- and* id = field "id" uri in
- succeed id
- let delete obj =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected Delete (received %s)" "Delete"
- and* id = field "id" uri
- and* actor = field "actor" uri
- and* published = field_opt "published" rfc3339
- and* obj = field "object" obj
- (* and* raw = value *) in
- succeed ({id;published;actor;obj(*;raw*)}: _ Types.delete)
- let block =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected Block (received %s)" "Block"
- and* id = field "id" uri
- and* obj = field "object" uri
- and* published = field_opt "published" rfc3339
- and* actor = field "actor" uri
- (* and* raw = value *) in
- succeed ({id;published;obj;actor(*;raw*)}: Types.block)
- let accept obj =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected Accept (received %s)" "Accept"
- and* id = field "id" uri
- and* actor = field "actor" uri
- and* published = field_opt "published" rfc3339
- and* obj = field "object" obj
- (*and* raw = value *) in
- succeed ({id;published;actor;obj(*;raw*)}: _ Types.accept)
- (* https://www.w3.org/TR/activitystreams-vocabulary/#dfn-link *)
- let link =
- let open D in
- (*
- let* () = field "type" @@ constant ~msg:"expected Link (received %s)" "Link"
- and* href = field "href" uri
- and* name = field_opt "name" string in
- let name_map = []
- and rel = None in
- succeed (Some ({href;name;name_map;rel}: Types.link))
- *)
- succeed None
- let public_key =
- let open D in
- let* id = field "id" uri
- and* owner = field_opt "owner" uri
- and* pem = field "publicKeyPem" string
- and* signatureAlgorithm = field_opt "signatureAlgorithm" string in
- succeed ({id;owner;pem;signatureAlgorithm}: Types.public_key)
- let property_value =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected PropertyValue (received %s)" "PropertyValue" in
- let* name = field "name" string
- and* name_map = field_or_default "nameMap" (key_value_pairs string) []
- and* value = field "value" string
- and* value_map = field_or_default "valueMap" (key_value_pairs string) [] in
- succeed ({name;name_map;value;value_map}: Types.property_value)
- let attachment =
- let open D in
- let* media_type = field_opt "mediaType" string
- and* name = field_opt "name" string
- and* type_ = field_opt "type" string
- and* url = field "url" uri in
- succeed ({media_type;name;type_;url}: Types.attachment)
- let person =
- let open D in
- (* how would we get the default @language from the @context? *)
- let* () = one_of [
- "type", field "type" @@ constant ~msg:"expected Person (received %s)" "Person";
- (* pleroma uses type='service' at times. *)
- "type", field "type" @@ constant ~msg:"expected Service (received %s)" "Service";
- ]
- and* id = field "id" uri
- and* name = field_or_default "name" (nullable string) None
- and* name_map = field_or_default "nameMap" (key_value_pairs string) []
- and* url = field_or_default "url" (singleton_or_list uri) []
- and* preferred_username = field_opt "preferredUsername" string
- and* preferred_username_map = field_or_default "preferredUsernameMap" (key_value_pairs string) []
- and* inbox = field "inbox" uri
- and* outbox = field "outbox" uri
- and* summary = field_opt "summary" string
- and* summary_map = field_or_default "summaryMap" (key_value_pairs string) []
- and* public_key = field "publicKey" public_key
- and* published = field_opt "published" rfc3339
- and* manually_approves_followers = field_or_default "manuallyApprovesFollowers" bool false
- and* discoverable = field_or_default "discoverable" bool false
- and* generator = field_or_default "generator" link None
- and* followers = field_opt "followers" uri
- and* following = field_opt "following" uri
- and* attachment = field_or_default "attachment" (list_ignoring_unknown property_value) []
- and* icon = maybe (at ["icon";"url"] uri)
- and* image = maybe (at ["image";"url"] uri)
- (* and* raw = value *) in
- succeed ({
- id;
- inbox;
- outbox;
- followers;
- following;
- name; name_map;
- url;
- preferred_username; preferred_username_map;
- summary; summary_map;
- public_key;
- published;
- manually_approves_followers;
- discoverable;
- generator;
- attachment;
- icon;
- image;
- (* raw; *)
- }: Types.person)
- let note =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected Note (received %s)" "Note"
- and* id = field "id" uri
- and* actor = one_of ["actor", field "actor" uri; "attributed_to", field "attributedTo" uri]
- and* attachment = field_or_default "attachment" (singleton_or_list attachment) []
- and* to_ = field "to" (singleton_or_list uri)
- and* in_reply_to = field_or_default "inReplyTo" (singleton_or_list uri) []
- and* cc = field_or_default "cc" (singleton_or_list uri) []
- and* content = field "content" string
- and* content_map = field_or_default "contentMap" (key_value_pairs string) []
- and* source = field_opt "source"
- (one_of ["string", uri; "multi-encode", field "content" uri])
- and* summary = field_or_default "summary" (nullable string) None
- and* summary_map = field_or_default "summaryMap" (key_value_pairs string) []
- and* sensitive = field_or_default "sensitive" (nullable bool) None
- and* media_type = field_opt "mediaType" string
- and* published = field_opt "published" rfc3339
- and* tags = field_or_default "tag" (list_ignoring_unknown tag) []
- and* url = field "url" (singleton_or_list uri)
- (* and* raw = value *) in
- succeed ({ id; actor; attachment; in_reply_to; to_; cc;
- sensitive=Option.value ~default:false sensitive;
- media_type; content; content_map; source; summary; summary_map; tags; published; url(*; raw*) }: Types.note)
- let follow =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected create object (received %s)" "Follow"
- and* actor = field "actor" uri
- and* cc = field_or_default "cc" (singleton_or_list uri) []
- and* to_ = field_or_default "to" (singleton_or_list uri) []
- and* id = field "id" uri
- and* object_ = field "object" uri
- and* state = field_opt "state" (string >>= function "pending" -> succeed `Pending
- | "cancelled" -> succeed `Cancelled
- | _ -> fail "unknown status")
- (* and* raw = value *) in
- succeed ({actor; cc; to_; id; object_; state(*; raw*)}: Types.follow)
- let announce obj =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected create object (received %s)" "Announce"
- and* actor = field "actor" uri
- and* id = field "id" uri
- and* published = field_opt "published" rfc3339
- and* to_ = field "to" (singleton_or_list uri)
- and* cc = field_or_default "cc" (singleton_or_list uri) []
- and* obj = field "object" obj
- (* and* raw = value *) in
- succeed ({id; published; actor; to_; cc; obj(* ; raw*)}: _ Types.announce)
- let create obj =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected create object (received %s)" "Create"
- and* id = field "id" uri
- and* actor = field "actor" uri
- and* direct_message = field_or_default "direct" bool false
- and* published = field_opt "published" rfc3339
- and* to_ = field_or_default "to" (singleton_or_list uri) []
- and* cc = field_or_default "cc" (singleton_or_list uri) []
- and* obj = field "object" obj
- (* and* raw = value *) in
- succeed ({
- id; actor; published;
- to_; cc;
- direct_message;
- obj;
- (*raw;*)
- }: _ Types.create)
- let update obj =
- let open D in
- let* () = field "type" @@ constant ~msg:"expected create object (received %s)" "Update"
- and* id = field "id" uri
- and* actor = field "actor" uri
- and* direct_message = field_or_default "direct" bool false
- and* published = field_opt "published" rfc3339
- and* to_ = field_or_default "to" (singleton_or_list uri) []
- and* cc = field_or_default "cc" (singleton_or_list uri) []
- and* obj = field "object" obj
- (* and* raw = value *) in
- succeed ({
- id; actor; published;
- to_; cc;
- direct_message;
- obj;
- (*raw;*)
- }: _ Types.update)
- let core_obj () =
- let open D in
- let* ty = field_opt "type" string in
- match ty with
- | Some "Person" -> person >|= fun v -> `Person v
- | Some "Follow" -> follow >|= fun v -> `Follow v
- | Some "Note" -> note >|= fun v -> `Note v
- | Some "Block" -> block >|= fun v -> `Block v
- | Some "Like" -> like >|= fun v -> `Like v
- | None -> string >|= fun v -> `Link v
- | Some ev -> fail ("unsupported event" ^ ev)
- let core_obj = core_obj ()
- let event (enc: Types.core_obj D.decoder) : Types.obj D.decoder =
- let open D in
- let* ty = field "type" string in
- match ty with
- | "Accept" -> accept enc >|= fun v -> `Accept v
- | "Announce" -> announce enc >|= fun v -> `Announce v
- | "Create" -> create enc >|= fun v -> `Create v
- | "Update" -> update enc >|= fun v -> `Update v
- | "Delete" -> delete enc >|= fun v -> `Delete v
- | "Undo" -> undo enc >|= fun v -> `Undo v
- | _ -> fail "unsupported event"
- let obj : Types.obj D.decoder =
- D.one_of [
- "core_obj", core_obj;
- "core_obj event", (event core_obj)
- ]
- module Webfinger = struct
- let ty =
- let open D in
- string >>= function
- | str when string_prefix ~pre:Constants.ContentType.text_html str -> succeed `Html
- | str when string_prefix ~pre:Constants.ContentType.app_json str -> succeed `Json
- | str when string_prefix ~pre:Constants.ContentType._app_act_json str -> succeed `ActivityJson_
- | str when string_prefix ~pre:Constants.ContentType.app_jlda str -> succeed `ActivityJsonLd
- | _ -> fail "unsupported self link type"
- let self =
- let open D in
- let* ty = field "type" ty
- and* href = field "href" uri in
- succeed @@ Types.Webfinger.Self (ty, href)
- let profile_page =
- let open D in
- let* ty = field "type" ty
- and* href = field "href" uri in
- succeed @@ Types.Webfinger.ProfilePage (ty, href)
- let ostatus_subscribe =
- let open D in
- let* template = field "template" string in
- succeed @@ Types.Webfinger.OStatusSubscribe template
- let link =
- let open D in
- let* rel = field "rel" string in
- match rel with
- | "self" -> self
- | str when String.equal str Constants.Webfinger.ostatus_rel ->
- ostatus_subscribe
- | str when String.equal str Constants.Webfinger.profile_page ->
- profile_page
- | _ -> fail "unsupported link relation"
- let query_result =
- let open D in
- let* subject = field "subject" string
- and* aliases = field_or_default "aliases" (list string) []
- and* links = field "links" (list_ignoring_unknown link) in
- succeed Types.Webfinger.{subject;aliases;links}
- end
|