Response.hs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. {-# LANGUAGE OverloadedStrings #-}
  2. module Data.SMTP.Response (
  3. ResponseStatus(..),
  4. Response(..),
  5. parseResponse,
  6. parseLineResponse,
  7. renderResponse,
  8. renderLineResponse,
  9. )where
  10. import qualified Data.Attoparsec.ByteString.Char8 as A
  11. import Data.Attoparsec.ByteString.Char8.Extras
  12. import Data.Default.Class
  13. import Data.ByteString (ByteString)
  14. import qualified Data.ByteString as BS
  15. import Text.StringConvert
  16. data ResponseStatus = Preliminary | Completion | Intermediate | TransientError | PermanentError
  17. deriving (Read, Show, Ord, Eq)
  18. data Response = Response {status :: ResponseStatus, respLines :: [ByteString],
  19. code :: Int, extended :: Maybe (Int, Int, Int), message :: ByteString}
  20. deriving (Read, Show, Ord, Eq)
  21. instance Default Response where
  22. def = Response PermanentError [] 500 Nothing ""
  23. parseLineResponse :: A.Parser Response
  24. parseLineResponse = do
  25. (cd, xcd, m, _) <- parseLine
  26. return $ Response (statusFromCode cd) [] cd xcd m
  27. parseResponse :: A.Parser Response
  28. parseResponse = do
  29. (cd, xcd, m, moreLines) <- parseLine
  30. A.endOfLine
  31. lns <- parseLines moreLines
  32. return $ Response (statusFromCode cd) lns cd xcd m
  33. parseLines :: Bool -> A.Parser [ByteString]
  34. parseLines False = return []
  35. parseLines True = do
  36. (_, _, m, moreLines) <- parseLine
  37. A.endOfLine
  38. lns <- parseLines moreLines
  39. return $ m : lns
  40. parseLine :: A.Parser (Int, Maybe (Int, Int, Int), ByteString, Bool)
  41. parseLine = A.choice [
  42. do
  43. cd <- A.decimal
  44. A.string "-"
  45. xcd <- parsexcode "-"
  46. m <- A.takeTill (A.isEndOfLine. asW8)
  47. return (cd, xcd, m, True),
  48. do
  49. cd <- A.decimal
  50. A.string " "
  51. xcd <- parsexcode " "
  52. m <- A.takeTill (A.isEndOfLine . asW8)
  53. return (cd, xcd, m, False)
  54. ]
  55. parsexcode :: ByteString -> A.Parser (Maybe (Int, Int, Int))
  56. parsexcode sep = A.choice [
  57. do
  58. c1 <- A.decimal
  59. A.string "."
  60. c2 <- A.decimal
  61. A.string "."
  62. c3 <- A.decimal
  63. A.string sep
  64. return $ Just (c1, c2, c3),
  65. return Nothing
  66. ]
  67. statusFromCode :: Int -> ResponseStatus
  68. statusFromCode c
  69. | c < 100 = PermanentError
  70. | c < 200 = Preliminary
  71. | c < 300 = Completion
  72. | c < 400 = Intermediate
  73. | c < 500 = TransientError
  74. | otherwise = PermanentError
  75. renderResponse :: Response -> ByteString
  76. renderResponse (Response _ lns cd ext msg) = let
  77. tst = showBS cd
  78. sep = if null lns then " " else "-"
  79. enh = case ext of
  80. Nothing -> ""
  81. Just (s1, s2, s3) -> BS.intercalate "." $ map showBS [s1, s2, s3]
  82. in BS.concat $ [tst, sep, enh, if BS.null enh then "" else sep, msg, "\r\n"] ++ renderLines tst enh lns
  83. where
  84. renderLines _ _ [] = []
  85. renderLines tst enh (l:ll) = let
  86. sep = if null ll then " " else "-"
  87. in [tst, sep, enh, if BS.null enh then "" else sep, l, "\r\n"] ++ renderLines tst enh ll
  88. renderLineResponse :: Response -> ByteString
  89. renderLineResponse (Response _ _ cd ext msg) = let
  90. tst = showBS cd
  91. sep = " "
  92. enh = case ext of
  93. Nothing -> ""
  94. Just (s1, s2, s3) -> BS.intercalate "." $ map showBS [s1, s2, s3]
  95. in BS.concat [tst, sep, enh, if BS.null enh then "" else sep, msg]
  96. showBS :: Show a => a -> ByteString
  97. showBS = s . show