Email.hs 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. {-# LANGUAGE OverloadedStrings #-}
  2. -- Parser of email text
  3. module Data.SMTP.Parser.Email (parseHeaders, takeHeaders) where
  4. import Data.SMTP.Types.Email
  5. import Text.StringConvert
  6. import Control.Applicative ((<|>))
  7. import Control.Monad
  8. import Data.Attoparsec.ByteString.Char8
  9. import qualified Data.Attoparsec.ByteString.Char8 as A
  10. import qualified Data.Attoparsec.ByteString.Lazy as Alz
  11. import qualified Data.ByteString as BS
  12. import Data.ByteString (ByteString)
  13. import qualified Data.Word8 as W
  14. import qualified Data.Char as C
  15. {- |
  16. Splits the data into headers and body,
  17. also, parses the headers, returning both
  18. the bare headers data and the parsed result.
  19. Returns: (headers data, headers, body)
  20. Notice that this function chomps the lone CRLF
  21. that separates the body from the headers.
  22. -}
  23. takeHeaders :: EmailData -> ([ByteString], [Header], EmailData)
  24. takeHeaders dt = case Alz.parse parseHeadersAndReturn dt of
  25. Alz.Fail{} -> ([], [], dt)
  26. Alz.Done dtb (dth, hh) -> (dth, hh, dtb)
  27. parseHeaders :: Parser [Header]
  28. parseHeaders = snd <$> parseHeadersAndReturn
  29. parseHeadersAndReturn :: Parser ([ByteString], [Header])
  30. parseHeadersAndReturn = (
  31. do
  32. blankLine
  33. return ([], [])
  34. ) <|> (
  35. do
  36. (dth, h) <- headerAndReturn
  37. (dthh, hh) <- parseHeadersAndReturn
  38. return (dth:dthh, h:hh)
  39. )
  40. -- header :: Parser Header
  41. -- header = do
  42. -- key <- A.takeTill (== ':')
  43. -- char ':'
  44. -- if BS.null key then failParser else return ()
  45. -- skipSpace
  46. -- value <- scan Value headerValueScanner
  47. -- let key' = s . BS.reverse . (BS.dropWhile W.isSpace) . BS.reverse . (BS.dropWhile W.isSpace) $ key
  48. -- let value' = s . BS.reverse . (BS.dropWhile A.isEndOfLine) . BS.reverse $ value
  49. -- return $ Header (key', value')
  50. headerAndReturn :: Parser (ByteString, Header)
  51. headerAndReturn = do
  52. key <- A.takeTill (== ':')
  53. char ':'
  54. when (BS.null key) $ fail "email header must have a name"
  55. sp <- A.takeWhile isSpace
  56. value <- scan Value headerValueScanner
  57. let key' = s . BS.reverse . BS.dropWhile W.isSpace . BS.reverse . BS.dropWhile W.isSpace $ key
  58. let value' = s . BS.reverse . BS.dropWhile A.isEndOfLine . BS.reverse $ value
  59. return (BS.concat [key, sp, value], Header (key', value'))
  60. data HeaderScanStatus = Value | CR | LF
  61. headerValueScanner :: HeaderScanStatus -> Char -> Maybe HeaderScanStatus
  62. headerValueScanner Value c
  63. | c == '\r' = Just CR
  64. | c == '\n' = Just LF
  65. | otherwise = Just Value
  66. headerValueScanner CR c
  67. | c == '\r' = Just CR
  68. | c == '\n' = Just LF
  69. | otherwise = Nothing
  70. headerValueScanner LF c
  71. | isHorizontalSpace . fromIntegral .C.ord $ c = Just Value
  72. | otherwise = Nothing
  73. blankLine :: Parser ByteString
  74. blankLine = string "\n" <|> string "\r\n"