Resource.hs 5.2 KB

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