decode.ml 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. (* has an alloc, but part of ocaml >= 4.13 anyway *)
  2. let starts_with ~prefix s =
  3. let lp = prefix |> String.length in
  4. lp <= (s |> String.length)
  5. && (prefix |> String.equal (String.sub s 0 lp))
  6. let string_prefix ~pre = starts_with ~prefix:pre
  7. open Common
  8. let decode_string enc vl = D.decode_string enc vl
  9. |> Result.map_error D.string_of_error
  10. let uri dec =
  11. Result.bind
  12. (D.string dec)
  13. (fun s -> D.succeed (s |> Uri.of_string) dec)
  14. let collection_page obj =
  15. let open D in
  16. let* () = field "type" @@ constant ~msg:"Expected OrderedCollectionPage (received %s)" "OrderedCollectionPage"
  17. and* id = field "id" uri
  18. and* next = field_opt "next" uri
  19. and* first = field_opt "first" uri
  20. and* last = field_opt "last" uri
  21. and* current = field_opt "current" uri
  22. and* prev = field_opt "prev" uri
  23. and* part_of = field_opt "partOf" uri
  24. and* total_items = field_opt "totalItems" int
  25. and* (is_ordered, items) = items obj in
  26. succeed ({id;
  27. current;
  28. first;
  29. is_ordered;
  30. items;
  31. last;
  32. next;
  33. part_of;
  34. prev;
  35. total_items;
  36. }: _ Types.collection_page)
  37. let collection obj =
  38. let open D in
  39. let* () = field "type" @@ constant ~msg:"Expected OrderedCollection (received %s)" "OrderedCollection"
  40. and* id = field "id" uri
  41. and* first = field_opt "first" uri
  42. and* last = field_opt "last" uri
  43. and* current = field_opt "current" uri
  44. and* total_items = field_opt "totalItems" int
  45. and* items' = items_opt obj in
  46. let (is_ordered,items) = match items' with
  47. | Some (b,l) -> (b,Some l)
  48. | None -> (false,None) in
  49. succeed ({id;
  50. current;
  51. first;
  52. is_ordered;
  53. items;
  54. last;
  55. total_items;
  56. }: _ Types.collection)
  57. let mention =
  58. let open D in
  59. let* () = field "type" @@ constant ~msg:"expected Mention (received %s)" "Mention"
  60. and* href = field "href" uri
  61. and* name = field "name" string in
  62. succeed ({ty=`Mention; href;name} : Types.tag)
  63. let hashtag =
  64. let open D in
  65. let* () = field "type" @@ constant ~msg:"expected Hashtag (received %s)" "Hashtag"
  66. and* href = field "href" uri
  67. and* name = field "name" string in
  68. succeed ({ty=`Hashtag; href;name}: Types.tag)
  69. let tag =
  70. let open D in
  71. let* ty = field "type" string in
  72. match ty with
  73. | "Mention" -> mention
  74. | "Hashtag" -> hashtag
  75. | _ -> fail (Printf.sprintf "unknown tag %s" ty)
  76. let undo obj =
  77. let open D in
  78. let* () = field "type" @@ constant ~msg:"expected Undo (received %s)" "Undo"
  79. and* id = field "id" uri
  80. and* actor = field "actor" uri
  81. and* published = field_opt "published" rfc3339
  82. and* obj = field "object" obj
  83. (* and* raw = value *) in
  84. succeed ({id;published;actor;obj(*;raw*)}: _ Types.undo)
  85. let like =
  86. let open D in
  87. let* () = field "type" @@ constant ~msg:"expected Like (received %s)" "Like"
  88. and* id = field "id" uri
  89. and* actor = field "actor" uri
  90. and* published = field_opt "published" rfc3339
  91. and* obj = field "object" uri
  92. (* and* raw = value *) in
  93. succeed ({id; actor; published; obj (*; raw*)}: Types.like)
  94. let tombstone =
  95. let open D in
  96. let* () = field "type" @@ constant ~msg:"expected Tombstone (received %s)" "Tombstone"
  97. and* id = field "id" uri in
  98. succeed id
  99. let delete obj =
  100. let open D in
  101. let* () = field "type" @@ constant ~msg:"expected Delete (received %s)" "Delete"
  102. and* id = field "id" uri
  103. and* actor = field "actor" uri
  104. and* published = field_opt "published" rfc3339
  105. and* obj = field "object" obj
  106. (* and* raw = value *) in
  107. succeed ({id;published;actor;obj(*;raw*)}: _ Types.delete)
  108. let block =
  109. let open D in
  110. let* () = field "type" @@ constant ~msg:"expected Block (received %s)" "Block"
  111. and* id = field "id" uri
  112. and* obj = field "object" uri
  113. and* published = field_opt "published" rfc3339
  114. and* actor = field "actor" uri
  115. (* and* raw = value *) in
  116. succeed ({id;published;obj;actor(*;raw*)}: Types.block)
  117. let accept obj =
  118. let open D in
  119. let* () = field "type" @@ constant ~msg:"expected Accept (received %s)" "Accept"
  120. and* id = field "id" uri
  121. and* actor = field "actor" uri
  122. and* published = field_opt "published" rfc3339
  123. and* obj = field "object" obj
  124. (*and* raw = value *) in
  125. succeed ({id;published;actor;obj(*;raw*)}: _ Types.accept)
  126. (* https://www.w3.org/TR/activitystreams-vocabulary/#dfn-link *)
  127. let link =
  128. let open D in
  129. (*
  130. let* () = field "type" @@ constant ~msg:"expected Link (received %s)" "Link"
  131. and* href = field "href" uri
  132. and* name = field_opt "name" string in
  133. let name_map = []
  134. and rel = None in
  135. succeed (Some ({href;name;name_map;rel}: Types.link))
  136. *)
  137. succeed None
  138. let public_key =
  139. let open D in
  140. let* id = field "id" uri
  141. and* owner = field_opt "owner" uri
  142. and* pem = field "publicKeyPem" string
  143. and* signatureAlgorithm = field_opt "signatureAlgorithm" string in
  144. succeed ({id;owner;pem;signatureAlgorithm}: Types.public_key)
  145. let property_value =
  146. let open D in
  147. let* () = field "type" @@ constant ~msg:"expected PropertyValue (received %s)" "PropertyValue" in
  148. let* name = field "name" string
  149. and* name_map = field_or_default "nameMap" (key_value_pairs string) []
  150. and* value = field "value" string
  151. and* value_map = field_or_default "valueMap" (key_value_pairs string) [] in
  152. succeed ({name;name_map;value;value_map}: Types.property_value)
  153. let attachment =
  154. let open D in
  155. let* media_type = field_opt "mediaType" string
  156. and* name = field_opt "name" string
  157. and* type_ = field_opt "type" string
  158. and* url = field "url" uri in
  159. succeed ({media_type;name;type_;url}: Types.attachment)
  160. let person =
  161. let open D in
  162. (* how would we get the default @language from the @context? *)
  163. let* () = one_of [
  164. "type", field "type" @@ constant ~msg:"expected Person (received %s)" "Person";
  165. (* pleroma uses type='service' at times. *)
  166. "type", field "type" @@ constant ~msg:"expected Service (received %s)" "Service";
  167. ]
  168. and* id = field "id" uri
  169. and* name = field_or_default "name" (nullable string) None
  170. and* name_map = field_or_default "nameMap" (key_value_pairs string) []
  171. and* url = field_or_default "url" (singleton_or_list uri) []
  172. and* preferred_username = field_opt "preferredUsername" string
  173. and* preferred_username_map = field_or_default "preferredUsernameMap" (key_value_pairs string) []
  174. and* inbox = field "inbox" uri
  175. and* outbox = field "outbox" uri
  176. and* summary = field_opt "summary" string
  177. and* summary_map = field_or_default "summaryMap" (key_value_pairs string) []
  178. and* public_key = field "publicKey" public_key
  179. and* published = field_opt "published" rfc3339
  180. and* manually_approves_followers = field_or_default "manuallyApprovesFollowers" bool false
  181. and* discoverable = field_or_default "discoverable" bool false
  182. and* generator = field_or_default "generator" link None
  183. and* followers = field_opt "followers" uri
  184. and* following = field_opt "following" uri
  185. and* attachment = field_or_default "attachment" (list_ignoring_unknown property_value) []
  186. and* icon = maybe (at ["icon";"url"] uri)
  187. and* image = maybe (at ["image";"url"] uri)
  188. (* and* raw = value *) in
  189. succeed ({
  190. id;
  191. inbox;
  192. outbox;
  193. followers;
  194. following;
  195. name; name_map;
  196. url;
  197. preferred_username; preferred_username_map;
  198. summary; summary_map;
  199. public_key;
  200. published;
  201. manually_approves_followers;
  202. discoverable;
  203. generator;
  204. attachment;
  205. icon;
  206. image;
  207. (* raw; *)
  208. }: Types.person)
  209. let note =
  210. let open D in
  211. let* () = field "type" @@ constant ~msg:"expected Note (received %s)" "Note"
  212. and* id = field "id" uri
  213. and* actor = one_of ["actor", field "actor" uri; "attributed_to", field "attributedTo" uri]
  214. and* attachment = field_or_default "attachment" (singleton_or_list attachment) []
  215. and* to_ = field "to" (singleton_or_list uri)
  216. and* in_reply_to = field_or_default "inReplyTo" (singleton_or_list uri) []
  217. and* cc = field_or_default "cc" (singleton_or_list uri) []
  218. and* content = field "content" string
  219. and* content_map = field_or_default "contentMap" (key_value_pairs string) []
  220. and* source = field_opt "source"
  221. (one_of ["string", uri; "multi-encode", field "content" uri])
  222. and* summary = field_or_default "summary" (nullable string) None
  223. and* summary_map = field_or_default "summaryMap" (key_value_pairs string) []
  224. and* sensitive = field_or_default "sensitive" (nullable bool) None
  225. and* media_type = field_opt "mediaType" string
  226. and* published = field_opt "published" rfc3339
  227. and* tags = field_or_default "tag" (list_ignoring_unknown tag) []
  228. and* url = field "url" (singleton_or_list uri)
  229. (* and* raw = value *) in
  230. succeed ({ id; actor; attachment; in_reply_to; to_; cc;
  231. sensitive=Option.value ~default:false sensitive;
  232. media_type; content; content_map; source; summary; summary_map; tags; published; url(*; raw*) }: Types.note)
  233. let follow =
  234. let open D in
  235. let* () = field "type" @@ constant ~msg:"expected create object (received %s)" "Follow"
  236. and* actor = field "actor" uri
  237. and* cc = field_or_default "cc" (singleton_or_list uri) []
  238. and* to_ = field_or_default "to" (singleton_or_list uri) []
  239. and* id = field "id" uri
  240. and* object_ = field "object" uri
  241. and* state = field_opt "state" (string >>= function "pending" -> succeed `Pending
  242. | "cancelled" -> succeed `Cancelled
  243. | _ -> fail "unknown status")
  244. (* and* raw = value *) in
  245. succeed ({actor; cc; to_; id; object_; state(*; raw*)}: Types.follow)
  246. let announce obj =
  247. let open D in
  248. let* () = field "type" @@ constant ~msg:"expected create object (received %s)" "Announce"
  249. and* actor = field "actor" uri
  250. and* id = field "id" uri
  251. and* published = field_opt "published" rfc3339
  252. and* to_ = field "to" (singleton_or_list uri)
  253. and* cc = field_or_default "cc" (singleton_or_list uri) []
  254. and* obj = field "object" obj
  255. (* and* raw = value *) in
  256. succeed ({id; published; actor; to_; cc; obj(* ; raw*)}: _ Types.announce)
  257. let create obj =
  258. let open D in
  259. let* () = field "type" @@ constant ~msg:"expected create object (received %s)" "Create"
  260. and* id = field "id" uri
  261. and* actor = field "actor" uri
  262. and* direct_message = field_or_default "direct" bool false
  263. and* published = field_opt "published" rfc3339
  264. and* to_ = field_or_default "to" (singleton_or_list uri) []
  265. and* cc = field_or_default "cc" (singleton_or_list uri) []
  266. and* obj = field "object" obj
  267. (* and* raw = value *) in
  268. succeed ({
  269. id; actor; published;
  270. to_; cc;
  271. direct_message;
  272. obj;
  273. (*raw;*)
  274. }: _ Types.create)
  275. let update obj =
  276. let open D in
  277. let* () = field "type" @@ constant ~msg:"expected create object (received %s)" "Update"
  278. and* id = field "id" uri
  279. and* actor = field "actor" uri
  280. and* direct_message = field_or_default "direct" bool false
  281. and* published = field_opt "published" rfc3339
  282. and* to_ = field_or_default "to" (singleton_or_list uri) []
  283. and* cc = field_or_default "cc" (singleton_or_list uri) []
  284. and* obj = field "object" obj
  285. (* and* raw = value *) in
  286. succeed ({
  287. id; actor; published;
  288. to_; cc;
  289. direct_message;
  290. obj;
  291. (*raw;*)
  292. }: _ Types.update)
  293. let core_obj () =
  294. let open D in
  295. let* ty = field_opt "type" string in
  296. match ty with
  297. | Some "Person" -> person >|= fun v -> `Person v
  298. | Some "Follow" -> follow >|= fun v -> `Follow v
  299. | Some "Note" -> note >|= fun v -> `Note v
  300. | Some "Block" -> block >|= fun v -> `Block v
  301. | Some "Like" -> like >|= fun v -> `Like v
  302. | None -> string >|= fun v -> `Link v
  303. | Some ev -> fail ("unsupported event" ^ ev)
  304. let core_obj = core_obj ()
  305. let event (enc: Types.core_obj D.decoder) : Types.obj D.decoder =
  306. let open D in
  307. let* ty = field "type" string in
  308. match ty with
  309. | "Accept" -> accept enc >|= fun v -> `Accept v
  310. | "Announce" -> announce enc >|= fun v -> `Announce v
  311. | "Create" -> create enc >|= fun v -> `Create v
  312. | "Update" -> update enc >|= fun v -> `Update v
  313. | "Delete" -> delete enc >|= fun v -> `Delete v
  314. | "Undo" -> undo enc >|= fun v -> `Undo v
  315. | _ -> fail "unsupported event"
  316. let obj : Types.obj D.decoder =
  317. D.one_of [
  318. "core_obj", core_obj;
  319. "core_obj event", (event core_obj)
  320. ]
  321. module Webfinger = struct
  322. let ty =
  323. let open D in
  324. string >>= function
  325. | str when string_prefix ~pre:Constants.ContentType.text_html str -> succeed `Html
  326. | str when string_prefix ~pre:Constants.ContentType.app_json str -> succeed `Json
  327. | str when string_prefix ~pre:Constants.ContentType._app_act_json str -> succeed `ActivityJson_
  328. | str when string_prefix ~pre:Constants.ContentType.app_jlda str -> succeed `ActivityJsonLd
  329. | _ -> fail "unsupported self link type"
  330. let self =
  331. let open D in
  332. let* ty = field "type" ty
  333. and* href = field "href" uri in
  334. succeed @@ Types.Webfinger.Self (ty, href)
  335. let profile_page =
  336. let open D in
  337. let* ty = field "type" ty
  338. and* href = field "href" uri in
  339. succeed @@ Types.Webfinger.ProfilePage (ty, href)
  340. let ostatus_subscribe =
  341. let open D in
  342. let* template = field "template" string in
  343. succeed @@ Types.Webfinger.OStatusSubscribe template
  344. let link =
  345. let open D in
  346. let* rel = field "rel" string in
  347. match rel with
  348. | "self" -> self
  349. | str when String.equal str Constants.Webfinger.ostatus_rel ->
  350. ostatus_subscribe
  351. | str when String.equal str Constants.Webfinger.profile_page ->
  352. profile_page
  353. | _ -> fail "unsupported link relation"
  354. let query_result =
  355. let open D in
  356. let* subject = field "subject" string
  357. and* aliases = field_or_default "aliases" (list string) []
  358. and* links = field "links" (list_ignoring_unknown link) in
  359. succeed Types.Webfinger.{subject;aliases;links}
  360. end