mime.bend checks
raw source on the hub · import 0xfd64aefef43747d3eaeea50c10a112e5/mime.bend as Mime
MIME and message format: UTF-8 bytes, base64 (RFC 4648), the Date header (RFC 5322 3.3), encoded words for non-ASCII subjects (RFC 2047) and a transfer encoding that keeps every line legal (RFC 2045, RFC 5322 2.1.1). The whole message comes out 7-bit ASCII, so it needs no 8BITMIME or SMTPUTF8 from the server.
1 import
import Base
Types
type Words source · line 197 · raw
Data
Encoded words built so far, the bytes of the current one and its size.
Words@out:String -> @cur:List<&2, U32> -> @n:U32 -> Words
type Body source · line 272 · raw
Data
The body's transfer encoding and its text: 7bit as written, else base64 of its UTF-8 (CRLF line breaks first, RFC 2045 6.8).
Body@encoding:String -> @text:String -> Body
type Part source · line 346 · raw
Data
An attachment: its file name, content type and bytes.
Part@name:String -> @ctype:String -> @data:List<&2, U32> -> Part
Definitions
def Word.pick source · line 8 · raw
@c:Bool -> @a:U32 -> @b:U32 -> U32
def Utf8.cont source · line 18 · raw
@+c:U32 -> @n:Nat -> U32
def Utf8.size source · line 22 · raw
@+c:U32 -> Nat
How many bytes a code point takes.
def Utf8.put.k source · line 27 · raw
@k:Nat -> @+c:U32 -> @rest:List<&2, U32> -> List<&2, U32>
def Utf8.put source · line 41 · raw
@+c:U32 -> @rest:List<&2, U32> -> List<&2, U32>
One code point's bytes, put in front of rest.
def Utf8.bytes source · line 44 · raw
@s:String -> List<&2, U32>
def B64.ch source · line 55 · raw
@+i:U32 -> Char
The base64 digit for 0..63.
def B64.at source · line 61 · raw
@+x:U32 -> @n:Nat -> Char
def B64.quad source · line 65 · raw
@+x:U32 -> @pad:Nat -> @rest:String -> String
A group of 4 digits, the last pad of them "=".
def B64.word source · line 76 · raw
@+a:U32 -> @+b:U32 -> @+c:U32 -> U32
def B64.onto source · line 79 · raw
@acc:String -> @tail:String -> String
def B64.quad.rev source · line 87 · raw
@+x:U32 -> @pad:Nat -> @acc:String -> String
The 4 digits of a group, reversed, on acc; pad of them "=".
def B64.brk source · line 98 · raw
@full:Bool -> @acc:String -> String
A CRLF (reversed) before the group when wrap groups fill the line.
def B64.go source · line 108 · raw
@bs:List<&2, U32> -> @+k:U32 -> @+wrap:U32 -> @acc:String -> @tail:String -> String
The digits of bs, in front of tail; k groups are on the current line,
and a line takes wrap groups (0: one line). acc holds the digits so
far, reversed: one tail call per group, then one pass onto the tail.
def B64.encode source · line 124 · raw
@bs:List<&2, U32> -> String
def B64.lines source · line 129 · raw
@bs:List<&2, U32> -> @tail:String -> String
Base64 in lines of 76 digits (19 groups), as MIME bodies need, then tail.
def B64.text source · line 132 · raw
@s:String -> String
def Date.pad source · line 138 · raw
@+n:U32 -> String
def Date.day source · line 141 · raw
@+w:U32 -> String
def Date.month source · line 149 · raw
@+m:U32 -> String
def Date.civil source · line 164 · raw
@+days:U32 -> String
Year, month and day of a day count since 1970-01-01 (H. Hinnant's civil_from_days, for days >= 0), as "DD Mon YYYY".
def Date.fmt source · line 177 · raw
@+t:U32 -> String
The Date header's value for t seconds since 1970, in UTC.
def Mime.printable source · line 189 · raw
@s:String -> Bool
Whether every char is printable ASCII (space to tilde).
def Mime.word source · line 200 · raw
@cur:List<&2, U32> -> String
def Mime.add.at source · line 204 · raw
@full:Bool -> @out:String -> @cur:List<&2, U32> -> @+n:U32 -> @bc:List<&2, U32> -> @+k:U32 -> Words
39 bytes make 52 digits: "Subject: " plus one word is 73 columns.
def Mime.add source · line 212 · raw
@w:Words -> @+c:U32 -> Words
def Mime.words source · line 217 · raw
@s:String -> @w:Words -> String
def Mime.encoded source · line 226 · raw
@s:String -> String
s as RFC 2047 encoded words of at most 39 bytes each, folded.
def Mime.subject.at source · line 229 · raw
@plain:Bool -> @s:String -> String
def Mime.subject source · line 238 · raw
@+s:String -> String
A Subject value: as is when it is printable ASCII that fits one line and cannot read as an encoded word, else encoded words, folded.
def Mime.crlf source · line 246 · raw
@s:String -> String
Line breaks as CRLF, bare CRs dropped.
def Mime.seven source · line 259 · raw
@s:String -> @+col:U32 -> Bool
Whether the body can go as 7bit: ASCII without NUL, and no line past 998 chars (RFC 5322 2.1.1); col counts the current line.
def Mime.body.at source · line 275 · raw
@seven:Bool -> @s:String -> Body
def Mime.body source · line 283 · raw
@+s:String -> Body
Text goes 7bit only if it cannot hold a part boundary (Mime.boundary).
def B64.val source · line 290 · raw
@+c:U32 -> U32
A base64 digit's value, or 64 for anything else.
def B64.keep source · line 296 · raw
@ok:Bool -> @v:U32 -> @rest:List<&2, U32> -> List<&2, U32>
def B64.vals source · line 303 · raw
@s:String -> List<&2, U32>
def B64.byte source · line 311 · raw
@+x:U32 -> @n:Nat -> U32
def B64.group source · line 314 · raw
@+a:U32 -> @+b:U32 -> @+c:U32 -> @+d:U32 -> U32
def B64.bytes source · line 317 · raw
@vs:List<&2, U32> -> List<&2, U32>
def B64.decode source · line 331 · raw
@s:String -> List<&2, U32>
The bytes of base64 text; padding and anything off the alphabet skipped.
def B64.ascii source · line 335 · raw
@bs:List<&2, U32> -> String
Bytes as text: ASCII as is, any other byte as "?" (for error messages).
def Mime.ext.is source · line 349 · raw
@+name:String -> @e:String -> Bool
def Mime.ctype source · line 354 · raw
@+n:String -> String
A content type from the file name's extension (application/octet-stream when unknown). message/rfc822 is never used: it may not be base64.
def Mime.attr source · line 382 · raw
@+c:U32 -> Bool
attr-char (RFC 2231 7): ALPHA DIGIT !#$&+-.^_`|~.
def Mime.hex source · line 389 · raw
@+d:U32 -> Char
def Mime.pct.at source · line 392 · raw
@keep:Bool -> @+b:U32 -> @rest:String -> String
def Mime.pct source · line 400 · raw
@bs:List<&2, U32> -> String
Bytes percent-encoded outside attr-char (RFC 2231 4).
def Mime.safe source · line 407 · raw
@s:String -> Bool
def Mime.param.at source · line 415 · raw
@safe:Bool -> @+key:String -> @+n:String -> String
def Mime.param source · line 425 · raw
@+key:String -> @+n:String -> String
A file name parameter: quoted when the name is safe ASCII, else only in RFC 2231's extended form (as Python's email package writes it: a second, ASCII form would be read first by some parsers).
def Mime.leaf.with source · line 432 · raw
@ctype:String -> @b:Body -> String
A text leaf: its headers, a blank line, its body.
def Mime.leaf source · line 437 · raw
@ctype:String -> @+s:String -> String
def Mime.attach source · line 441 · raw
@p:Part -> @rest:String -> String
An attachment leaf, always base64, in front of rest.
def Mime.parts source · line 447 · raw
@+b:String -> @ps:List<&2, String> -> String
def Mime.preamble source · line 454 · raw
String
def Mime.multi source · line 459 · raw
@sub:String -> @+b:String -> @ps:List<&2, String> -> String
A multipart entity (RFC 2046 5.1): a boundary that no part can hold, then each part after a delimiter line, then the close delimiter.
def Mime.attaches source · line 464 · raw
@fs:List<&2, Part> -> @+b:String -> String
The files as parts, each after its delimiter, then the close one.
def Mime.alt.at source · line 471 · raw
@none:Bool -> @text:String -> @html:String -> @+b:String -> String
def Mime.mixed.at source · line 479 · raw
@none:Bool -> @alt:String -> @files:List<&2, Part> -> @+b:String -> String
def Mime.entity source · line 493 · raw
@text:String -> @+html:String -> @+files:List<&2, Part> -> @+b:String -> String
The message's entity: text alone; text and HTML as alternatives (plain first, RFC 2046 5.1.4); either one with files as mixed. b is the boundary stem: base64 and the 7bit texts (Mime.body) cannot hold it.
def Mime.boundary source · line 499 · raw
@+hex:String -> String
A boundary stem from random words: "=_bend_" and hex.