123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133moduleStable=structopenCore.Core_stablemoduleEmail_simple=Email_simple.StablemoduleEmail_address=Email_address.StablemoduleV1=structtypet={header:Email_simple.Content.V1.t;from:[`Keep|`Change_toofEmail_address.V1.t];to_:[`Keep|`Change_toofEmail_address.V1.tlist];cc:[`Keep|`Change_toofEmail_address.V1.tlist];subject:[`Keep|`Prependofstring]}[@@derivingsexp,bin_io]endendopenCoretypet=Stable.V1.t={header:Email_simple.Content.t;from:[`Keep|`Change_toofEmail_address.t];to_:[`Keep|`Change_toofEmail_address.tlist];cc:[`Keep|`Change_toofEmail_address.tlist];subject:[`Keep|`Prependofstring]}[@@derivingsexp_of]letcreate?(from=`Keep)?(to_=`Keep)?(cc=`Keep)?(subject=`Keep)header={header;from;to_;cc;subject};;letcreate_from_emailemail=letget_headerx=Headers.last~whitespace:`Raw(Email.headersemail)xinletfrom=matchget_header"From"with|None|Some""->`Keep|Somefrom->`Change_to(Email_address.of_string_exnfrom)inletto_=matchget_header"To"with|None|Some""->`Keep|Someto_->`Change_to(Email_address.list_of_string_exnto_)inletcc=matchget_header"Cc"with|None|Some""->`Keep|Somecc->`Change_to(Email_address.list_of_string_exncc)inletsubject=matchget_header"Subject"with|None|Some""->`Keep|Somesubject->`Prependsubjectinletemail=Email.modify_headersemail~f:(Headers.filter~f:(fun~name~value:_->letopenString.Caseless.Replace_polymorphic_compareinname<>"From"&&name<>"To"&&name<>"Cc"&&name<>"Subject"))increate~from~to_~cc~subject(Email_simple.Content.of_emailemail);;letcontent_of_emailemail=Email_simple.Content.create~content_type:(Email_simple.Mimetype.of_string"message/rfc822")(Email.to_stringemail);;(* We must be very careful with the email headers that we use in the new email. We use the
following policies:
(1) Add "From", "To", "Cc", "Subject" according to the supplied arguments to [create]
(2) Copy over all other headers except:
(i) DKIM-Signature - We break the signing by altering the email content
(ii) Return-Path - We don't want the altered email to ever bounce back to the
original sender
(iii) Content-Transfer-Encoding, Content-Type, Content-Disposition - We structure
the email differently. These wouldn't make sense anymore
*)letadd{header;from;to_;cc;subject}email=letcontent=Email_simple.Content.mixed[header;content_of_emailemail]inletheaders=Email.headersemailinletget_headersx=Headers.find_all~whitespace:`Rawheadersxinletget_headerx=Headers.last~whitespace:`Rawheadersxinletfrom=matchfromwith|`Keep->get_header"From"|>Option.value~default:""|`Change_toaddr->Email_address.to_stringaddrinletto_=matchto_with|`Keep->get_headers"To"|`Change_toaddrs->List.mapaddrs~f:Email_address.to_stringinletcc=matchccwith|`Keep->get_headers"Cc"|`Change_toaddrs->List.mapaddrs~f:Email_address.to_stringinletsubject=letsubj=get_header"Subject"|>Option.value~default:""inmatchsubjectwith|`Keep->subj|`Prependstr->sprintf"%s %s"strsubjinletextra_headers=Headers.filterheaders~f:(fun~name~value:_->matchnamewith|"From"|"To"|"Cc"|"Subject"|"Message-Id"|"Date"|"DKIM-Signature"|"Return-Path"|"Content-Transfer-Encoding"|"Content-Type"|"Content-Disposition"->false|_->true)|>Headers.to_listinletid=get_header"Message-Id"inletdate=get_header"Date"inEmail_simple.Expert.create_raw?id?date~from~to_~cc~subject~extra_headerscontent;;