decode.ml 16 KB

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