mime-mail 0.0.0.4 → 0.1.0
raw patch · 2 files changed
+91/−20 lines, 2 filesdep ~blaze-builder
Dependency ranges changed: blaze-builder
Files
- Network/Mail/Mime.hs +89/−18
- mime-mail.cabal +2/−2
Network/Mail/Mime.hs view
@@ -16,12 +16,12 @@ , simpleMail -- * Utilities , randomString+ , quotedPrintable ) where import qualified Data.ByteString.Lazy as L import Blaze.ByteString.Builder.Char.Utf8 import Blaze.ByteString.Builder-import Blaze.ByteString.Builder.Internal.Write (fromWriteList) import Data.Monoid import System.Random import Control.Arrow@@ -34,6 +34,9 @@ import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.Encoding as LT import Data.ByteString.Char8 ()+import Data.Bits ((.&.), shiftR)+import Data.Char (isAscii)+import Data.Word (Word8) -- | Generates a random sequence of alphanumerics of the given length. randomString :: RandomGen d => Int -> d -> (String, d)@@ -58,7 +61,8 @@ -- | An entire mail message. data Mail = Mail- { mailHeaders :: [(String, String)] -- ^ All headers, including to, from subject.+ { -- | All headers, including to, from subject.+ mailHeaders :: [(String, String)] -- | A list of different sets of alternatives. As a concrete example: -- -- > mailParts = [ [textVersion, htmlVersion], [attachment1], [attachment1]]@@ -69,7 +73,7 @@ } -- | How to encode a single part. You should use 'Base64' for binary data.-data Encoding = None | Base64+data Encoding = None | Base64 | QuotedPrintableText | QuotedPrintableBinary -- | Multiple alternative representations of the same data. For example, you -- could provide a plain-text and HTML version of a message.@@ -82,6 +86,7 @@ -- | The filename for this part, if it is to be sent with an attachemnt -- disposition. , partFilename :: Maybe String+ , partHeaders :: [(String, String)] , partContent :: L.ByteString } @@ -89,24 +94,30 @@ type Pair = (Headers, Builder) partToPair :: Part -> Pair-partToPair (Part contentType encoding disposition content) =- (headers, builder)+partToPair (Part contentType encoding disposition headers content) =+ (headers', builder) where- headers =+ headers' = ((:) ("Content-Type", contentType)) $ (case encoding of None -> id- Base64 -> (:) ("Content-Transfer-Encoding", "base64"))+ Base64 -> (:) ("Content-Transfer-Encoding", "base64")+ QuotedPrintableText ->+ (:) ("Content-Transfer-Encoding", "quoted-printable")+ QuotedPrintableBinary ->+ (:) ("Content-Transfer-Encoding", "quoted-printable")) $ (case disposition of Nothing -> id Just fn -> (:) ("Content-Disposition", "attachment; filename=" ++ fn))- $ []+ $ headers builder = case encoding of- None -> fromWriteList writeByteString $ L.toChunks content- Base64 -> fromWriteList writeWord8 $ map (toEnum . fromEnum)+ None -> fromWrite16List writeByteString $ L.toChunks content+ Base64 -> fromWrite16List writeWord8 $ map (toEnum . fromEnum) $ encode $ L.unpack content+ QuotedPrintableText -> quotedPrintable True content+ QuotedPrintableBinary -> quotedPrintable False content showPairs :: RandomGen g => String -- ^ multipart type, eg mixed, alternative@@ -144,9 +155,9 @@ helper :: g -> [g -> (x, g)] -> ([x], g) helper g [] = ([], g) helper g (x:xs) =- let (b, g') = x g- (bs, g'') = helper g' xs- in (b : bs, g'')+ let (b, g_) = x g+ (bs, g__) = helper g_ xs+ in (b : bs, g__) ((finalHeaders, finalBuilder), g'') = showPairs "mixed" pairs' g' builder = mconcat [ mconcat $ map showHeader headers@@ -156,13 +167,20 @@ , finalBuilder ] +showHeader :: (String, String) -> Builder showHeader (k, v) = mconcat [ fromString k , fromByteString ": "- , fromString v+ , v'' , fromByteString "\n" ]+ where+ v' = LT.pack v+ v'' = if needsEncodedWord v'+ then encodedWord v'+ else fromLazyText v' +showBoundPart :: Boundary -> ([(String, String)], Builder) -> Builder showBoundPart (Boundary b) (headers, content) = mconcat [ fromByteString "--" , fromString b@@ -172,6 +190,7 @@ , content ] +showBoundEnd :: Boundary -> Builder showBoundEnd (Boundary b) = mconcat [ fromByteString "\n--" , fromString b@@ -224,11 +243,63 @@ , ("Subject", subject) ] , mailParts =- [ Part "text/plain; charset=utf-8" None Nothing+ [ Part "text/plain; charset=utf-8" QuotedPrintableText Nothing [] $ LT.encodeUtf8 plainBody- , Part "text/html; charset=utf-8" None Nothing+ , Part "text/html; charset=utf-8" QuotedPrintableText Nothing [] $ LT.encodeUtf8 htmlBody ] :- (map (\(ct, fn, content) -> [Part ct Base64 (Just fn) content])- as)+ (map (\(ct, fn, content) ->+ [Part ct Base64 (Just fn) [] content]) as) }++-- | The first parameter denotes whether the input should be treated as text.+-- If treated as text, then CRs will be stripped and LFs output as CRLFs. If+-- binary, then CRs and LFs will be escaped.+quotedPrintable :: Bool -> L.ByteString -> Builder+quotedPrintable isText lbs =+ fst $ L.foldl' go (mempty, 0 :: Int) lbs+ where+ go (front, lineLen) w =+ (front `mappend` b, lineLen')+ where+ (lineLen', b)+ | w == 13 && isText = (lineLen, mempty) -- CR+ | w == 10 && isText = (0, fromByteString "\r\n")+ | w == 61 = helper 3 $ fromByteString "=3D"+ | 33 <= w && w <= 126 = helper 1 $ fromWord8 w+ | (w == 9 || w == 0x20) && lineLen < 75 = helper 1 $ fromWord8 w+ | w == 9 = (0, fromByteString "=09=\r\n")+ | w == 0x20 = (0, fromByteString "=20=\r\n")+ | otherwise = helper 3 escaped+ helper newLen bs+ | newLen + lineLen > 78 =+ (0, bs `mappend` fromByteString "=\r\n")+ | otherwise = (newLen + lineLen, bs)+ escaped = fromWord8 61 `mappend` hex (w `shiftR` 4)+ `mappend` hex (w .&. 15)++hex :: Word8 -> Builder+hex x+ | x < 10 = fromWord8 $ x + 48+ | otherwise = fromWord8 $ x + 55++needsEncodedWord :: LT.Text -> Bool+needsEncodedWord = not . LT.all isAscii++encodedWord :: LT.Text -> Builder+encodedWord t = mconcat+ [ fromByteString "=?utf-8?Q?"+ , L.foldl' go mempty $ LT.encodeUtf8 t+ , fromByteString "?="+ ]+ where+ go front w = front `mappend` go' w+ go' 32 = fromWord8 95+ go' 95 = go'' 95+ go' 63 = go'' 63+ go' 61 = go'' 61+ go' w+ | 33 <= w && w <= 126 = fromWord8 w+ | otherwise = go'' w+ go'' w = fromWord8 61 `mappend` hex (w `shiftR` 4)+ `mappend` hex (w .&. 15)
mime-mail.cabal view
@@ -1,5 +1,5 @@ Name: mime-mail-Version: 0.0.0.4+Version: 0.1.0 Synopsis: Compose MIME email messages. Description: This package provides some high-level datatypes for declaring MIME email messages, functions for automatically composing these into bytestrings, and the ability to send bytestrings via the sendmail executable. You can also use any other library you wish to send via different methods, eg directly to SMTP. Homepage: http://github.com/snoyberg/mime-mail@@ -20,7 +20,7 @@ , dataenc >= 0.13.0.4 && < 0.14 , process >= 1.0 && < 1.1 , random >= 1.0 && < 1.1- , blaze-builder >= 0.2.1 && < 0.3+ , blaze-builder >= 0.2 && < 0.3 , bytestring >= 0.9.1 && < 0.10 , text >= 0.7 && < 0.12