Safe Haskell | None |
---|---|
Language | Haskell2010 |
MIME messages (RFC 2045, RFC 2046 and friends).
Synopsis
- data MIME
- mime :: Headers -> Parser MIME
- type MIMEMessage = Message EncStateWire MIME
- type WireEntity = Message EncStateWire ByteString
- type ByteEntity = Message EncStateByte ByteString
- type TextEntity = Message () Text
- data EncStateWire
- data EncStateByte
- entities :: Traversal' MIMEMessage WireEntity
- attachments :: Traversal' MIMEMessage WireEntity
- isAttachment :: MIMEMessage -> Bool
- transferDecoded :: (HasTransferEncoding a, AsTransferEncodingError e) => Getter a (Either e (TransferDecoded a))
- charsetDecoded :: (HasCharset a, AsCharsetError e) => CharsetLookup -> Getter a (Either e (Decoded a))
- decodeEncodedWords :: CharsetLookup -> ByteString -> Text
- contentType :: Lens' Headers ContentType
- data ContentType = ContentType (CI ByteString) (CI ByteString) Parameters
- ctType :: Lens' ContentType (CI ByteString)
- ctSubtype :: Lens' ContentType (CI ByteString)
- matchContentType :: CI ByteString -> Maybe (CI ByteString) -> ContentType -> Bool
- ctEq :: ContentType -> ContentType -> Bool
- parseContentType :: Parser ContentType
- showContentType :: ContentType -> Text
- mimeBoundary :: Traversal' ContentType ByteString
- contentTypeTextPlain :: ContentType
- contentTypeApplicationOctetStream :: ContentType
- contentTypeMultipartMixed :: ByteString -> ContentType
- defaultContentType :: ContentType
- contentDisposition :: Traversal' Headers ContentDisposition
- data ContentDisposition = ContentDisposition DispositionType Parameters
- data DispositionType
- = Inline
- | Attachment
- dispositionType :: Lens' ContentDisposition DispositionType
- filename :: HasParameters a => CharsetLookup -> Traversal' a Text
- filenameParameter :: HasParameters a => Lens' a (Maybe EncodedParameterValue)
- renderMessage :: MIMEMessage -> ByteString
- buildMessage :: MIMEMessage -> Builder
- headerFrom :: Lens' Headers [Mailbox]
- headerTo :: Lens' Headers [Address]
- headerCC :: Lens' Headers [Address]
- headerBCC :: Lens' Headers [Address]
- headerDate :: Lens' Headers UTCTime
- replyHeaderReferences :: Getter Headers (Maybe ByteString)
- createAttachmentFromFile :: ContentType -> FilePath -> IO MIMEMessage
- createAttachment :: ContentType -> Maybe FilePath -> ByteString -> MIMEMessage
- createTextPlainMessage :: Text -> MIMEMessage
- createMultipartMixedMessage :: ByteString -> [MIMEMessage] -> MIMEMessage
- type CharsetLookup = CI ByteString -> Maybe Charset
- defaultCharsets :: CharsetLookup
- module Data.RFC5322
- module Data.MIME.Parameter
- module Data.MIME.Error
Overview
This module extends RFC5322
with types for handling MIME
messages. It provides the mime
parsing helper function for
use with message
.
mime
:: Headers -> Parser MIME
message mime :: Parser (Message ctx MIME)
The Message
data type has a phantom type parameter for context.
In this module we use it to track whether the body has
content transfer encoding or charset encoding applied. Type
aliases are provided for convenince.
dataMessage
ctx a = Message Headers a dataEncStateWire
dataEncStateByte
typeMIMEMessage
= Message EncStateWire MIME typeWireEntity
= Message EncStateWire B.ByteString typeByteEntity
= Message EncStateByte B.ByteString typeTextEntity
= Message () T.Text
Folds are provided over all leaf entities, and entities that are identified as attachments:
entities
:: Fold MIMEMessage WireEntityattachments
:: Fold MIMEMessage WireEntity
Content transfer decoding is performed using the transferDecoded
optic. This will convert Quoted-Printable or Base64 encoded
entities into their decoded form.
transferDecoded
:: Getter WireEntity (EitherEncodingError
ByteEntity) transferDecoded :: Getter WireEntity (EitherEncodingError
ByteEntity) transferDecoded :: (HasTransferEncoding a, AsTransferEncodingError e) => Getter a (Either e (TransferDecoded a))
Charset decoding is performed using the charsetDecoded
optic:
charsetDecoded
:: Getter ByteEntity (EitherEncodingError
TextEntity) charsetDecoded :: Getter ByteEntity (EitherEncodingError
TextEntity) charsetDecoded :: (HasCharset a, AsCharsetError e) => Getter a (Either e (Decoded a))
Examples / HOWTO
Parse a MIME message:
parse
(message mime) :: ByteString -> Either String MIMEMessage
Find the first entity with the text/plain
content type:
getTextPlain ::MIMEMessage
-> MaybeWireEntity
getTextPlain = firstOf (entities
. filtered f) where f =matchContentType
"text" (Just "plain") . view (headers
.contentType
)
Perform content transfer decoding and charset decoding while preserving decode errors:
viewtransferDecoded
>=> viewcharsetDecoded
:: WireEntity -> Either EncodingError TextEntity
Get all attachments (transfer decoded) and their filenames (if specified):
getAttachments ::MIMEMessage
-> [(EitherEncodingError
B.ByteString, Maybe T.Text)] getAttachments = toListOf (attachments
. to (liftA2 (,) content name) where content = viewtransferDecodedBytes
name = preview (headers
.contentDisposition
.filename
)
Create an inline, plain text message and render it:
renderMessage $ createTextPlainMessage "This is a test body"
API
MIME data type
MIME message body. Either a single Part
, or Multipart
.
Only the body is represented; preamble and epilogue are not.
mime :: Headers -> Parser MIME Source #
Top-level MIME body parser that uses headers to decide how to parse the body.
Do not use this parser for parsing a nested message. This parser should only be used when the message you want to parse is the whole input. If you use it to parse a nested message it will treat the remainder of the outer message(s) as part of the epilogue.
Preambles and epilogues are discarded.
This parser accepts non-MIME messages, and unconditionally treats them as a single part.
type MIMEMessage = Message EncStateWire MIME Source #
type WireEntity = Message EncStateWire ByteString Source #
type ByteEntity = Message EncStateByte ByteString Source #
type TextEntity = Message () Text Source #
data EncStateWire Source #
Entity is formatted for transfer. Processing requires transfer decoding.
Instances
HasTransferEncoding WireEntity Source # | |
Defined in Data.MIME type TransferDecoded WireEntity :: Type Source # | |
type TransferDecoded WireEntity Source # | |
Defined in Data.MIME |
data EncStateByte Source #
Entity requires content-transfer-encoding to send, and may require charset decoding to read.
Instances
HasCharset ByteEntity Source # | RFC 2046 §4.1.2. defines the default character set to be US-ASCII. |
type Decoded ByteEntity Source # | |
Defined in Data.MIME |
Accessing and processing entities
entities :: Traversal' MIMEMessage WireEntity Source #
Get all leaf entities from the MIME message
attachments :: Traversal' MIMEMessage WireEntity Source #
Leaf entities with Content-Disposition: attachment
isAttachment :: MIMEMessage -> Bool Source #
MIMEMessage content disposition is an Attachment
transferDecoded :: (HasTransferEncoding a, AsTransferEncodingError e) => Getter a (Either e (TransferDecoded a)) Source #
Perform content transfer decoding.
charsetDecoded :: (HasCharset a, AsCharsetError e) => CharsetLookup -> Getter a (Either e (Decoded a)) Source #
Structure with the encoded data replaced with Text
Header processing
decodeEncodedWords :: CharsetLookup -> ByteString -> Text Source #
RFC 2047 and RFC 2231 define the encoded-words mechanism for embedding non-ASCII data in headers. This function locates encoded-words and decodes them.
λ> T.putStrLn $ decodeEncodedWords defaultCharsets "hello =?utf-8?B?5LiW55WM?=!" hello 世界!
If parsing fails or the encoding is unrecognised the encoded-word is left unchanged in the result.
λ> T.putStrLn $ decodeEncodedWords defaultCharsets "=?utf-8?B?bogus?=" =?utf-8?B?bogus?= λ> T.putStrLn $ decodeEncodedWords defaultCharsets "=?utf-8?X?unrecognised_encoding?=" =?utf-8?X?unrecognised_encoding?=
Language specification is supported (the datum is discarded).
λ> T.putStrLn $ decodeEncodedWords defaultCharsets "=?utf-8*es?Q?hola_mundo!?=" hola mundo!
Content-Type header
contentType :: Lens' Headers ContentType Source #
Lens to the content-type header. Probably not a lawful lens.
If the header is not specified or is syntactically invalid,
defaultContentType
is used. For more info see
https://tools.ietf.org/html/rfc2045#section-5.2.
If the Content-Transfer-Encoding is unrecognised, the
actual Content-Type value is ignored and
application/octet-stream
is returned, as required by
https://tools.ietf.org/html/rfc2049#section-2.
When setting, if the header already exists it is replaced, otherwise it is added. Unrecognised Content-Transfer-Encoding is ignored when setting.
data ContentType Source #
Content-Type header (RFC 2183).
Use parameters
to access the parameters.
Example:
ContentType "text" "plain" [("charset", "utf-8")]
Instances
ctType :: Lens' ContentType (CI ByteString) Source #
ctSubtype :: Lens' ContentType (CI ByteString) Source #
:: CI ByteString | type |
-> Maybe (CI ByteString) | optional subtype |
-> ContentType | |
-> Bool |
Match content type. If Nothing
is given for subtype, any
subtype is accepted.
ctEq :: ContentType -> ContentType -> Bool Source #
Deprecated: Use matchContentType
instead
Are the type and subtype the same? (parameters are ignored)
parseContentType :: Parser ContentType Source #
Parser for Content-Type header
showContentType :: ContentType -> Text Source #
Rendered content type field value for displaying
mimeBoundary :: Traversal' ContentType ByteString Source #
Get the boundary, if specified
Content-Type values
contentTypeTextPlain :: ContentType Source #
text/plain
contentTypeApplicationOctetStream :: ContentType Source #
application/octet-stream
contentTypeMultipartMixed :: ByteString -> ContentType Source #
multipart/mixed; boundary=asdf
defaultContentType :: ContentType Source #
text/plain; charset=us-ascii
Content-Disposition header
contentDisposition :: Traversal' Headers ContentDisposition Source #
Get Content-Disposition header.
Unrecognised disposition types are coerced to Attachment
in accordance with RFC 2183 §2.8 which states:
Unrecognized disposition types should be treated as attachment.
The fold may be empty, e.g. if the header is absent or unparseable.
data ContentDisposition Source #
Content-Disposition header (RFC 2183).
Use parameters
to access the parameters.
Instances
data DispositionType Source #
Instances
Eq DispositionType Source # | |
Defined in Data.MIME (==) :: DispositionType -> DispositionType -> Bool # (/=) :: DispositionType -> DispositionType -> Bool # | |
Show DispositionType Source # | |
Defined in Data.MIME showsPrec :: Int -> DispositionType -> ShowS # show :: DispositionType -> String # showList :: [DispositionType] -> ShowS # | |
Generic DispositionType Source # | |
Defined in Data.MIME type Rep DispositionType :: Type -> Type # from :: DispositionType -> Rep DispositionType x # to :: Rep DispositionType x -> DispositionType # | |
NFData DispositionType Source # | |
Defined in Data.MIME rnf :: DispositionType -> () # | |
type Rep DispositionType Source # | |
filename :: HasParameters a => CharsetLookup -> Traversal' a Text Source #
Traverse the value of the filename parameter (if present).
filenameParameter :: HasParameters a => Lens' a (Maybe EncodedParameterValue) Source #
Access the filename parameter as a Maybe (
.ParameterValue
B.ByteString)
This can be used to read or set the filename parameter (see also
the newParameter
convenience function):
λ> let hdrs = Headers [("Content-Disposition", "attachment")] λ> set (contentDisposition
.filenameParameter
) (Just (newParameter
"foo.txt")) hdrs Headers [("Content-Disposition","attachment; filename=foo.txt")]
Serialisation
renderMessage :: MIMEMessage -> ByteString Source #
Serialise a given MIMEMessage
into a ByteString. The message is
serialised as is. No additional headers are set.
buildMessage :: MIMEMessage -> Builder Source #
Serialise a given MIMEMessage
using a Builder
Mail creation
replyHeaderReferences :: Getter Headers (Maybe ByteString) Source #
Returns a space delimited ByteString
with values from identification
fields from the parents message Headers
. Rules to gather the values are in
accordance to RFC5322 - 3.6.4 as follows sorted by priority (first has
precedence):
* Values from References
and `Message-ID` (if any)
* Values from 'In-Reply-To' and 'Message-ID' (if any)
* Value from 'Message-ID' (in case it's the first reply to a parent mail)
* otherwise Nothing is returned indicating that the replying mail should not have a References
field.
createAttachment :: ContentType -> Maybe FilePath -> ByteString -> MIMEMessage Source #
Create an attachment from the given file contents. Optionally set the given filename parameter to the given file path.
createTextPlainMessage Source #
:: Text | message body |
-> MIMEMessage |
Create an inline, text/plain, utf-8 encoded message
createMultipartMixedMessage Source #
:: ByteString | Boundary |
-> [MIMEMessage] | attachments |
-> MIMEMessage |
Create a mixed MIMEMessage
with an inline text/plain part and multiple
attachments
Additional headers can be set (e.g. cc
) by using At
and Ixed
, for
example:
λ> set (at "subject") (Just "Hey there") $ Headers [] Headers [("subject", "Hey there")]
You can also use the Mailbox
instances:
λ> let address = Mailbox (Just "roman") (AddrSpec "roman" (DomainLiteral "192.168.1.1")) λ> set (at "cc") (Just $ renderMailbox address) $ Headers [] Headers [("cc", "\"roman\" roman@192.168.1.1")]
Re-exports
type CharsetLookup = CI ByteString -> Maybe Charset Source #
defaultCharsets :: CharsetLookup Source #
Supports US-ASCII, UTF-8 and ISO-8859-1.
module Data.RFC5322
module Data.MIME.Parameter
module Data.MIME.Error