Resource.hs 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  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 qualified Data.SMTP.Types.Mime as Mime
  6. import Text.StringConvert
  7. import Control.Applicative ((<|>))
  8. import Control.Monad
  9. import Data.Default.Class
  10. import Data.Attoparsec.ByteString.Char8
  11. import qualified Data.Attoparsec.ByteString.Char8 as A
  12. import qualified Data.Attoparsec.ByteString.Lazy as Alz
  13. import qualified Data.ByteString as BS
  14. import Data.ByteString (ByteString)
  15. import qualified Data.Word8 as W
  16. import qualified Data.Char as C
  17. {- |
  18. Splits the data into headers and body,
  19. also, parses the headers, returning both
  20. the bare headers data and the parsed result.
  21. Returns: (headers data, headers, body)
  22. Notice that this function chomps the lone CRLF
  23. that separates the body from the headers.
  24. -}
  25. takeHeaders :: ResourceData -> (PlainHeaders, ResourceData)
  26. takeHeaders dt = case Alz.parse parseHeaders dt of
  27. Alz.Fail{} -> (def, dt)
  28. Alz.Done dtb hh -> (hh, dtb)
  29. parseHeaders :: Parser PlainHeaders
  30. parseHeaders = do
  31. (pp, psep) <- parseHeadersGroup
  32. let (Mime.ContentTypeHeader ct _) = (fst $ getMimeData pp)
  33. if ct == Mime.MessageMime Mime.FcmtpResource
  34. then do
  35. -- Has sealed headers
  36. (ss, ssep) <- parseHeadersGroup
  37. return $ PlainHeaders pp ss (psep, ssep)
  38. else
  39. return $ PlainHeaders pp [] (psep, "")
  40. parseHeadersGroup :: Parser ([Header], ByteString)
  41. parseHeadersGroup = (
  42. blankLine >>= (\l -> return ([], l))
  43. ) <|> (
  44. do
  45. h <- header
  46. (hh, l) <- parseHeadersGroup
  47. return (h:hh, l)
  48. )
  49. -- header :: Parser Header
  50. -- header = do
  51. -- key <- A.takeTill (== ':')
  52. -- char ':'
  53. -- if BS.null key then failParser else return ()
  54. -- skipSpace
  55. -- value <- scan Value headerValueScanner
  56. -- let key' = s . BS.reverse . (BS.dropWhile W.isSpace) . BS.reverse . (BS.dropWhile W.isSpace) $ key
  57. -- let value' = s . BS.reverse . (BS.dropWhile A.isEndOfLine) . BS.reverse $ value
  58. -- return $ Header (key', value')
  59. header :: Parser Header
  60. header = do
  61. k <- A.takeTill (\x -> elem x [':', '\r', '\n']) -- OverloadedStrings!
  62. char ':'
  63. when (BS.null k) $ fail "email header must have a name"
  64. sp <- A.takeWhile isSpace
  65. v <- scan Value headerValueScanner
  66. let k' = s . BS.reverse . BS.dropWhile W.isSpace . BS.reverse . BS.dropWhile W.isSpace $ k
  67. let v' = s . BS.reverse . BS.dropWhile A.isEndOfLine . BS.reverse $ v
  68. let b = BS.concat [k, ":", sp, v]
  69. return $ Header k' v' b
  70. data HeaderScanStatus = Value | CR | LF
  71. headerValueScanner :: HeaderScanStatus -> Char -> Maybe HeaderScanStatus
  72. headerValueScanner Value c
  73. | c == '\r' = Just CR
  74. | c == '\n' = Just LF
  75. | otherwise = Just Value
  76. headerValueScanner CR c
  77. | c == '\r' = Just CR
  78. | c == '\n' = Just LF
  79. | otherwise = Nothing
  80. headerValueScanner LF c
  81. | isHorizontalSpace . fromIntegral . C.ord $ c = Just Value
  82. | otherwise = Nothing
  83. blankLine :: Parser ByteString
  84. blankLine = string "\n" <|> string "\r\n"