Safe Haskell | None |
---|---|
Language | Haskell2010 |
Email messages. Deals specifically with RFC 5322, which is stricter than RFC 822 or RFC 2822. If you have to deal with messages that comply with the older specifications but not RFC 5322, preprocess the input and massage it to be RFC 5322 compliant.
This parser allows LF line endings in addition to CRLF (RFC 5322 demands CRLF but LF-only is common in on-disk formats).
The main parsing function is message
. It takes a second function
that can inspect the headers to determine how to parse the body.
message
:: (Headers
->BodyHandler
a) -> Parser (Message ctx a)
The Message
type is parameterised over the body type, and a
phantom type that can be used for context.
dataMessage
ctx a = MessageHeaders
a
Headers and body can be accessed via the headers
, header
and
body
optics.
headers
:: Lens' (Message ctx a) Headersheader
:: CI B.ByteString -> Fold Headers B.ByteStringbody
:: Lens (Message ctx a) (Message ctx' b) a b
The following example program parses an input, interpreting the body
as a raw ByteString
, and prints the subject (if present), the
number of headers and the body length. The message context type is
()
.
analyse :: B.ByteString -> IO () analyse input = caseparse
(message
(const takeByteString)) of Left errMsg -> hPutStrLn stderr errMsg *> exitFailure Right (msg :: Message () B.ByteString) -> do B.putStrLn $ "subject: " <> foldOf (headers
.header
"subject") msg putStrLn $ "num headers: " <> show (length (viewheaders
msg)) putStrLn $ "body length: " <> show (B.length (viewbody
msg))
Synopsis
- data Message s a = Message Headers a
- message :: (Headers -> BodyHandler a) -> Parser (Message (MessageContext a) a)
- type family MessageContext a
- data BodyHandler a
- = RequiredBody (Parser a)
- | OptionalBody (Parser a, a)
- | NoBody a
- body :: Lens (Message ctx a) (Message ctx' b) a b
- class EqMessage a where
- type Header = (CI ByteString, ByteString)
- class HasHeaders a where
- header :: HasHeaders a => CI ByteString -> Traversal' a ByteString
- headerList :: HasHeaders a => Lens' a [(CI ByteString, ByteString)]
- newtype Headers = Headers [Header]
- data Address
- address :: CharsetLookup -> Parser Address
- addressList :: CharsetLookup -> Parser [Address]
- data AddrSpec = AddrSpec ByteString Domain
- data Domain
- data Mailbox = Mailbox (Maybe Text) AddrSpec
- mailbox :: CharsetLookup -> Parser Mailbox
- mailboxList :: CharsetLookup -> Parser [Mailbox]
- parse :: Cons s s Word8 Word8 => Parser a -> s -> Either String a
- parsed :: Cons s s Word8 Word8 => Parser a -> Fold s a
- parsePrint :: Parser a -> (a -> ByteString) -> Prism' ByteString a
- crlf :: Alternative (f s) => CharParsing f s a => f s ()
- quotedString :: (Alternative (f s), CharParsing f s a, SM s) => f s s
- field :: Parser (CI ByteString, ByteString)
- rfc5422DateTimeFormat :: String
- rfc5422DateTimeFormatLax :: String
- buildMessage :: forall ctx a. RenderMessage a => Message ctx a -> Builder
- renderMessage :: RenderMessage a => Message ctx a -> ByteString
- class RenderMessage a where
- renderRFC5422Date :: UTCTime -> ByteString
- buildFields :: Headers -> Builder
- buildField :: (CI ByteString, ByteString) -> Builder
- renderAddressSpec :: AddrSpec -> ByteString
- renderMailbox :: Mailbox -> ByteString
- renderMailboxes :: [Mailbox] -> ByteString
- renderAddress :: Address -> ByteString
- renderAddresses :: [Address] -> ByteString
Message types
Message type, parameterised over context and body type. The context type is not used in this module but is provided for uses such as tracking the transfer/charset encoding state in MIME messages.
Instances
message :: (Headers -> BodyHandler a) -> Parser (Message (MessageContext a) a) Source #
Parse a message. The function argument receives the headers and yields a handler for the message body.
type family MessageContext a Source #
Instances
type MessageContext MIME Source # | |
Defined in Data.MIME |
data BodyHandler a Source #
Specify how to handle a message body, including the possibility of optional bodies and no body (which is distinct from empty body).
RequiredBody (Parser a) | |
OptionalBody (Parser a, a) | If body is present run parser, otherwise use constant value |
NoBody a |
class EqMessage a where Source #
How to compare messages with this body type.
This class arises because we may want to tweak the headers, possibly in response to body data, or vice-versa, when comparing messages.
The default implementation compares headers and body using (==).
Nothing
Headers
type Header = (CI ByteString, ByteString) Source #
class HasHeaders a where Source #
header :: HasHeaders a => CI ByteString -> Traversal' a ByteString Source #
Target all values of the given header
headerList :: HasHeaders a => Lens' a [(CI ByteString, ByteString)] Source #
Access headers as a list of key/value pairs.
Instances
Eq Headers Source # | |
Show Headers Source # | |
Generic Headers Source # | |
Semigroup Headers Source # | |
Monoid Headers Source # | |
NFData Headers Source # | |
Defined in Data.RFC5322 | |
Ixed Headers Source # | |
Defined in Data.RFC5322 | |
At Headers Source # | Acts upon the first occurrence of the header only. |
HasHeaders Headers Source # | |
type Rep Headers Source # | |
Defined in Data.RFC5322 | |
type Index Headers Source # | |
Defined in Data.RFC5322 | |
type IxValue Headers Source # | |
Defined in Data.RFC5322 |
Addresses
Instances
Eq Address Source # | |
Show Address Source # | |
Generic Address Source # | |
NFData Address Source # | |
Defined in Data.RFC5322.Address.Types | |
type Rep Address Source # | |
Defined in Data.RFC5322.Address.Types type Rep Address = D1 (MetaData "Address" "Data.RFC5322.Address.Types" "purebred-email-0.4.1-LKGlLUD09Vd8pTVewOembz" False) (C1 (MetaCons "Single" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Mailbox)) :+: C1 (MetaCons "Group" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Text) :*: S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 [Mailbox]))) |
addressList :: CharsetLookup -> Parser [Address] Source #
Instances
Eq AddrSpec Source # | |
Show AddrSpec Source # | |
Generic AddrSpec Source # | |
NFData AddrSpec Source # | |
Defined in Data.RFC5322.Address.Types | |
type Rep AddrSpec Source # | |
Defined in Data.RFC5322.Address.Types type Rep AddrSpec = D1 (MetaData "AddrSpec" "Data.RFC5322.Address.Types" "purebred-email-0.4.1-LKGlLUD09Vd8pTVewOembz" False) (C1 (MetaCons "AddrSpec" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 ByteString) :*: S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Domain))) |
Instances
Eq Domain Source # | |
Show Domain Source # | |
Generic Domain Source # | |
NFData Domain Source # | |
Defined in Data.RFC5322.Address.Types | |
type Rep Domain Source # | |
Defined in Data.RFC5322.Address.Types type Rep Domain = D1 (MetaData "Domain" "Data.RFC5322.Address.Types" "purebred-email-0.4.1-LKGlLUD09Vd8pTVewOembz" False) (C1 (MetaCons "DomainDotAtom" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 (NonEmpty ByteString))) :+: C1 (MetaCons "DomainLiteral" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 ByteString))) |
Instances
Eq Mailbox Source # | |
Show Mailbox Source # | |
Generic Mailbox Source # | |
NFData Mailbox Source # | |
Defined in Data.RFC5322.Address.Types | |
type Rep Mailbox Source # | |
Defined in Data.RFC5322.Address.Types type Rep Mailbox = D1 (MetaData "Mailbox" "Data.RFC5322.Address.Types" "purebred-email-0.4.1-LKGlLUD09Vd8pTVewOembz" False) (C1 (MetaCons "Mailbox" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 (Maybe Text)) :*: S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 AddrSpec))) |
mailboxList :: CharsetLookup -> Parser [Mailbox] Source #
Parsers
parse :: Cons s s Word8 Word8 => Parser a -> s -> Either String a Source #
Parse an a
.
The input is convered to a lazy ByteString
.
Build with rewrite rules enabled (-O
, cabal's default)
to achieve the following conversion overheads:
- Lazy
ByteString
: no conversion - Strict
ByteString
: O(1) conversion [Word8]
: O(n) conversion
It is recommended to use strict bytestring input. Parsing a lazy bytestring will cause numerous parser buffer resizes. The lazy chunks in the input can be GC'd but the buffer keeps growing so you don't actually keep the memory usage low by using a lazy bytestring.
parsePrint :: Parser a -> (a -> ByteString) -> Prism' ByteString a Source #
Construct a prism from a parser and a printer
crlf :: Alternative (f s) => CharParsing f s a => f s () Source #
Either CRLF or LF (lots of mail programs transform CRLF to LF)
quotedString :: (Alternative (f s), CharParsing f s a, SM s) => f s s Source #
Helpers
field :: Parser (CI ByteString, ByteString) Source #
Serialisation
buildMessage :: forall ctx a. RenderMessage a => Message ctx a -> Builder Source #
Construct a Builder
for the message. This allows efficient
streaming to IO handles.
renderMessage :: RenderMessage a => Message ctx a -> ByteString Source #
Render a message to a lazy ByteString
. (You will probably not
need a strict ByteString
and it is inefficient for most use cases.)
class RenderMessage a where Source #
Define how to render an RFC 5322 message with given payload type.
buildFields :: Headers -> Builder Source #
buildField :: (CI ByteString, ByteString) -> Builder Source #
renderMailbox :: Mailbox -> ByteString Source #
renderMailboxes :: [Mailbox] -> ByteString Source #
renderAddress :: Address -> ByteString Source #
renderAddresses :: [Address] -> ByteString Source #