123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215open!CoremoduleMultipart:sigtypet=private{boundary:Boundary.t;prologue:Bigstring_shared.toption;epilogue:Bigstring_shared.toption;parts:Email.tlist;container_headers:Headers.t}[@@derivingfields,sexp_of]valcreate_unsafe:boundary:Boundary.t->?prologue:Bigstring_shared.t->?epilogue:Bigstring_shared.t->Email.tlist->container_headers:Headers.t->tvalcreate:?boundary:Boundary.t->?prologue:Bigstring_shared.t->?epilogue:Bigstring_shared.t->?container_headers:Headers.t->Email.tlist->tvalset:t->?boundary:Boundary.t->?prologue:Bigstring_shared.toption->?epilogue:Bigstring_shared.toption->?parts:Email.tlist->?container_headers:Headers.t->unit->tincludeString_monoidable.Swithtypet:=tend=structtypet={boundary:Boundary.t;prologue:Bigstring_shared.toption;epilogue:Bigstring_shared.toption;parts:Email.tlist;container_headers:Headers.t}[@@derivingfields,sexp_of]letcreate_unsafe~boundary?prologue?epilogueparts~container_headers={boundary;prologue;epilogue;parts;container_headers};;letcreate?boundary?prologue?epilogue?(container_headers=Headers.empty)parts=letboundary=Boundary.generate_non_conflicting_boundary?prologue~parts:(List.mapparts~f:Email.to_string_monoid)?epilogue(Option.value_mapboundary~default:Boundary.Generator.default~f:Boundary.Generator.from_existing_boundary)increate_unsafe~boundary?prologue?epilogueparts~container_headers;;letsett?(boundary=t.boundary)?(prologue=t.prologue)?(epilogue=t.epilogue)?(parts=t.parts)?(container_headers=t.container_headers)()=create~boundary?prologue?epilogue~container_headersparts;;letto_string_monoidt=Boundary.join_without_checking_for_conflicts?prologue:t.prologue~parts:(List.mapt.parts~f:Email.to_string_monoid)?epilogue:t.epiloguet.boundary;;endtypet=|MultipartofMultipart.t|MessageofEmail.t|DataofOctet_stream.t[@@derivingsexp_of]letrecmultipart_of_bigstring_shared~boundary~container_headersbstr=letopenOr_error.Let_syntaxinletprologue,parts,epilogue=Boundary.splitboundarybstrinlet%mapparts=List.mapparts~f:(funpart->Or_error.tag(Or_error.try_with(fun()->Email.of_bigstring(Bigstring_shared.to_bigstringpart)))~tag:(sprintf"failed part:\n%s"(Bigstring_shared.to_stringpart)))|>Or_error.allinMultipart.create_unsafe~boundary?prologue?epilogue~container_headerspartsandcontent_of_bigstring_shared~headers?container_headersbstr=letopenOr_error.Let_syntaxinletparent_media_type=Option.bindcontainer_headers~f:Media_type.from_headersinletmedia_type=Option.value(Media_type.from_headersheaders)~default:(Media_type.default?parent:parent_media_type())inletencoding=Octet_stream.Encoding.of_headers_or_defaultheadersinletoctet_stream=Octet_stream.of_bigstring_shared~encodingbstrinletdecodeoctet_stream=matchOctet_stream.decodeoctet_streamwith|None->Or_error.error"Unknown message encoding"encodingOctet_stream.Encoding.sexp_of_t|Somedecoded_bstr->Okdecoded_bstrinmatchMedia_type.multipart_boundarymedia_typewith|Someboundary->(* According to Wikipedia, the content-transfer-encoding of a multipart
type must always be "7bit", "8bit" or "binary" to avoid the
complications that would be posed by multiple levels of decoding. In
this case this decode call is free. *)let%binddecoded_bstr=decodeoctet_streaminlet%bindmultipart=multipart_of_bigstring_shared~boundary~container_headers:headersdecoded_bstrinOk(Multipartmultipart)|None->ifMedia_type.is_message_rfc822media_typethen(let%binddecoded_bstr=decodeoctet_streaminlet%bindemail=Or_error.try_with(fun()->Email.of_bigstring(Bigstring_shared.to_bigstringdecoded_bstr))inOk(Messageemail))elseOk(Dataoctet_stream)andparse?container_headersemail=content_of_bigstring_shared?container_headers~headers:(Email.headersemail)(Email.raw_contentemail|>Email_raw_content.to_bigstring_shared);;letto_string_monoid=function|Multipartmultipart->Multipart.to_string_monoidmultipart|Messagemessage->Email.to_string_monoidmessage|Dataoctet_stream->Octet_stream.encoded_contentsoctet_stream|>Bigstring_shared.to_string_monoid;;letto_bigstring_sharedt=to_string_monoidt|>String_monoid.to_bigstring|>Bigstring_shared.of_bigstring;;letto_raw_contentt=to_bigstring_sharedt|>Email_raw_content.of_bigstring_sharedletrecmultipart_map_data~on_unparsable_contentmp~f=Multipart.setmp~parts:(List.mapmp.Multipart.parts~f:(map_data~on_unparsable_content~f))()andcontent_map_data~on_unparsable_contentt~f=matchtwith|Multipartt->Multipart(multipart_map_data~on_unparsable_contentt~f)|Messagemessage->Message(map_data~on_unparsable_contentmessage~f)|Datadata->Data(fdata)andmap_data~on_unparsable_contentemail~f=matchparseemailwith|Okcontent->letcontent=content_map_datacontent~on_unparsable_content~finEmail.set_raw_contentemail(to_bigstring_sharedcontent|>Email_raw_content.of_bigstring_shared)|Errore->(matchon_unparsable_contentwith|`Skip->email|`Raise->raise_s[%message"[map_data] has unparsable content"(e:Error.t)]);;letmap_data?(on_unparsable_content=`Skip)email~f=map_data~on_unparsable_contentemail~f;;letto_email~headerst=letheaders=letmedia_type=matchtwith|Multipartmp->(matchMedia_type.from_headersheaderswith|None->Some(Media_type.create_multipart"related"~boundary:mp.boundary)|Somemedia_type->Some(Media_type.set_multipart_boundarymedia_typemp.boundary))|_->Noneinmatchmedia_typewith|None->headers|Somemedia_type->Media_type.set_headersheadersmedia_typeinEmail.create~headers~raw_content:(to_bigstring_sharedt|>Email_raw_content.of_bigstring_shared);;letset_contentemailt=to_email~headers:(Email.headersemail)t