~/bend-docscommunity

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.

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).

type Part source · line 347 · raw

Data

An attachment: its file name, content type and bytes.

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.