mime.bend checks
raw source on the hub · import 0x650cd729029c76d9f6151ee977dc0ae8/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.
2 imports
import Base import ./text.bend as T
Types
type Words source · line 198 · 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 273 · 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 347 · 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 9 · raw
@c:Bool -> @a:U32 -> @b:U32 -> U32
def Utf8.cont source · line 19 · raw
@+c:U32 -> @n:Nat -> U32
def Utf8.size source · line 23 · raw
@+c:U32 -> Nat
How many bytes a code point takes.
def Utf8.put.k source · line 28 · raw
@k:Nat -> @+c:U32 -> @rest:List<&2, U32> -> List<&2, U32>
def Utf8.put source · line 42 · 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 45 · raw
@s:String -> List<&2, U32>
def B64.ch source · line 56 · raw
@+i:U32 -> Char
The base64 digit for 0..63.
def B64.at source · line 62 · raw
@+x:U32 -> @n:Nat -> Char
def B64.quad source · line 66 · raw
@+x:U32 -> @pad:Nat -> @rest:String -> String
A group of 4 digits, the last pad of them "=".
def B64.word source · line 77 · raw
@+a:U32 -> @+b:U32 -> @+c:U32 -> U32
def B64.onto source · line 80 · raw
@acc:String -> @tail:String -> String
def B64.quad.rev source · line 88 · 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 99 · raw
@full:Bool -> @acc:String -> String
A CRLF (reversed) before the group when wrap groups fill the line.
def B64.go source · line 109 · 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 125 · raw
@bs:List<&2, U32> -> String
def B64.lines source · line 130 · 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 133 · raw
@s:String -> String
def Date.pad source · line 139 · raw
@+n:U32 -> String
def Date.day source · line 142 · raw
@+w:U32 -> String
def Date.month source · line 150 · raw
@+m:U32 -> String
def Date.civil source · line 165 · 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 178 · raw
@+t:U32 -> String
The Date header's value for t seconds since 1970, in UTC.
def Mime.printable source · line 190 · raw
@s:String -> Bool
Whether every char is printable ASCII (space to tilde).
def Mime.word source · line 201 · raw
@cur:List<&2, U32> -> String
def Mime.add.at source · line 205 · 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 213 · raw
@w:Words -> @+c:U32 -> Words
def Mime.words source · line 218 · raw
@s:String -> @w:Words -> String
def Mime.encoded source · line 227 · raw
@s:String -> String
s as RFC 2047 encoded words of at most 39 bytes each, folded.
def Mime.subject.at source · line 230 · raw
@plain:Bool -> @s:String -> String
def Mime.subject source · line 239 · 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 247 · raw
@s:String -> String
Line breaks as CRLF, bare CRs dropped.
def Mime.seven source · line 260 · 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 276 · raw
@seven:Bool -> @s:String -> Body
def Mime.body source · line 284 · raw
@+s:String -> Body
Text goes 7bit only if it cannot hold a part boundary (Mime.boundary).
def B64.val source · line 291 · raw
@+c:U32 -> U32
A base64 digit's value, or 64 for anything else.
def B64.keep source · line 297 · raw
@ok:Bool -> @v:U32 -> @rest:List<&2, U32> -> List<&2, U32>
def B64.vals source · line 304 · raw
@s:String -> List<&2, U32>
def B64.byte source · line 312 · raw
@+x:U32 -> @n:Nat -> U32
def B64.group source · line 315 · raw
@+a:U32 -> @+b:U32 -> @+c:U32 -> @+d:U32 -> U32
def B64.bytes source · line 318 · raw
@vs:List<&2, U32> -> List<&2, U32>
def B64.decode source · line 332 · raw
@s:String -> List<&2, U32>
The bytes of base64 text; padding and anything off the alphabet skipped.
def B64.ascii source · line 336 · 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 350 · raw
@+name:String -> @e:String -> Bool
def Mime.ctype source · line 355 · 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 383 · raw
@+c:U32 -> Bool
attr-char (RFC 2231 7): ALPHA DIGIT !#$&+-.^_`|~.
def Mime.hex source · line 390 · raw
@+d:U32 -> Char
def Mime.pct.at source · line 393 · raw
@keep:Bool -> @+b:U32 -> @rest:String -> String
def Mime.pct source · line 401 · raw
@bs:List<&2, U32> -> String
Bytes percent-encoded outside attr-char (RFC 2231 4).
def Mime.safe source · line 408 · raw
@s:String -> Bool
def Mime.param.at source · line 416 · raw
@safe:Bool -> @+key:String -> @+n:String -> String
def Mime.param source · line 426 · 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 433 · raw
@ctype:String -> @b:Body -> String
A text leaf: its headers, a blank line, its body.
def Mime.leaf source · line 438 · raw
@ctype:String -> @+s:String -> String
def Mime.token.ch source · line 443 · raw
@+c:U32 -> Bool
token chars (RFC 2045 5.1): printable ASCII but space and the tspecials ()<>@,;:\"/[]?=.
def Mime.token source · line 450 · raw
@s:String -> Bool
def Mime.ctype.parts source · line 457 · raw
@ps:List<&2, String> -> Bool
def Mime.ctype.ok source · line 467 · raw
@s:String -> Bool
Whether s is a bare content type, "type/subtype" in tokens: no parameters, no spaces, nothing that could end the header line.
def Mime.ctype.safe source · line 472 · raw
@+s:String -> String
The type as it goes in the header: itself when it is a bare content type, else application/octet-stream; and no CR or LF either way.
def Mime.ctype.bad source · line 476 · raw
@fs:List<&2, Part> -> String
The first attachment type that is not a bare content type, or "".
def Mime.attach source · line 486 · raw
@p:Part -> @rest:String -> String
An attachment leaf, always base64, in front of rest.
def Mime.parts source · line 492 · raw
@+b:String -> @ps:List<&2, String> -> String
def Mime.preamble source · line 499 · raw
String
def Mime.multi source · line 504 · 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 509 · 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 516 · raw
@none:Bool -> @text:String -> @html:String -> @+b:String -> String
def Mime.mixed.at source · line 524 · raw
@none:Bool -> @alt:String -> @files:List<&2, Part> -> @+b:String -> String
def Mime.entity source · line 538 · 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 544 · raw
@+hex:String -> String
A boundary stem from random words: "=_bend_" and hex.