123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165typet=[`Bit7|`Bit8|`Binary|`Quoted_printable|`Base64|`Ietf_tokenofstring|`X_tokenofstring]leterror_msgffmt=Fmt.kstr(funmsg->Error(`Msgmsg))fmtletppppf=function|`Bit7->Fmt.stringppf"7bit"|`Bit8->Fmt.stringppf"8bit"|`Binary->Fmt.stringppf"binary"|`Quoted_printable->Fmt.stringppf"quoted-printable"|`Base64->Fmt.stringppf"base64"|`Ietf_tokentoken->Fmt.pfppf"ietf:%s"token|`X_tokentoken->Fmt.pfppf"x:%s"tokenletdefault=`Bit7letbit8=`Bit8letbit7=`Bit7letbinary=`Binaryletquoted_printable=`Quoted_printableletbase64=`Base64letof_string=function|"7bit"->Ok`Bit7|"8bit"->Ok`Bit8|"binary"->Ok`Binary|"quoted-printable"->Ok`Quoted_printable|"base64"->Ok`Base64|x->error_msgf"Invalid MIME encoding: %s"x(* TODO:
- let the user to craft an extension token.
- check IETF database *)letequalab=match(a,b)with|`Bit7,`Bit7->true|`Bit8,`Bit8->true|`Binary,`Binary->true|`Quoted_printable,`Quoted_printable->true|`Base64,`Base64->true|`Ietf_tokena,`Ietf_tokenb->String.(equal(lowercase_asciia)(lowercase_asciib))|`X_tokena,`X_tokenb->String.(equal(lowercase_asciia)(lowercase_asciib))|_,_->falsemoduleDecoder=structopenAngstromletinvalid_tokentoken=Fmt.kstrfail"invalid token: %s"tokenletof_stringsa=matchparse_string~consume:Consume.Allaswith|Okv->Somev|Error_->None(* From RFC 2045
tspecials := "(" / ")" / "<" / ">" / "@" /
"," / ";" / ":" / "\" / <">
"/" / "[" / "]" / "?" / "="
; Must be in quoted-string,
; to use within parameter values
Note that the definition of "tspecials" is the same as the RFC 822
definition of "specials" with the addition of the three characters
"/", "?", and "=", and the removal of ".".
*)letis_tspecials=function|'('|')'|'<'|'>'|'@'|','|';'|':'|'\\'|'"'|'/'|'['|']'|'?'|'='->true|_->falseletis_ctl=function'\000'..'\031'|'\127'->true|_->falseletis_space=(=)' '(* From RFC 2045
token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
or tspecials>
*)letis_ascii=function'\000'..'\127'->true|_->falseletis_tokenc=is_asciic&&(not(is_tspecialsc))&&(not(is_ctlc))&¬(is_spacec)lettoken=take_while1is_token(* From RFC 2045
ietf-token := <An extension token defined by a
standards-track RFC and registered
with IANA.>
XXX(dinosaure): we don't check at this time if IETF token exists.
*)letietf_token=token(* From RFC 2045
x-token := <The two characters "X-" or "x-" followed, with
no intervening white space, by any token>
*)letx_token=satisfy(function'x'|'X'->true|_->false)*>char'-'*>token(* From RFC 2045
extension-token := ietf-token / x-token
*)letextension_token=peek_char>>=function|Some'X'|Some'x'->x_token>>|funv->`X_tokenv|_->ietf_token>>|funv->`Ietf_tokenvletis_wsp=function' '|'\t'->true|_->false(* From RFC 2045
mechanism := "7bit" / "8bit" / "binary" /
"quoted-printable" / "base64" /
ietf-token / x-token
These values are not case sensitive -- Base64 and BASE64 and bAsE64
are all equivalent. An encoding type of 7BIT requires that the body
is already in a 7bit mail-ready representation. This is the default
value -- that is, "Content-Transfer-Encoding: 7BIT" is assumed if the
Content-Transfer-Encoding header field is not present.
*)letmechanism=skip_whileis_wsp*>token<*skip_whileis_wsp>>=funs->(* XXX(dinosaure): lowercase_*ascii* is fine, not utf8 in this part. *)matchString.lowercase_asciiswith|"7bit"->return`Bit7|"8bit"->return`Bit8|"binary"->return`Binary|"quoted-printable"->return`Quoted_printable|"base64"->return`Base64|_->matchof_stringsextension_tokenwith|Somev->returnv|None->invalid_tokensendmoduleEncoder=structopenPrettymletmechanismppf=function|`Bit7->stringppf"7bit"|`Bit8->stringppf"8bit"|`Binary->stringppf"binary"|`Quoted_printable->stringppf"quoted-printable"|`Base64->stringppf"base64"|`Ietf_tokenx->stringppfx|`X_tokenx->evalppf[string$"X-";!!string]xendletto_stringv=Prettym.to_stringEncoder.mechanismv