1 module NmcTransform ( seedNmcDom
5 import Prelude hiding (lookup)
6 import Data.ByteString.Lazy (ByteString)
7 import Data.Text.Lazy (splitOn, pack, unpack)
8 import Data.Map.Lazy (empty, lookup, delete, size, singleton
9 , foldrWithKey, insert, insertWith)
10 import Control.Monad (foldM)
11 import Data.Aeson (decode)
15 -- | Perform query and return error string or parsed domain object
17 (String -> IO (Either String ByteString)) -- ^ query operation action
19 -> IO (Either String NmcDom) -- ^ error string or domain
20 queryNmcDom queryOp key = do
23 Left estr -> return $ Left estr
24 Right str -> case decode str :: Maybe NmcDom of
25 Nothing -> return $ Left $ "Unparseable value: " ++ (show str)
26 Just dom -> return $ Right dom
28 -- | Try to fetch "delegate" or "import" object and merge them into the
29 -- base domain. Original "import" element is removed, but newly
30 -- merged data may contain new "import" or "delegate", so the objects
31 -- that are about to be merged are processed recursively until there
32 -- are no more "import" and "deletage" attributes (or the depth gauge
35 (String -> IO (Either String ByteString)) -- ^ query operation action
36 -> Int -- ^ recursion counter
37 -> NmcDom -- ^ base domain
38 -> IO (Either String NmcDom) -- ^ result with merged import
39 mergeIncl queryOp depth base = do
41 mbase = (expandSrv . splitSubdoms . mergeSelf) base
42 base' = mbase {domDelegate = Nothing, domImport = Nothing}
44 if depth <= 0 then return $ Left "Nesting of imports is too deep"
45 else case ((domDelegate mbase), (domImport mbase)) of
46 (Nothing, Nothing ) -> return $ Right base'
47 (Nothing, Just keys) -> foldM mergeIncl1 (Right base') keys
48 (Just key, _ ) -> mergeIncl1 (Right emptyNmcDom) key
50 mergeIncl1 (Left err) _ = return $ Left err -- can never happen
51 mergeIncl1 (Right acc) key = do
52 sub <- queryNmcDom queryOp key
54 Left err -> return $ Left err
55 Right sub' -> mergeIncl queryOp (depth - 1) $ sub' `mergeNmcDom` acc
57 -- | If there is an element in the map with key "", merge the contents
58 -- and remove this element. Do this recursively.
59 mergeSelf :: NmcDom -> NmcDom
63 base' = base {domMap = removeSelf map}
64 removeSelf Nothing = Nothing
65 removeSelf (Just map) = if size map' == 0 then Nothing else Just map'
66 where map' = delete "" map
71 case lookup "" map' of
73 Just sub -> (mergeSelf sub) `mergeNmcDom` base'
74 -- recursion depth limited by the size of the record
76 -- | replace Service with Srv down in the Map
77 expandSrv :: NmcDom -> NmcDom
80 base' = base { domService = Nothing }
82 case domService base of
84 Just sl -> foldr addSrvMx base' sl
86 addSrvMx sr acc = sub1 `mergeNmcDom` acc
88 sub1 = emptyNmcDom { domMap = Just (singleton proto sub2)
90 sub2 = emptyNmcDom { domMap = Just (singleton srvid sub3) }
91 sub3 = emptyNmcDom { domSrv = Just [srvStr] }
92 proto = "_" ++ (srvProto sr)
93 srvid = "_" ++ (srvName sr)
94 srvStr = (show (srvPrio sr)) ++ "\t"
95 ++ (show (srvWeight sr)) ++ " "
96 ++ (show (srvPort sr)) ++ " "
99 if srvName sr == "smtp"
100 && srvProto sr == "tcp"
102 then Just [(show (srvPrio sr)) ++ "\t" ++ (srvHost sr)]
105 -- | Convert map elements of the form "subN...sub2.sub1.dom.bit"
106 -- into nested map and merge it
107 splitSubdoms :: NmcDom -> NmcDom
110 base' = base { domMap = Nothing }
114 Just sdmap -> (emptyNmcDom { domMap = Just sdmap' }) `mergeNmcDom` base'
116 sdmap' = foldrWithKey stow empty sdmap
117 stow fqdn sdom acc = insertWith mergeNmcDom fqdn' sdom' acc
120 nest (map unpack (splitOn (pack ".") (pack fqdn)), sdom)
121 nest ([], v) = (fqdn, v) -- can split result be empty?
122 nest ([k], v) = (k, v)
124 nest (ks, emptyNmcDom { domMap = Just (singleton k v) })
126 -- | Presence of some elements require removal of some others
127 normalizeDom :: NmcDom -> NmcDom
128 normalizeDom dom = foldr id dom [ translateNormalizer
132 nsNormalizer dom = case domNs dom of
134 Just ns -> emptyNmcDom { domNs = domNs dom, domEmail = domEmail dom }
135 translateNormalizer dom = case domTranslate dom of
137 Just tr -> dom { domMap = Nothing }
139 -- | Merge imports and Selfs and follow the maps tree to get dom
141 (String -> IO (Either String ByteString)) -- ^ query operation action
142 -> [String] -- ^ subdomain chain
143 -> NmcDom -- ^ base domain
144 -> IO (Either String NmcDom) -- ^ fully processed result
145 descendNmcDom queryOp subdom base = do
146 base' <- mergeIncl queryOp 10 base
148 [] -> return $ fmap normalizeDom base'
151 Left err -> return base'
153 case domMap base'' of
154 Nothing -> return $ Right emptyNmcDom
157 Nothing -> return $ Right emptyNmcDom
158 Just sub -> descendNmcDom queryOp ds sub
160 -- | Initial NmcDom populated with "import" only, suitable for "descend"
162 String -- ^ domain key (without namespace prefix)
163 -> NmcDom -- ^ resulting seed domain
164 seedNmcDom dn = emptyNmcDom { domImport = Just (["d/" ++ dn])}