add mail
commit
50a25c2a28
|
@ -0,0 +1,5 @@
|
||||||
|
USING: kernel ;
|
||||||
|
USING: mail.parser ;
|
||||||
|
|
||||||
|
IN: mail
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
! Copyright (C) 2009 Doug Coleman.
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: accessors ascii assocs byte-arrays combinators fry
|
||||||
|
hashtables http http.parsers io io.encodings.binary io.files
|
||||||
|
io.files.temp io.files.unique io.streams.string kernel math
|
||||||
|
quoting sequences splitting namespaces sequences.parser arrays regexp ;
|
||||||
|
USING: prettyprint ;
|
||||||
|
IN: mail.multipart
|
||||||
|
|
||||||
|
CONSTANT: buffer-size 65536
|
||||||
|
CONSTANT: separator-prefix "\r\n--"
|
||||||
|
|
||||||
|
SYMBOL: current-boundary
|
||||||
|
|
||||||
|
TUPLE: multipart
|
||||||
|
end-of-stream?
|
||||||
|
current-separator mime-separator
|
||||||
|
header
|
||||||
|
content-disposition bytes
|
||||||
|
filename temp-file
|
||||||
|
name name-content
|
||||||
|
mime-parts ;
|
||||||
|
|
||||||
|
TUPLE: mime-file headers filename temporary-path ;
|
||||||
|
C: <mime-file> mime-file
|
||||||
|
|
||||||
|
TUPLE: mime-variable headers key value ;
|
||||||
|
C: <mime-variable> mime-variable
|
||||||
|
|
||||||
|
: <multipart> ( mime-separator -- multipart )
|
||||||
|
multipart new
|
||||||
|
swap >>mime-separator
|
||||||
|
{ } clone >>mime-parts ;
|
||||||
|
|
||||||
|
: mime-write ( sequence -- )
|
||||||
|
>byte-array write ;
|
||||||
|
|
||||||
|
: parse-headers ( string -- hashtable )
|
||||||
|
string-lines harvest [ parse-header-line ] map >hashtable ;
|
||||||
|
|
||||||
|
: fill-bytes ( multipart -- multipart )
|
||||||
|
buffer-size read
|
||||||
|
[ '[ _ B{ } append-as ] change-bytes ]
|
||||||
|
[ t >>end-of-stream? ] if* ;
|
||||||
|
|
||||||
|
ERROR: mime-decoding-ran-out-of-bytes ;
|
||||||
|
: dump-until-separator ( multipart -- multipart )
|
||||||
|
[ ] [ current-separator>> ] [ bytes>> ] tri
|
||||||
|
dup [ mime-decoding-ran-out-of-bytes ] unless
|
||||||
|
2dup subseq-start [
|
||||||
|
cut-slice
|
||||||
|
[ mime-write ]
|
||||||
|
[ swap length tail-slice >>bytes ] bi*
|
||||||
|
] [
|
||||||
|
tuck [ length ] bi@ - 1 - cut-slice
|
||||||
|
[ mime-write ]
|
||||||
|
[ >>bytes ] bi* fill-bytes
|
||||||
|
dup end-of-stream?>> [ dump-until-separator ] unless
|
||||||
|
] if* ;
|
||||||
|
|
||||||
|
: dump-string ( multipart separator -- multipart string )
|
||||||
|
>>current-separator
|
||||||
|
[ dump-until-separator ] with-string-writer ;
|
||||||
|
|
||||||
|
: read-header ( multipart -- multipart )
|
||||||
|
dup bytes>> "--\r\n" sequence= [
|
||||||
|
t >>end-of-stream?
|
||||||
|
] [
|
||||||
|
"\r\n\r\n" dump-string parse-headers >>header
|
||||||
|
] if ;
|
||||||
|
|
||||||
|
: empty-name? ( string -- ? )
|
||||||
|
{ "''" "\"\"" "" f } member? ;
|
||||||
|
|
||||||
|
: save-uploaded-file ( multipart -- )
|
||||||
|
dup filename>> empty-name? [
|
||||||
|
drop
|
||||||
|
] [
|
||||||
|
[ [ header>> ] [ filename>> ] [ temp-file>> ] tri <mime-file> ]
|
||||||
|
[ content-disposition>> "name" of unquote ]
|
||||||
|
[ mime-parts>> set-at ] tri
|
||||||
|
] if ;
|
||||||
|
|
||||||
|
: save-mime-part ( multipart -- )
|
||||||
|
dup name>> empty-name? [
|
||||||
|
drop
|
||||||
|
] [
|
||||||
|
[ name-content>> ]
|
||||||
|
[ name>> unquote ]
|
||||||
|
[ mime-parts>> set-at ] tri
|
||||||
|
] if ;
|
||||||
|
|
||||||
|
: dump-mime-file ( multipart filename -- multipart )
|
||||||
|
binary <file-writer> [
|
||||||
|
dup mime-separator>> >>current-separator dump-until-separator
|
||||||
|
] with-output-stream ;
|
||||||
|
|
||||||
|
: dump-file ( multipart -- multipart )
|
||||||
|
[ "factor-" "-upload" unique-file ] with-temp-directory
|
||||||
|
[ >>temp-file ] [ dump-mime-file ] bi ;
|
||||||
|
|
||||||
|
: parse-content-disposition-form-data ( string -- hashtable )
|
||||||
|
";" split
|
||||||
|
[ "=" split1 [ [ blank? ] trim ] bi@ ] H{ } map>assoc ;
|
||||||
|
|
||||||
|
: lookup-disposition ( multipart string -- multipart value/f )
|
||||||
|
over content-disposition>> at ;
|
||||||
|
|
||||||
|
ERROR: unknown-content-disposition multipart ;
|
||||||
|
|
||||||
|
: parse-form-data ( multipart -- multipart )
|
||||||
|
"filename" lookup-disposition [
|
||||||
|
unquote
|
||||||
|
>>filename
|
||||||
|
[ dump-file ] [ save-uploaded-file ] bi
|
||||||
|
] [
|
||||||
|
"name" lookup-disposition [
|
||||||
|
[ dup mime-separator>> dump-string >>name-content ] dip
|
||||||
|
>>name dup save-mime-part
|
||||||
|
] [
|
||||||
|
unknown-content-disposition
|
||||||
|
] if*
|
||||||
|
] if* ;
|
||||||
|
|
||||||
|
ERROR: no-content-disposition multipart ;
|
||||||
|
|
||||||
|
: process-header ( multipart -- multipart )
|
||||||
|
dup "content-disposition" header ";" split1 swap {
|
||||||
|
{ "form-data" [
|
||||||
|
parse-content-disposition-form-data >>content-disposition
|
||||||
|
parse-form-data
|
||||||
|
] }
|
||||||
|
[ no-content-disposition ]
|
||||||
|
} case ;
|
||||||
|
|
||||||
|
: read-assert-sequence= ( sequence -- )
|
||||||
|
[ length read ] keep assert-sequence= ;
|
||||||
|
|
||||||
|
! : with-boundary ( boundary quote -- )
|
||||||
|
! [] dip
|
||||||
|
! current-boundary swap with-variable ; inline
|
||||||
|
|
||||||
|
: parse-beginning ( str boundary -- str' )
|
||||||
|
[ <sequence-parser> ] dip
|
||||||
|
[ take-sequence drop ] curry
|
||||||
|
[ take-rest ] bi ;
|
||||||
|
|
||||||
|
: parse-header ( str -- header str' )
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"\r\n\r\n" take-until-sequence*
|
||||||
|
string-lines harvest [ parse-header-line
|
||||||
|
parse-content-disposition-form-data ] map >hashtable
|
||||||
|
]
|
||||||
|
[ take-rest ] bi
|
||||||
|
] parse-sequence ;
|
||||||
|
|
||||||
|
: parse-multipart ( str separator -- seq )
|
||||||
|
"--" prepend "\r\n" append dup <regexp> [ parse-beginning ] dip re-split
|
||||||
|
[ [ from>> ] [ to>> ] [ seq>> ] tri subseq parse-header 2array ] map ;
|
||||||
|
|
|
@ -0,0 +1,499 @@
|
||||||
|
USING: kernel peg.parsers sequences sequences.parser make strings qw sequences.deep calendar calendar.english calendar.parser math math.parser math.order accessors combinators peg unicode parser arrays combinators.short-circuit splitting assocs vocabs.parser words slots.syntax quotations regexp documents.private ;
|
||||||
|
USING: prettyprint ;
|
||||||
|
|
||||||
|
IN: mail.parser
|
||||||
|
|
||||||
|
TUPLE: mailbox-field display-name local domain ;
|
||||||
|
|
||||||
|
TUPLE: received-field tokens date-time ;
|
||||||
|
|
||||||
|
TUPLE: content-type sub-type parameters ;
|
||||||
|
|
||||||
|
TUPLE: content-type/text < content-type ;
|
||||||
|
TUPLE: content-type/image < content-type ;
|
||||||
|
TUPLE: content-type/video < content-type ;
|
||||||
|
TUPLE: content-type/audio < content-type ;
|
||||||
|
TUPLE: content-type/application < content-type ;
|
||||||
|
TUPLE: content-type/multipart < content-type ;
|
||||||
|
TUPLE: content-type/unknown < content-type ;
|
||||||
|
|
||||||
|
TUPLE: mail-header
|
||||||
|
mime-version content-type
|
||||||
|
bcc cc comments date from in-reply-to keywords
|
||||||
|
message-id references reply-to sender subject to
|
||||||
|
{ optional-fields initial: { } } ;
|
||||||
|
|
||||||
|
TUPLE: multipart-header
|
||||||
|
content-type content-transfer-encoding content-id content-disposition ;
|
||||||
|
|
||||||
|
TUPLE: multipart header message ;
|
||||||
|
|
||||||
|
TUPLE: mail { header mail-header } body ;
|
||||||
|
|
||||||
|
! Core rules
|
||||||
|
|
||||||
|
: wsp-parser ( -- parser )
|
||||||
|
" " token "\t" token 2choice ;
|
||||||
|
|
||||||
|
: crlf-parser ( -- parser )
|
||||||
|
"\r\n" token hide ;
|
||||||
|
|
||||||
|
: special? ( ch -- ? )
|
||||||
|
"()<>[]:;@\\,.\"" member? ;
|
||||||
|
|
||||||
|
: except ( quot -- parser )
|
||||||
|
[ not ] compose satisfy [ 1string ] action ; inline
|
||||||
|
|
||||||
|
: except-these ( quots -- parser )
|
||||||
|
[ 1|| ] curry except ; inline
|
||||||
|
|
||||||
|
: digit-parser ( -- parser )
|
||||||
|
"0-9" range-pattern [ 1string ] action ;
|
||||||
|
|
||||||
|
: digit-parser2 ( -- parser )
|
||||||
|
digit-parser 2 exactly-n ;
|
||||||
|
|
||||||
|
: digit-parser4 ( -- parser )
|
||||||
|
digit-parser 4 exactly-n ;
|
||||||
|
|
||||||
|
: vchar-parser ( -- parser )
|
||||||
|
CHAR: \x20 CHAR: \x7e range [ 1string ] action ;
|
||||||
|
|
||||||
|
: quoted-pair ( -- parser )
|
||||||
|
CHAR: \ 1token vchar-parser wsp-parser 2choice 2seq ;
|
||||||
|
|
||||||
|
! Folding White Space and Comments
|
||||||
|
|
||||||
|
: ctext-parser ( -- parser )
|
||||||
|
{ [ control? ] [ "()" member? ] } except-these ;
|
||||||
|
|
||||||
|
: foldable-wsp-parser ( -- parser )
|
||||||
|
wsp-parser repeat0 crlf-parser 2seq optional
|
||||||
|
wsp-parser repeat1 2seq ;
|
||||||
|
|
||||||
|
: comment-parser ( -- parser )
|
||||||
|
ctext-parser [ comment-parser ] delay 2choice repeat0 "(" ")" surrounded-by hide ;
|
||||||
|
|
||||||
|
: comment-foldable-wsp-parser ( -- parser )
|
||||||
|
foldable-wsp-parser optional
|
||||||
|
comment-parser 2seq repeat1 foldable-wsp-parser optional 2seq
|
||||||
|
foldable-wsp-parser 2choice ;
|
||||||
|
|
||||||
|
! Atom
|
||||||
|
|
||||||
|
: atext-parser ( -- parser )
|
||||||
|
"a-zA-Z0-9!#$%&'*+-/=?^_`{}|~" range-pattern [ 1string ] action ;
|
||||||
|
|
||||||
|
: atom-parser ( -- parser )
|
||||||
|
comment-foldable-wsp-parser optional hide
|
||||||
|
atext-parser repeat1
|
||||||
|
comment-foldable-wsp-parser optional hide 3seq [ flatten "" concat-as ] action ;
|
||||||
|
|
||||||
|
: dot-atom-parser ( -- parser )
|
||||||
|
atom-parser "." token atom-parser 2seq repeat0 optional 2seq sp [ flatten "" concat-as ] action ;
|
||||||
|
|
||||||
|
! Quoted Strings
|
||||||
|
|
||||||
|
: quoted-string-parser ( -- parser )
|
||||||
|
string-parser ;
|
||||||
|
|
||||||
|
! Miscellaneous Tokens
|
||||||
|
|
||||||
|
: word-parser ( -- parser )
|
||||||
|
atom-parser quoted-string-parser 2choice ;
|
||||||
|
|
||||||
|
: phrase-parser ( -- parser )
|
||||||
|
word-parser repeat1 [ "" concat-as ] action ;
|
||||||
|
|
||||||
|
: unstructured-parser ( -- parser )
|
||||||
|
foldable-wsp-parser optional vchar-parser 2seq repeat0 wsp-parser repeat0 2seq
|
||||||
|
[ flatten "" concat-as ] action sp ;
|
||||||
|
|
||||||
|
! Date and Time Specification
|
||||||
|
|
||||||
|
: date-time-zone-parser ( -- parser )
|
||||||
|
foldable-wsp-parser hide "+" token "-" token 2choice digit-parser4 3seq
|
||||||
|
[ flatten "" concat-as parse-rfc822-gmt-offset ] action ;
|
||||||
|
|
||||||
|
: date-time-second-parser ( -- parser )
|
||||||
|
digit-parser2 [ "" concat-as string>number ] action
|
||||||
|
[ 0 60 between? ] semantic ;
|
||||||
|
|
||||||
|
: date-time-hour-parser ( -- parser )
|
||||||
|
digit-parser2 [ "" concat-as string>number ] action
|
||||||
|
[ 0 23 between? ] semantic ;
|
||||||
|
|
||||||
|
: date-time-minute-parser ( -- parser )
|
||||||
|
digit-parser2 [ "" concat-as string>number ] action
|
||||||
|
[ 0 59 between? ] semantic ;
|
||||||
|
|
||||||
|
: time-of-day-parser ( -- parser )
|
||||||
|
date-time-hour-parser ":" token hide
|
||||||
|
date-time-minute-parser ":" token hide
|
||||||
|
date-time-second-parser 2seq optional
|
||||||
|
[ [ first ] [ 0 ] if* ] action
|
||||||
|
4seq ;
|
||||||
|
|
||||||
|
: date-time-time-parser ( -- parser )
|
||||||
|
time-of-day-parser date-time-zone-parser 2seq ;
|
||||||
|
|
||||||
|
: date-time-year-parser ( -- parser )
|
||||||
|
foldable-wsp-parser hide
|
||||||
|
digit-parser 4 at-least-n
|
||||||
|
foldable-wsp-parser hide 3seq
|
||||||
|
[ flatten "" concat-as string>number ] action
|
||||||
|
[ 1900 >= ] semantic ;
|
||||||
|
|
||||||
|
: date-time-month-parser ( -- parse )
|
||||||
|
qw{ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec } [ token ] map choice
|
||||||
|
[ month-abbreviations index 1 + ] action ;
|
||||||
|
|
||||||
|
: date-time-day-parser ( -- parser )
|
||||||
|
foldable-wsp-parser optional hide
|
||||||
|
digit-parser 1 2 from-m-to-n
|
||||||
|
foldable-wsp-parser hide 3seq
|
||||||
|
[ flatten "" concat-as string>number ] action ;
|
||||||
|
|
||||||
|
: date-time-date-parser ( -- parser )
|
||||||
|
date-time-day-parser
|
||||||
|
date-time-month-parser
|
||||||
|
date-time-year-parser 3seq ;
|
||||||
|
|
||||||
|
: date-time-day-name-parser ( -- parser )
|
||||||
|
qw{ Mon Tue Wed Thu Fri Sat Sun } [ token ] map choice ;
|
||||||
|
|
||||||
|
: date-time-day-of-week-parser ( -- parser )
|
||||||
|
foldable-wsp-parser optional
|
||||||
|
date-time-day-name-parser 2seq ;
|
||||||
|
|
||||||
|
: date-time-parser ( -- parser )
|
||||||
|
date-time-day-of-week-parser "," token 2seq optional hide
|
||||||
|
date-time-date-parser
|
||||||
|
date-time-time-parser
|
||||||
|
comment-foldable-wsp-parser optional hide 4seq
|
||||||
|
[
|
||||||
|
flatten
|
||||||
|
[ timestamp new ] dip
|
||||||
|
[ ] each
|
||||||
|
{
|
||||||
|
[ >>day ] [ >>month ] [ >>year ]
|
||||||
|
[ >>hour ] [ >>minute ] [ >>second ]
|
||||||
|
[ >>gmt-offset ]
|
||||||
|
} spread
|
||||||
|
] action ;
|
||||||
|
|
||||||
|
! Addr-Spec Specification
|
||||||
|
|
||||||
|
: dtext-parser ( -- parser )
|
||||||
|
{ [ " []\\" member? ] [ control? ] } except-these repeat0 ;
|
||||||
|
|
||||||
|
: domain-literal-parser ( -- parser )
|
||||||
|
[
|
||||||
|
comment-foldable-wsp-parser optional ,
|
||||||
|
foldable-wsp-parser optional
|
||||||
|
dtext-parser 2seq
|
||||||
|
foldable-wsp-parser optional 2seq
|
||||||
|
"[" "]" surrounded-by ,
|
||||||
|
comment-foldable-wsp-parser optional ,
|
||||||
|
] seq* [ flatten "" concat-as ] action ;
|
||||||
|
|
||||||
|
: domain-parser ( -- parser )
|
||||||
|
dot-atom-parser domain-literal-parser 2choice ;
|
||||||
|
|
||||||
|
: local-part-parser ( -- parser )
|
||||||
|
dot-atom-parser quoted-string-parser 2choice ;
|
||||||
|
|
||||||
|
: addr-spec-parser ( -- parser )
|
||||||
|
local-part-parser "@" token hide domain-parser 3seq
|
||||||
|
[ f swap [ ] each mailbox-field boa ] action ;
|
||||||
|
|
||||||
|
! Address Specification
|
||||||
|
|
||||||
|
: display-name-parser ( -- parser )
|
||||||
|
phrase-parser ;
|
||||||
|
|
||||||
|
: angle-addr ( -- parser )
|
||||||
|
comment-foldable-wsp-parser optional hide
|
||||||
|
addr-spec-parser "<" ">" surrounded-by
|
||||||
|
comment-foldable-wsp-parser optional hide 3seq [ first ] action ;
|
||||||
|
|
||||||
|
: name-addr-parser ( -- parser )
|
||||||
|
display-name-parser optional angle-addr 2seq [ [ ] each swap >>display-name ] action ;
|
||||||
|
|
||||||
|
: mailbox-parser ( -- parser )
|
||||||
|
name-addr-parser addr-spec-parser 2choice ;
|
||||||
|
|
||||||
|
: mailbox-list-parser ( -- parser )
|
||||||
|
mailbox-parser "," token list-of [ >array ] action ;
|
||||||
|
|
||||||
|
DEFER: group-parser
|
||||||
|
|
||||||
|
: address-parser ( -- parser )
|
||||||
|
mailbox-parser group-parser 2choice [ ] action ;
|
||||||
|
|
||||||
|
: group-list-parser ( -- parser )
|
||||||
|
mailbox-list-parser comment-foldable-wsp-parser 2choice ;
|
||||||
|
|
||||||
|
: group-parser ( -- parser )
|
||||||
|
[
|
||||||
|
display-name-parser ,
|
||||||
|
":" token ,
|
||||||
|
group-list-parser optional ,
|
||||||
|
";" token ,
|
||||||
|
comment-foldable-wsp-parser optional hide ,
|
||||||
|
] seq* ;
|
||||||
|
|
||||||
|
: address-list-parser ( -- parser )
|
||||||
|
address-parser "," token list-of [ >array ] action ;
|
||||||
|
|
||||||
|
! Optional Fields
|
||||||
|
|
||||||
|
: ftext ( -- parser )
|
||||||
|
CHAR: \x21 CHAR: \x39 range
|
||||||
|
CHAR: \x3b CHAR: \x7e range
|
||||||
|
2choice [ 1string ] action ;
|
||||||
|
|
||||||
|
: known-field-name-parser ( str -- parser )
|
||||||
|
[ ftext repeat1 [ "" concat-as >lower ] action ] dip [ = ] curry semantic ;
|
||||||
|
|
||||||
|
: field-name-parser ( -- parser )
|
||||||
|
ftext repeat1 [ "" concat-as >lower ] action ;
|
||||||
|
|
||||||
|
: field-parser ( -- parser )
|
||||||
|
[
|
||||||
|
field-name-parser ,
|
||||||
|
":" token ,
|
||||||
|
unstructured-parser ,
|
||||||
|
crlf-parser ,
|
||||||
|
] seq* ;
|
||||||
|
|
||||||
|
: known-field-parser ( str parser -- parser )
|
||||||
|
[
|
||||||
|
known-field-name-parser
|
||||||
|
":" token hide
|
||||||
|
] dip
|
||||||
|
crlf-parser 4seq [ >array ] action ;
|
||||||
|
|
||||||
|
: no-fold-literal ( -- parser )
|
||||||
|
dtext-parser repeat0 "[" "]" surrounded-by ;
|
||||||
|
|
||||||
|
: id-left-parser ( -- parser )
|
||||||
|
dot-atom-parser ;
|
||||||
|
|
||||||
|
: id-right-parser ( -- parser )
|
||||||
|
dot-atom-parser no-fold-literal 2choice ;
|
||||||
|
|
||||||
|
: msg-id-parser ( -- parser )
|
||||||
|
comment-foldable-wsp-parser optional hide
|
||||||
|
id-left-parser "@" token id-right-parser 3seq [ "" concat-as ] action
|
||||||
|
"<" ">" surrounded-by
|
||||||
|
comment-foldable-wsp-parser optional hide 3seq [ first ] action ;
|
||||||
|
|
||||||
|
! The origination Date Field
|
||||||
|
|
||||||
|
: orig-date-parser ( -- parser )
|
||||||
|
"date" date-time-parser known-field-parser ;
|
||||||
|
|
||||||
|
! Originator fields
|
||||||
|
|
||||||
|
: from-parser ( -- parser )
|
||||||
|
"from" mailbox-list-parser known-field-parser ;
|
||||||
|
: sender-parser ( -- parser )
|
||||||
|
"sender" mailbox-parser known-field-parser ;
|
||||||
|
: reply-to-parser ( -- parser )
|
||||||
|
"reply-to" address-list-parser known-field-parser ;
|
||||||
|
|
||||||
|
! Destination Address fields
|
||||||
|
|
||||||
|
: to-parser ( -- parser )
|
||||||
|
"to" address-list-parser known-field-parser ;
|
||||||
|
: cc-parser ( -- parser )
|
||||||
|
"cc" address-list-parser known-field-parser ;
|
||||||
|
: bcc-parser ( -- parser )
|
||||||
|
"bcc" address-list-parser known-field-parser ;
|
||||||
|
|
||||||
|
: message-id-parser ( -- parser )
|
||||||
|
"message-id" msg-id-parser known-field-parser ;
|
||||||
|
|
||||||
|
: in-reply-to-parser ( -- parser )
|
||||||
|
"in-reply-to" msg-id-parser repeat1 [ >array ] action known-field-parser ;
|
||||||
|
|
||||||
|
: references-parser ( -- parser )
|
||||||
|
"references" msg-id-parser repeat1 [ >array ] action known-field-parser ;
|
||||||
|
|
||||||
|
: subject-parser ( -- parser )
|
||||||
|
"subject" unstructured-parser known-field-parser ;
|
||||||
|
|
||||||
|
: comments-parser ( -- parser )
|
||||||
|
"comments" unstructured-parser known-field-parser ;
|
||||||
|
|
||||||
|
: keywords-parser ( -- parser )
|
||||||
|
"keywords" phrase-parser sp "," token list-of [ >array ] action known-field-parser ;
|
||||||
|
|
||||||
|
! Resent Fields
|
||||||
|
|
||||||
|
: resent-date-parser ( -- parser )
|
||||||
|
"resent-date" date-time-parser known-field-parser ;
|
||||||
|
|
||||||
|
: resent-from-parser ( -- parser )
|
||||||
|
"resent-from" mailbox-list-parser known-field-parser ;
|
||||||
|
|
||||||
|
: resent-sender-parser ( -- parser )
|
||||||
|
"resent-sender" mailbox-parser known-field-parser ;
|
||||||
|
|
||||||
|
: resent-to-parser ( -- parser )
|
||||||
|
"resent-to" address-list-parser known-field-parser ;
|
||||||
|
|
||||||
|
: resent-cc-parser ( -- parser )
|
||||||
|
"resent-cc" address-list-parser known-field-parser ;
|
||||||
|
|
||||||
|
: resent-bcc-parser ( -- parser )
|
||||||
|
"resent-bcc" address-list-parser known-field-parser ;
|
||||||
|
|
||||||
|
: resent-message-id-parser ( -- parser )
|
||||||
|
"resent-message-id" msg-id-parser known-field-parser ;
|
||||||
|
|
||||||
|
! Trace Fields
|
||||||
|
|
||||||
|
: received-token-parser ( -- parser )
|
||||||
|
angle-addr addr-spec-parser domain-parser word-parser 4choice ;
|
||||||
|
|
||||||
|
: path-parser ( -- parser )
|
||||||
|
angle-addr comment-foldable-wsp-parser "<" ">" surrounded-by 2choice ;
|
||||||
|
|
||||||
|
: return-path-parser ( -- parser )
|
||||||
|
"return-path" path-parser known-field-parser ;
|
||||||
|
|
||||||
|
: received-parser ( -- parser )
|
||||||
|
"received"
|
||||||
|
received-token-parser repeat0 [ >array ] action
|
||||||
|
";" token hide
|
||||||
|
date-time-parser 3seq
|
||||||
|
[ [ ] each received-field boa ] action
|
||||||
|
known-field-parser ;
|
||||||
|
|
||||||
|
: trace-parser ( -- parser )
|
||||||
|
return-path-parser optional
|
||||||
|
received-parser repeat1 2seq [ flatten >array ] action ;
|
||||||
|
|
||||||
|
! Content-Type Field
|
||||||
|
|
||||||
|
: trim-quotes ( str -- str' )
|
||||||
|
[ CHAR: \" = ] trim ;
|
||||||
|
: trim-blanks ( str -- str' )
|
||||||
|
[ blank? ] trim ;
|
||||||
|
|
||||||
|
: parse-content-type ( str -- mime-type )
|
||||||
|
[
|
||||||
|
[ "/" take-until-sequence* >lower ]
|
||||||
|
[ ";" take-until-sequence* >lower ]
|
||||||
|
[ take-rest ";" split [ "=" split1 [ trim-blanks trim-quotes ] bi@ ] H{ } map>assoc ] tri rot
|
||||||
|
] parse-sequence >lower
|
||||||
|
{
|
||||||
|
{ "text" [ content-type/text boa ] }
|
||||||
|
{ "image" [ content-type/image boa ] }
|
||||||
|
{ "audio" [ content-type/audio boa ] }
|
||||||
|
{ "video" [ content-type/video boa ] }
|
||||||
|
{ "application" [ content-type/application boa ] }
|
||||||
|
{ "multipart" [ content-type/multipart boa ] }
|
||||||
|
{ "unknown" [ content-type/unknown boa ] }
|
||||||
|
[ drop content-type/unknown boa ]
|
||||||
|
} case ;
|
||||||
|
|
||||||
|
: content-type-parser ( -- parser )
|
||||||
|
"content-type" unstructured-parser [ parse-content-type ] action known-field-parser ;
|
||||||
|
|
||||||
|
: parse-content-disposition ( str -- content-disposition )
|
||||||
|
";" split [ "=" split1 [ trim-blanks trim-quotes ] bi@ ] H{ } map>assoc ;
|
||||||
|
|
||||||
|
: optional-field-parser ( -- parser )
|
||||||
|
field-name-parser ":" token hide unstructured-parser crlf-parser 4seq [ >array ] action ;
|
||||||
|
|
||||||
|
: known-multipart-field-parser ( str parser -- parser )
|
||||||
|
[
|
||||||
|
known-field-name-parser
|
||||||
|
":" token hide
|
||||||
|
] dip
|
||||||
|
3seq [ >array ] action ;
|
||||||
|
|
||||||
|
: multipart-header-field-parser ( -- parser )
|
||||||
|
"content-type" unstructured-parser [ parse-content-type ] action known-multipart-field-parser
|
||||||
|
"content-disposition" unstructured-parser [ parse-content-disposition ] action known-multipart-field-parser
|
||||||
|
"content-id" unstructured-parser known-multipart-field-parser
|
||||||
|
"content-transfer-encoding" unstructured-parser known-multipart-field-parser
|
||||||
|
4choice ;
|
||||||
|
|
||||||
|
PEG: parse-multipart-field-header ( str -- header ) multipart-header-field-parser ;
|
||||||
|
|
||||||
|
: parse-multipart-beginning ( str boundary -- str' )
|
||||||
|
[ <sequence-parser> ] dip
|
||||||
|
[ take-sequence drop ] curry
|
||||||
|
[ take-rest ] bi ;
|
||||||
|
|
||||||
|
: parse-multipart-header ( str -- header str' )
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"\r\n\r\n" take-until-sequence*
|
||||||
|
[
|
||||||
|
string-lines
|
||||||
|
multipart-header new
|
||||||
|
[
|
||||||
|
parse-multipart-field-header
|
||||||
|
[ second ] [ first ] bi >>writer-word
|
||||||
|
1quotation call( multipart-header value -- multipart-header )
|
||||||
|
] reduce
|
||||||
|
]
|
||||||
|
[ f ] if*
|
||||||
|
]
|
||||||
|
[ take-rest ] bi
|
||||||
|
] parse-sequence ;
|
||||||
|
|
||||||
|
|
||||||
|
: parse-multipart ( str separator -- seq )
|
||||||
|
"--" prepend "-?-?\r\n" append dup <regexp> [ parse-multipart-beginning ] dip re-split
|
||||||
|
[
|
||||||
|
[ from>> ] [ to>> ] [ seq>> ] tri subseq
|
||||||
|
parse-multipart-header [ multipart boa ] when*
|
||||||
|
] map sift ;
|
||||||
|
|
||||||
|
: fields-parser ( -- seq )
|
||||||
|
[
|
||||||
|
trace-parser ,
|
||||||
|
resent-date-parser ,
|
||||||
|
resent-from-parser ,
|
||||||
|
resent-sender-parser ,
|
||||||
|
resent-to-parser ,
|
||||||
|
resent-cc-parser ,
|
||||||
|
resent-bcc-parser ,
|
||||||
|
resent-message-id-parser ,
|
||||||
|
orig-date-parser ,
|
||||||
|
from-parser ,
|
||||||
|
sender-parser ,
|
||||||
|
reply-to-parser ,
|
||||||
|
to-parser ,
|
||||||
|
cc-parser ,
|
||||||
|
bcc-parser ,
|
||||||
|
message-id-parser ,
|
||||||
|
in-reply-to-parser ,
|
||||||
|
references-parser ,
|
||||||
|
subject-parser ,
|
||||||
|
comments-parser ,
|
||||||
|
keywords-parser ,
|
||||||
|
content-type-parser ,
|
||||||
|
optional-field-parser ,
|
||||||
|
] choice* repeat0
|
||||||
|
[
|
||||||
|
mail-header new
|
||||||
|
[
|
||||||
|
[ second ] [ first ] bi dup >>writer-word
|
||||||
|
[ swap drop 1quotation call( mail-header value -- mail-header ) ]
|
||||||
|
[
|
||||||
|
[ dup optional-fields>> ] 2dip
|
||||||
|
swap 2array 1array append >>optional-fields
|
||||||
|
] if*
|
||||||
|
] reduce
|
||||||
|
] action ;
|
||||||
|
|
||||||
|
PEG: parse-mail ( str -- mail )
|
||||||
|
fields-parser crlf-parser 2seq
|
||||||
|
[ first input-slice [ seq>> ] [ from>> ] bi tail mail boa ] action ;
|
Loading…
Reference in New Issue