2023-07-24 12:26:53 +02:00
use nom ::IResult ;
2023-07-14 19:12:34 +02:00
2023-07-24 19:19:49 +02:00
use crate ::header ::{ header , self } ;
use crate ::imf ;
2023-07-24 12:37:30 +02:00
use crate ::mime ;
2023-07-24 19:19:49 +02:00
use crate ::part ::{ self , AnyPart , field ::MixedField } ;
2023-07-23 16:37:47 +02:00
use crate ::text ::boundary ::{ boundary , Delimiter } ;
2023-07-23 09:46:57 +02:00
2023-07-24 12:26:53 +02:00
//--- Multipart
2023-07-23 13:25:33 +02:00
#[ derive(Debug, PartialEq) ]
2023-07-24 12:26:53 +02:00
pub struct Multipart < ' a > {
2023-07-24 13:53:59 +02:00
pub interpreted : mime ::Multipart < ' a > ,
2023-07-24 12:37:30 +02:00
pub children : Vec < AnyPart < ' a > > ,
2023-07-24 18:32:26 +02:00
pub preamble : & ' a [ u8 ] ,
pub epilogue : & ' a [ u8 ] ,
}
impl < ' a > Multipart < ' a > {
pub fn with_epilogue ( mut self , e : & ' a [ u8 ] ) -> Self {
self . epilogue = e ;
self
}
2023-07-22 14:38:43 +02:00
}
2023-07-14 19:12:34 +02:00
2023-07-23 16:37:47 +02:00
pub fn multipart < ' a > (
2023-07-24 13:53:59 +02:00
m : mime ::Multipart < ' a > ,
2023-07-23 16:37:47 +02:00
) -> impl Fn ( & ' a [ u8 ] ) -> IResult < & ' a [ u8 ] , Multipart < ' a > > {
2023-07-23 12:24:46 +02:00
let m = m . clone ( ) ;
move | input | {
let bound = m . 0. boundary . as_bytes ( ) ;
2023-07-24 18:32:26 +02:00
let ( mut input_loop , preamble ) = part ::part_raw ( bound ) ( input ) ? ;
2023-07-23 09:46:57 +02:00
let mut mparts : Vec < AnyPart > = vec! [ ] ;
2023-07-22 16:46:36 +02:00
loop {
2023-07-23 12:24:46 +02:00
let input = match boundary ( bound ) ( input_loop ) {
2023-07-24 12:37:30 +02:00
Err ( _ ) = > {
return Ok ( (
input_loop ,
Multipart {
interpreted : m . clone ( ) ,
children : mparts ,
2023-07-24 18:32:26 +02:00
preamble ,
epilogue : & [ ] ,
2023-07-24 12:37:30 +02:00
} ,
) )
}
Ok ( ( inp , Delimiter ::Last ) ) = > {
return Ok ( (
inp ,
Multipart {
interpreted : m . clone ( ) ,
children : mparts ,
2023-07-24 18:32:26 +02:00
preamble ,
epilogue : & [ ] ,
2023-07-24 12:37:30 +02:00
} ,
) )
}
2023-07-22 16:46:36 +02:00
Ok ( ( inp , Delimiter ::Next ) ) = > inp ,
} ;
2023-07-18 15:00:38 +02:00
2023-07-22 16:46:36 +02:00
// parse mime headers
2023-07-24 19:19:49 +02:00
let ( input , ( known , unknown , bad ) ) = header ( mime ::field ::content ) ( input ) ? ;
2023-07-24 18:01:37 +02:00
let mime = match m . 0. subtype {
2023-07-24 19:19:49 +02:00
mime ::r#type ::MultipartSubtype ::Digest = > mime ::field ::to_mime ::< mime ::WithDigestDefault > ( known ) . into ( ) ,
_ = > mime ::field ::to_mime ::< mime ::WithGenericDefault > ( known ) . into ( ) ,
2023-07-24 18:01:37 +02:00
} ;
2023-07-22 20:52:35 +02:00
2023-07-23 09:46:57 +02:00
// parse raw part
2023-07-24 12:26:53 +02:00
let ( input , rpart ) = part ::part_raw ( bound ) ( input ) ? ;
2023-07-23 09:46:57 +02:00
2023-07-22 20:52:35 +02:00
// parse mime body
2023-07-24 12:26:53 +02:00
mparts . push ( part ::to_anypart ( mime , rpart ) ) ;
2023-07-14 19:12:34 +02:00
2023-07-22 16:46:36 +02:00
input_loop = input ;
}
}
2023-07-18 15:00:38 +02:00
}
2023-07-24 12:26:53 +02:00
//--- Message
#[ derive(Debug, PartialEq) ]
pub struct Message < ' a > {
2023-07-24 13:53:59 +02:00
pub interpreted : mime ::Message < ' a > ,
2023-07-24 12:26:53 +02:00
pub imf : imf ::Imf < ' a > ,
pub child : Box < AnyPart < ' a > > ,
2023-07-24 18:32:26 +02:00
pub epilogue : & ' a [ u8 ] ,
2023-07-14 19:12:34 +02:00
}
2023-07-24 18:32:26 +02:00
impl < ' a > Message < ' a > {
pub fn with_epilogue ( mut self , e : & ' a [ u8 ] ) -> Self {
self . epilogue = e ;
self
}
}
2023-07-14 19:12:34 +02:00
2023-07-24 12:26:53 +02:00
pub fn message < ' a > (
2023-07-24 13:53:59 +02:00
m : mime ::Message < ' a > ,
2023-07-24 12:26:53 +02:00
) -> impl Fn ( & ' a [ u8 ] ) -> IResult < & ' a [ u8 ] , Message < ' a > > {
move | input : & [ u8 ] | {
2023-07-24 19:19:49 +02:00
let ( input , ( known , unknown , bad ) ) : ( _ , ( Vec ::< MixedField > , Vec < header ::Kv > , Vec < & [ u8 ] > ) ) =
2023-07-24 12:37:30 +02:00
header ( part ::field ::mixed_field ) ( input ) ? ;
2023-07-24 19:19:49 +02:00
let ( in_mime , imf ) = part ::field ::sections ::< mime ::WithGenericDefault > ( known ) ;
2023-07-24 12:26:53 +02:00
let part = part ::to_anypart ( in_mime , input ) ;
2023-07-24 12:37:30 +02:00
Ok ( (
& [ ] ,
Message {
interpreted : m . clone ( ) ,
imf ,
child : Box ::new ( part ) ,
2023-07-24 18:32:26 +02:00
epilogue : & [ ] ,
2023-07-24 12:37:30 +02:00
} ,
) )
2023-07-17 17:14:08 +02:00
}
2023-07-17 11:44:55 +02:00
}
2023-07-14 19:12:34 +02:00
#[ cfg(test) ]
mod tests {
use super ::* ;
2023-07-24 12:26:53 +02:00
use crate ::part ::discrete ::Text ;
use crate ::part ::AnyPart ;
2023-07-24 12:37:30 +02:00
use crate ::text ::encoding ::{ Base64Word , EncodedWord , QuotedChunk , QuotedWord } ;
use crate ::text ::misc_token ::{ Phrase , UnstrToken , Unstructured , Word } ;
2023-07-23 16:20:38 +02:00
use chrono ::{ FixedOffset , TimeZone } ;
2023-07-14 19:12:34 +02:00
2023-07-17 17:14:08 +02:00
#[ test ]
fn test_multipart ( ) {
2023-07-24 13:53:59 +02:00
let base_mime = mime ::Multipart (
2023-07-23 13:25:33 +02:00
mime ::r#type ::Multipart {
subtype : mime ::r#type ::MultipartSubtype ::Alternative ,
boundary : " simple boundary " . to_string ( ) ,
} ,
2023-07-24 13:53:59 +02:00
mime ::Generic ::default ( ) ,
2023-07-23 13:25:33 +02:00
) ;
2023-07-17 17:14:08 +02:00
assert_eq! (
2023-07-23 13:25:33 +02:00
multipart ( base_mime . clone ( ) ) ( b " This is the preamble. It is to be ignored, though it
2023-07-17 17:14:08 +02:00
is a handy place for composition agents to include an
explanatory note to non - MIME conformant readers .
- - simple boundary
This is implicitly typed plain US - ASCII text .
It does NOT end with a linebreak .
- - simple boundary
Content - type : text / plain ; charset = us - ascii
This is explicitly typed plain US - ASCII text .
It DOES end with a linebreak .
- - simple boundary - -
This is the epilogue . It is also to be ignored .
" ),
Ok ( ( & b " \n This is the epilogue. It is also to be ignored. \n " [ .. ] ,
2023-07-24 12:26:53 +02:00
Multipart {
interpreted : base_mime ,
2023-07-24 18:32:26 +02:00
preamble : & b " This is the preamble. It is to be ignored, though it \n is a handy place for composition agents to include an \n explanatory note to non-MIME conformant readers. \n " [ .. ] ,
epilogue : & b " " [ .. ] ,
2023-07-24 12:26:53 +02:00
children : vec ! [
2023-07-24 12:37:30 +02:00
AnyPart ::Txt ( Text {
2023-07-24 13:53:59 +02:00
interpreted : mime ::Text (
2023-07-23 13:25:33 +02:00
mime ::r#type ::Text {
subtype : mime ::r#type ::TextSubtype ::Plain ,
charset : mime ::charset ::EmailCharset ::US_ASCII ,
} ,
2023-07-24 13:53:59 +02:00
mime ::Generic ::default ( ) ,
2023-07-23 13:25:33 +02:00
) ,
2023-07-24 12:26:53 +02:00
body : & b " This is implicitly typed plain US-ASCII text. \n It does NOT end with a linebreak. " [ .. ] ,
} ) ,
AnyPart ::Txt ( Text {
2023-07-24 13:53:59 +02:00
interpreted : mime ::Text (
2023-07-23 13:25:33 +02:00
mime ::r#type ::Text {
subtype : mime ::r#type ::TextSubtype ::Plain ,
charset : mime ::charset ::EmailCharset ::US_ASCII ,
} ,
2023-07-24 13:53:59 +02:00
mime ::Generic ::default ( ) ,
2023-07-23 13:25:33 +02:00
) ,
2023-07-24 12:26:53 +02:00
body : & b " This is explicitly typed plain US-ASCII text. \n It DOES end with a linebreak. \n " [ .. ] ,
} ) ,
2023-07-23 13:25:33 +02:00
] ,
2023-07-24 12:26:53 +02:00
} ,
2023-07-23 13:25:33 +02:00
) )
2023-07-17 17:14:08 +02:00
) ;
}
2023-07-23 16:20:38 +02:00
#[ test ]
fn test_message ( ) {
let fullmail : & [ u8 ] = r #" Date: Sat, 8 Jul 2023 07:14:29 +0200
From : Grrrnd Zero < grrrndzero @ example . org >
To : John Doe < jdoe @ machine . example >
CC : = ? ISO - 8859 - 1 ? Q ? Andr = E9 ? = Pirard < PIRARD @ vm1 . ulg . ac . be >
Subject : = ? ISO - 8859 - 1 ? B ? SWYgeW91IGNhbiByZWFkIHRoaXMgeW8 = ? =
= ? ISO - 8859 - 2 ? B ? dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg = = ? =
X - Unknown : something something
Bad entry
on multiple lines
Message - ID : < NTAxNzA2AC47634Y366BAMTY4ODc5MzQyODY0ODY5 @ www . grrrndzero . org >
MIME - Version : 1.0
Content - Type : multipart / alternative ;
boundary = " b1_e376dc71bafc953c0b0fdeb9983a9956 "
Content - Transfer - Encoding : 7 bit
This is a multi - part message in MIME format .
- - b1_e376dc71bafc953c0b0fdeb9983a9956
Content - Type : text / plain ; charset = utf - 8
Content - Transfer - Encoding : quoted - printable
GZ
OoOoO
oOoOoOoOo
oOoOoOoOoOoOoOoOo
oOoOoOoOoOoOoOoOoOoOoOo
oOoOoOoOoOoOoOoOoOoOoOoOoOoOo
OoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO
- - b1_e376dc71bafc953c0b0fdeb9983a9956
Content - Type : text / html ; charset = us - ascii
< div style = " text-align: center; " > < strong > GZ < / strong > < br / >
OoOoO < br / >
oOoOoOoOo < br / >
oOoOoOoOoOoOoOoOo < br / >
oOoOoOoOoOoOoOoOoOoOoOo < br / >
oOoOoOoOoOoOoOoOoOoOoOoOoOoOo < br / >
OoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO < br / >
< / div >
- - b1_e376dc71bafc953c0b0fdeb9983a9956 - -
2023-07-23 16:37:47 +02:00
" #
. as_bytes ( ) ;
2023-07-23 16:20:38 +02:00
2023-07-24 13:53:59 +02:00
let base_mime = mime ::Message ::default ( ) ;
2023-07-23 16:20:38 +02:00
assert_eq! (
message ( base_mime . clone ( ) ) ( fullmail ) ,
Ok ( (
& [ ] [ .. ] ,
2023-07-24 12:26:53 +02:00
Message {
interpreted : base_mime ,
2023-07-24 18:32:26 +02:00
epilogue : & b " " [ .. ] ,
2023-07-24 12:26:53 +02:00
imf : imf ::Imf {
2023-07-23 16:20:38 +02:00
date : Some ( FixedOffset ::east_opt ( 2 * 3600 )
. unwrap ( )
. with_ymd_and_hms ( 2023 , 07 , 8 , 7 , 14 , 29 )
. unwrap ( ) ) ,
from : vec ! [
imf ::mailbox ::MailboxRef {
name : Some ( Phrase ( vec! [ Word ::Atom ( & b " Grrrnd " [ .. ] ) , Word ::Atom ( & b " Zero " [ .. ] ) ] ) ) ,
addrspec : imf ::mailbox ::AddrSpec {
local_part : imf ::mailbox ::LocalPart ( vec! [
imf ::mailbox ::LocalPartToken ::Word ( Word ::Atom ( & b " grrrndzero " [ .. ] ) )
] ) ,
domain : imf ::mailbox ::Domain ::Atoms ( vec! [ & b " example " [ .. ] , & b " org " [ .. ] ] ) ,
}
} ,
] ,
to : vec ! [ imf ::address ::AddressRef ::Single ( imf ::mailbox ::MailboxRef {
name : Some ( Phrase ( vec! [ Word ::Atom ( & b " John " [ .. ] ) , Word ::Atom ( & b " Doe " [ .. ] ) ] ) ) ,
addrspec : imf ::mailbox ::AddrSpec {
local_part : imf ::mailbox ::LocalPart ( vec! [
imf ::mailbox ::LocalPartToken ::Word ( Word ::Atom ( & b " jdoe " [ .. ] ) )
] ) ,
domain : imf ::mailbox ::Domain ::Atoms ( vec! [ & b " machine " [ .. ] , & b " example " [ .. ] ] ) ,
}
} ) ] ,
cc : vec ! [ imf ::address ::AddressRef ::Single ( imf ::mailbox ::MailboxRef {
name : Some ( Phrase ( vec! [
Word ::Encoded ( EncodedWord ::Quoted ( QuotedWord {
enc : encoding_rs ::WINDOWS_1252 ,
chunks : vec ! [
QuotedChunk ::Safe ( & b " Andr " [ .. ] ) ,
2023-07-23 18:27:03 +02:00
QuotedChunk ::Encoded ( vec! [ 0xE9 ] ) ,
2023-07-23 16:20:38 +02:00
] ,
} ) ) ,
Word ::Atom ( & b " Pirard " [ .. ] )
] ) ) ,
addrspec : imf ::mailbox ::AddrSpec {
local_part : imf ::mailbox ::LocalPart ( vec! [
imf ::mailbox ::LocalPartToken ::Word ( Word ::Atom ( & b " PIRARD " [ .. ] ) )
] ) ,
domain : imf ::mailbox ::Domain ::Atoms ( vec! [
& b " vm1 " [ .. ] , & b " ulg " [ .. ] , & b " ac " [ .. ] , & b " be " [ .. ] ,
] ) ,
}
} ) ] ,
subject : Some ( Unstructured ( vec! [
UnstrToken ::Encoded ( EncodedWord ::Base64 ( Base64Word {
enc : encoding_rs ::WINDOWS_1252 ,
content : & b " SWYgeW91IGNhbiByZWFkIHRoaXMgeW8 " [ .. ] ,
2023-07-23 16:37:47 +02:00
} ) ) ,
2023-07-23 16:20:38 +02:00
UnstrToken ::Encoded ( EncodedWord ::Base64 ( Base64Word {
enc : encoding_rs ::ISO_8859_2 ,
content : & b " dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg " [ .. ] ,
} ) ) ,
] ) ) ,
msg_id : Some ( imf ::identification ::MessageID {
left : & b " NTAxNzA2AC47634Y366BAMTY4ODc5MzQyODY0ODY5 " [ .. ] ,
right : & b " www.grrrndzero.org " [ .. ] ,
} ) ,
mime_version : Some ( imf ::mime ::Version { major : 1 , minor : 0 } ) ,
2023-07-24 11:09:21 +02:00
.. imf ::Imf ::default ( )
2023-07-23 16:20:38 +02:00
} ,
2023-07-24 12:26:53 +02:00
child : Box ::new ( AnyPart ::Mult ( Multipart {
2023-07-24 13:53:59 +02:00
interpreted : mime ::Multipart (
2023-07-23 16:20:38 +02:00
mime ::r#type ::Multipart {
subtype : mime ::r#type ::MultipartSubtype ::Alternative ,
boundary : " b1_e376dc71bafc953c0b0fdeb9983a9956 " . to_string ( ) ,
} ,
2023-07-24 13:53:59 +02:00
mime ::Generic ::default ( ) ,
2023-07-23 16:20:38 +02:00
) ,
2023-07-24 18:32:26 +02:00
preamble : & b " This is a multi-part message in MIME format. \n " [ .. ] ,
epilogue : & b " " [ .. ] ,
2023-07-24 12:26:53 +02:00
children : vec ! [
AnyPart ::Txt ( Text {
2023-07-24 13:53:59 +02:00
interpreted : mime ::Text (
2023-07-23 16:20:38 +02:00
mime ::r#type ::Text {
subtype : mime ::r#type ::TextSubtype ::Plain ,
charset : mime ::charset ::EmailCharset ::UTF_8 ,
} ,
2023-07-24 13:53:59 +02:00
mime ::Generic {
2023-07-23 16:20:38 +02:00
transfer_encoding : mime ::mechanism ::Mechanism ::QuotedPrintable ,
2023-07-24 13:53:59 +02:00
.. mime ::Generic ::default ( )
2023-07-23 16:20:38 +02:00
}
) ,
2023-07-24 12:26:53 +02:00
body : & b " GZ \n OoOoO \n oOoOoOoOo \n oOoOoOoOoOoOoOoOo \n oOoOoOoOoOoOoOoOoOoOoOo \n oOoOoOoOoOoOoOoOoOoOoOoOoOoOo \n OoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO \n " [ .. ] ,
} ) ,
AnyPart ::Txt ( Text {
2023-07-24 13:53:59 +02:00
interpreted : mime ::Text (
2023-07-23 16:20:38 +02:00
mime ::r#type ::Text {
subtype : mime ::r#type ::TextSubtype ::Html ,
charset : mime ::charset ::EmailCharset ::US_ASCII ,
} ,
2023-07-24 13:53:59 +02:00
mime ::Generic ::default ( ) ,
2023-07-23 16:20:38 +02:00
) ,
2023-07-24 12:26:53 +02:00
body : & br #" <div style= " text - align : center ; " ><strong>GZ</strong><br />
2023-07-23 16:20:38 +02:00
OoOoO < br / >
oOoOoOoOo < br / >
oOoOoOoOoOoOoOoOo < br / >
oOoOoOoOoOoOoOoOoOoOoOo < br / >
oOoOoOoOoOoOoOoOoOoOoOoOoOoOo < br / >
OoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO < br / >
< / div >
" #[..],
2023-07-24 12:26:53 +02:00
} ) ,
2023-07-23 16:20:38 +02:00
] ,
2023-07-24 12:26:53 +02:00
} ) ) ,
} ,
2023-07-23 16:20:38 +02:00
) )
) ;
}
2023-07-14 19:12:34 +02:00
}