Resource.hs 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. {-# LANGUAGE OverloadedStrings #-}
  2. -- Parser of email text
  3. module Data.SMTP.Parser.Resource (takeHeaders, parseHeaders) where
  4. import Data.SMTP.Types.Resource
  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 :: ResourceData -> ([ByteString], [Header], ResourceData)
  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. blankLine >> return ([], [])
  32. ) <|> (
  33. do
  34. (dth, h) <- headerAndReturn
  35. (dthh, hh) <- parseHeadersAndReturn
  36. return (dth:dthh, h:hh)
  37. )
  38. -- header :: Parser Header
  39. -- header = do
  40. -- key <- A.takeTill (== ':')
  41. -- char ':'
  42. -- if BS.null key then failParser else return ()
  43. -- skipSpace
  44. -- value <- scan Value headerValueScanner
  45. -- let key' = s . BS.reverse . (BS.dropWhile W.isSpace) . BS.reverse . (BS.dropWhile W.isSpace) $ key
  46. -- let value' = s . BS.reverse . (BS.dropWhile A.isEndOfLine) . BS.reverse $ value
  47. -- return $ Header (key', value')
  48. headerAndReturn :: Parser (ByteString, Header)
  49. headerAndReturn = do
  50. key <- A.takeTill (== ':')
  51. char ':'
  52. when (BS.null key) $ fail "email header must have a name"
  53. sp <- A.takeWhile isSpace
  54. value <- scan Value headerValueScanner
  55. let key' = s . BS.reverse . BS.dropWhile W.isSpace . BS.reverse . BS.dropWhile W.isSpace $ key
  56. let value' = s . BS.reverse . BS.dropWhile A.isEndOfLine . BS.reverse $ value
  57. return (BS.concat [key, sp, value], Header (key', value'))
  58. data HeaderScanStatus = Value | CR | LF
  59. headerValueScanner :: HeaderScanStatus -> Char -> Maybe HeaderScanStatus
  60. headerValueScanner Value c
  61. | c == '\r' = Just CR
  62. | c == '\n' = Just LF
  63. | otherwise = Just Value
  64. headerValueScanner CR c
  65. | c == '\r' = Just CR
  66. | c == '\n' = Just LF
  67. | otherwise = Nothing
  68. headerValueScanner LF c
  69. | isHorizontalSpace . fromIntegral .C.ord $ c = Just Value
  70. | otherwise = Nothing
  71. blankLine :: Parser ByteString
  72. blankLine = string "\n" <|> string "\r\n"