Resource.hs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. {-# LANGUAGE OverloadedStrings #-}
  2. module Data.SMTP.Types.Resource where
  3. import qualified Data.SMTP.URI as URI
  4. import qualified Data.SMTP.Types.Mime as Mime
  5. import qualified Data.SMTP.Parser.Mime as PMime
  6. import qualified Data.Attoparsec.ByteString as BA
  7. import qualified Data.ByteString as BS
  8. import qualified Data.ByteString.Lazy as LBS
  9. import Data.ByteString.Base64 as Base64
  10. import qualified Text.StringConvert as SC
  11. import Text.Read (readMaybe)
  12. import Data.Maybe
  13. import Data.Default.Class
  14. import qualified Data.Char as C
  15. data Header = Header {key :: String, value :: String, bare :: BS.ByteString} deriving (Read, Show, Eq, Ord)
  16. data PlainHeaders = PlainHeaders {public :: [Header], sealed :: [Header], separators :: (BS.ByteString, BS.ByteString)} deriving (Read, Show, Eq, Ord)
  17. instance Default PlainHeaders where
  18. def = PlainHeaders [] [] ("\r\n", "")
  19. data ResourceInfo = ResourceInfo {
  20. mimeType :: Mime.ContentType,
  21. mimeParameters :: Mime.ContentTypeParameters,
  22. revision :: URI.Revision,
  23. bases :: [URI.Revision],
  24. address :: [URI.URI]
  25. } deriving (Eq, Ord, Show)
  26. instance Default ResourceInfo where
  27. def = ResourceInfo def def URI.NoRevision [] []
  28. type ResourceData = LBS.ByteString
  29. -- | Creates a header from a key and a value
  30. makeHeader :: String -> String -> Header
  31. makeHeader k v = Header k v . SC.s $ k ++ ": " ++ v ++ "\r\n"
  32. -- | True if the header key matches
  33. hasKey :: String -> Header -> Bool
  34. hasKey k h = map C.toLower k == key h
  35. -- | Retrieves a single header with the given key
  36. getHeader :: [Header] -> String -> Maybe Header
  37. getHeader hh k = case getMultiple hh k of
  38. [] -> Nothing
  39. (h:_) -> Just h
  40. -- | Retrieves the value of the header that matches key
  41. getHeaderValue :: [Header] -> String -> Maybe String
  42. getHeaderValue hh k = value <$> getHeader hh k
  43. {- |
  44. Retrieves the value of a boolean header,
  45. accepting the common true strings:
  46. true, t, yes, y, 1
  47. -}
  48. getBooleanHeader :: [Header] -> String -> Maybe Bool
  49. getBooleanHeader hh s = (\k -> map C.toLower k `elem` ["true", "t", "yes", "y", "1"]) <$> getHeaderValue hh s
  50. -- | Retrieves the binary value of a base64 encoded header
  51. getBase64Header :: [Header] -> String -> Maybe BS.ByteString
  52. getBase64Header hh s = getHeaderValue hh s >>= (rightToJust . Base64.decode . SC.s)
  53. where
  54. rightToJust = either (\_ -> Nothing) Just
  55. -- | Gets the reader value, and converts it with readMaybe
  56. getReadHeader :: Read a => [Header] -> String -> Maybe a
  57. getReadHeader hh s = getHeaderValue hh s >>= readMaybe
  58. -- | Retrieves all headers that match the key
  59. getMultiple :: [Header] -> String -> [Header]
  60. getMultiple hh k = filter (hasKey k) hh
  61. -- | Retrieves all the values of the headers that match the key
  62. getMultipleValue :: [Header] -> String -> [String]
  63. getMultipleValue hh k = map value $ getMultiple hh k
  64. -- | fCMTP revision header
  65. revisionHeader :: String
  66. revisionHeader = "fCMTP-Revision"
  67. -- | fCMTP base revision header
  68. baseHeader :: String
  69. baseHeader = "fCMTP-Revision-Base"
  70. -- | fCMTP resource URI header
  71. addressHeader :: String
  72. addressHeader = "fCMTP-Address"
  73. -- | Retrieves the meta information from resource headers
  74. resourceInfo :: PlainHeaders -> ResourceInfo
  75. resourceInfo (PlainHeaders _ ss _) = let
  76. (mimet, mimep) = fromMaybe (def, def) $ getHeaderValue ss Mime.contentTypeHeaderName >>= bparse PMime.parseContentType
  77. curr = fromMaybe URI.NoRevision $ URI.Revision <$> getHeaderValue ss revisionHeader
  78. bb = map URI.Revision $ getMultipleValue ss baseHeader
  79. uu = catMaybes . map (bparse URI.parseURI) $ getMultipleValue ss addressHeader
  80. in ResourceInfo mimet mimep curr bb uu
  81. where
  82. bparse :: BA.Parser a -> String -> Maybe a
  83. bparse p v = eitherToMaybe . BA.parseOnly p $ SC.s v
  84. eitherToMaybe (Left _) = Nothing
  85. eitherToMaybe (Right v) = Just v
  86. -- | Retrieve MIME information from the resource headers
  87. getMimeData :: [Header] -> (Mime.ContentTypeHeader, Mime.TransferEncoding)
  88. getMimeData hh = (ct, te)
  89. where
  90. ct = case getHeader hh Mime.contentTypeHeaderName of
  91. Nothing -> def
  92. Just h -> case BA.parseOnly PMime.parseContentType (SC.s . value $ h) of
  93. Left _ -> def
  94. Right (ctv, ctp) -> Mime.ContentTypeHeader ctv ctp
  95. te = case getHeader hh Mime.transferEncodingHeaderName of
  96. Nothing -> def
  97. Just h -> case BA.parseOnly PMime.parseTransferEncoding (SC.s . value $ h) of
  98. Left _ -> def
  99. Right t -> t
  100. -- | Recreates the original resource text for the headers
  101. originalData :: PlainHeaders -> BS.ByteString
  102. originalData (PlainHeaders pp ss (psep, ssep)) =
  103. BS.concat [
  104. catData pp, psep,
  105. catData ss, ssep
  106. ]
  107. where
  108. catData :: [Header] -> BS.ByteString
  109. catData hh = BS.concat . map bare $ hh
  110. -- | Recreates the original resource text for the sealed headers only
  111. originalSealed :: PlainHeaders -> BS.ByteString
  112. originalSealed (PlainHeaders _ ss (_, ssep)) =
  113. (BS.concat $ map bare ss) `BS.append` ssep