purebred-email-0.4.1: types and parser for email messages (including MIME)

Safe HaskellNone
LanguageHaskell2010

Data.RFC5322

Contents

Description

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.

data Message ctx a = Message Headers a

Headers and body can be accessed via the headers, header and body optics.

headers :: Lens' (Message ctx a) Headers
header :: CI B.ByteString -> Fold Headers B.ByteString
body :: 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 =
  case parse (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 (view headers msg))
      putStrLn $ "body length: " <> show (B.length (view body msg))
Synopsis

Message types

data Message s a Source #

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.

Constructors

Message Headers a 
Instances
HasCharset ByteEntity Source #

RFC 2046 §4.1.2. defines the default character set to be US-ASCII.

Instance details

Defined in Data.MIME

Associated Types

type Decoded ByteEntity :: Type Source #

HasTransferEncoding WireEntity Source # 
Instance details

Defined in Data.MIME

Associated Types

type TransferDecoded WireEntity :: Type Source #

Functor (Message s) Source # 
Instance details

Defined in Data.RFC5322

Methods

fmap :: (a -> b) -> Message s a -> Message s b #

(<$) :: a -> Message s b -> Message s a #

EqMessage a => Eq (Message s a) Source # 
Instance details

Defined in Data.RFC5322

Methods

(==) :: Message s a -> Message s a -> Bool #

(/=) :: Message s a -> Message s a -> Bool #

Show a => Show (Message s a) Source # 
Instance details

Defined in Data.RFC5322

Methods

showsPrec :: Int -> Message s a -> ShowS #

show :: Message s a -> String #

showList :: [Message s a] -> ShowS #

Generic (Message s a) Source # 
Instance details

Defined in Data.RFC5322

Associated Types

type Rep (Message s a) :: Type -> Type #

Methods

from :: Message s a -> Rep (Message s a) x #

to :: Rep (Message s a) x -> Message s a #

NFData a => NFData (Message s a) Source # 
Instance details

Defined in Data.RFC5322

Methods

rnf :: Message s a -> () #

HasHeaders (Message s a) Source # 
Instance details

Defined in Data.RFC5322

type Decoded ByteEntity Source # 
Instance details

Defined in Data.MIME

type TransferDecoded WireEntity Source # 
Instance details

Defined in Data.MIME

type Rep (Message s a) Source # 
Instance details

Defined in Data.RFC5322

type Rep (Message s a) = D1 (MetaData "Message" "Data.RFC5322" "purebred-email-0.4.1-LKGlLUD09Vd8pTVewOembz" False) (C1 (MetaCons "Message" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Headers) :*: S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 a)))

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 # 
Instance details

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

Constructors

RequiredBody (Parser a) 
OptionalBody (Parser a, a)

If body is present run parser, otherwise use constant value

NoBody a 

body :: Lens (Message ctx a) (Message ctx' b) a b Source #

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

Minimal complete definition

Nothing

Methods

eqMessage :: Message s a -> Message s a -> Bool Source #

eqMessage :: Eq a => Message s a -> Message s a -> Bool Source #

Instances
EqMessage MIME Source #

Ignores the presence/absense of MIME-Version header

Instance details

Defined in Data.MIME

Headers

class HasHeaders a where Source #

Instances
HasHeaders Headers Source # 
Instance details

Defined in Data.RFC5322

HasHeaders (Message s a) Source # 
Instance details

Defined in Data.RFC5322

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.

newtype Headers Source #

Constructors

Headers [Header] 
Instances
Eq Headers Source # 
Instance details

Defined in Data.RFC5322

Methods

(==) :: Headers -> Headers -> Bool #

(/=) :: Headers -> Headers -> Bool #

Show Headers Source # 
Instance details

Defined in Data.RFC5322

Generic Headers Source # 
Instance details

Defined in Data.RFC5322

Associated Types

type Rep Headers :: Type -> Type #

Methods

from :: Headers -> Rep Headers x #

to :: Rep Headers x -> Headers #

Semigroup Headers Source # 
Instance details

Defined in Data.RFC5322

Monoid Headers Source # 
Instance details

Defined in Data.RFC5322

NFData Headers Source # 
Instance details

Defined in Data.RFC5322

Methods

rnf :: Headers -> () #

Ixed Headers Source # 
Instance details

Defined in Data.RFC5322

At Headers Source #

Acts upon the first occurrence of the header only.

Instance details

Defined in Data.RFC5322

HasHeaders Headers Source # 
Instance details

Defined in Data.RFC5322

type Rep Headers Source # 
Instance details

Defined in Data.RFC5322

type Rep Headers = D1 (MetaData "Headers" "Data.RFC5322" "purebred-email-0.4.1-LKGlLUD09Vd8pTVewOembz" True) (C1 (MetaCons "Headers" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 [Header])))
type Index Headers Source # 
Instance details

Defined in Data.RFC5322

type IxValue Headers Source # 
Instance details

Defined in Data.RFC5322

Addresses

data Address Source #

Constructors

Single Mailbox 
Group Text [Mailbox] 
Instances
Eq Address Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Methods

(==) :: Address -> Address -> Bool #

(/=) :: Address -> Address -> Bool #

Show Address Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Generic Address Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Associated Types

type Rep Address :: Type -> Type #

Methods

from :: Address -> Rep Address x #

to :: Rep Address x -> Address #

NFData Address Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Methods

rnf :: Address -> () #

type Rep Address Source # 
Instance details

Defined in Data.RFC5322.Address.Types

data AddrSpec Source #

Constructors

AddrSpec ByteString Domain 
Instances
Eq AddrSpec Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Show AddrSpec Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Generic AddrSpec Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Associated Types

type Rep AddrSpec :: Type -> Type #

Methods

from :: AddrSpec -> Rep AddrSpec x #

to :: Rep AddrSpec x -> AddrSpec #

NFData AddrSpec Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Methods

rnf :: AddrSpec -> () #

type Rep AddrSpec Source # 
Instance details

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

data Domain Source #

Instances
Eq Domain Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Methods

(==) :: Domain -> Domain -> Bool #

(/=) :: Domain -> Domain -> Bool #

Show Domain Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Generic Domain Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Associated Types

type Rep Domain :: Type -> Type #

Methods

from :: Domain -> Rep Domain x #

to :: Rep Domain x -> Domain #

NFData Domain Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Methods

rnf :: Domain -> () #

type Rep Domain Source # 
Instance details

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

data Mailbox Source #

Constructors

Mailbox (Maybe Text) AddrSpec 
Instances
Eq Mailbox Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Methods

(==) :: Mailbox -> Mailbox -> Bool #

(/=) :: Mailbox -> Mailbox -> Bool #

Show Mailbox Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Generic Mailbox Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Associated Types

type Rep Mailbox :: Type -> Type #

Methods

from :: Mailbox -> Rep Mailbox x #

to :: Rep Mailbox x -> Mailbox #

NFData Mailbox Source # 
Instance details

Defined in Data.RFC5322.Address.Types

Methods

rnf :: Mailbox -> () #

type Rep Mailbox Source # 
Instance details

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

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.

parsed :: Cons s s Word8 Word8 => Parser a -> Fold s a Source #

Given a parser, construct a Fold

See parse for discussion of performance.

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

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.

Minimal complete definition

buildBody

Methods

buildBody :: Headers -> a -> Maybe Builder Source #

Build the body. If there should be no body (as distinct from empty body) return Nothing

tweakHeaders :: Headers -> Headers Source #

Allows tweaking the headers before rendering. Default implementation is a no-op.

Instances
RenderMessage MIME Source #

Sets the MIME-Version: 1.0 header.

Instance details

Defined in Data.MIME