ldif 0.0.6 → 0.0.7
raw patch · 13 files changed
+213/−88 lines, 13 filesdep ~HUnitnew-component:exe:ldifparsePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: HUnit
API changes (from Hackage documentation)
- Text.LDIF.Types: LDIFChanges :: Maybe String -> [LDIFRecord] -> LDIF
- Text.LDIF.Types: LDIFContent :: Maybe String -> [LDIFRecord] -> LDIF
- Text.LDIF.Types: lcChanges :: LDIF -> [LDIFRecord]
+ Text.LDIF.Types: LDIF :: Maybe String -> [LDIFRecord] -> LDIF
+ Text.LDIF.Types: LDIFMixedType :: LDIFType
+ Text.LDIF.Utils: filterAttr :: String -> [AttrValue] -> [AttrValue]
+ Text.LDIF.Utils: getLDIFType :: LDIF -> LDIFType
+ Text.LDIF.Utils: isChangeRecord :: LDIFRecord -> Bool
+ Text.LDIF.Utils: isContentRecord :: LDIFRecord -> Bool
+ Text.LDIF.Utils: ldif2tree :: LDIF -> Tree LDIFRecord
+ Text.LDIF.Utils: lookupAttr :: String -> [AttrValue] -> Maybe Value
- Text.LDIF.Diff: diffLDIF :: LDIF -> LDIF -> Maybe LDIF
+ Text.LDIF.Diff: diffLDIF :: LDIF -> LDIF -> Either String LDIF
Files
- TODO.org +2/−2
- ldif.cabal +6/−2
- src/Text/LDIF/Apply.hs +21/−23
- src/Text/LDIF/Diff.hs +9/−6
- src/Text/LDIF/Parser.hs +45/−25
- src/Text/LDIF/Printer.hs +1/−2
- src/Text/LDIF/Types.hs +9/−3
- src/Text/LDIF/Utils.hs +56/−10
- src/diffLDIF.hs +25/−4
- src/ldif2html.hs +2/−4
- src/ldifmodify.hs +6/−1
- src/ldifparse.hs +18/−0
- tests/TestMain.hs +13/−6
TODO.org view
@@ -7,8 +7,8 @@ ** update serializers back to LDIF for base64 & all DN notations ** add module tests for versioned LDIFs ** add module tests for base64 parsing-** Use getopt for input parameters on diffLDIF cmd-** TODO Implement ldifmodify (like ldapmodify)+** DONE Use getopt for input parameters on diffLDIF cmd+** DONE Implement ldifmodify (like ldapmodify) offline applying of Change LDIF on Content LDIF ldifmodify -f <contentLDIF> -o <outputLDIF> [<modifyLDIF> ... ] * Archive
ldif.cabal view
@@ -1,5 +1,5 @@ Name: ldif-Version: 0.0.6+Version: 0.0.7 License: BSD3 License-File: LICENSE Synopsis: The LDAP Data Interchange Format (LDIF) parser @@ -82,9 +82,13 @@ Hs-Source-Dirs: src Main-Is: ldifmodify.hs +Executable ldifparse+ Hs-Source-Dirs: src+ Main-Is: ldifparse.hs+ Executable test Hs-Source-Dirs: src, tests Main-Is: TestMain.hs- Build-Depends: base, HUnit+ Build-Depends: base, HUnit == 1.2.* if !flag(test) Buildable: False
src/Text/LDIF/Apply.hs view
@@ -3,52 +3,50 @@ ) where import Text.LDIF.Types-import Text.LDIF.Printer import Text.LDIF.Utils-import Data.Maybe-import Data.Either+import Text.LDIF.Printer import Data.List (nub) -- | Apply one LDIF to another LDIF. The destination LDIF has -- | to be Content LDIF applyLDIF :: LDIF -> LDIF -> LDIF-applyLDIF dst@(LDIFContent _ _) (LDIFChanges _ xs) = foldr (applyRecord2LDIF) dst xs-applyLDIF dst@(LDIFContent _ _) (LDIFContent _ xs) = foldr (applyRecord2LDIF) dst xs-applyLDIF _ _ = error "Destination LDIF has to be Content LDIF and not Change LDIF"+applyLDIF dst (LDIF _ xs) = foldr (applyRecord2LDIF) dst xs -- | Apply one LDIF Content/Change Record into LDIF and produce Changed LDIF applyRecord2LDIF :: LDIFRecord -> LDIF -> LDIF-applyRecord2LDIF rec@(ContentRecord dn vals) dst = applyRecord2LDIF (ChangeRecord dn (ChangeAdd vals)) dst-applyRecord2LDIF rec@(ChangeRecord dn op) dst = applyChange2Record op dn dst (findRecordByDN dst dn)+applyRecord2LDIF (ContentRecord dn vals) dst = applyRecord2LDIF (ChangeRecord dn (ChangeAdd vals)) dst+applyRecord2LDIF (ChangeRecord dn op) dst = applyChange2Record op dn dst (findRecordByDN dst dn) -- | Apply one LDIF Change (add/del/modf) for given DN within LDIF Content applyChange2Record :: Change -> DN -> LDIF -> Maybe LDIFRecord -> LDIF-applyChange2Record (ChangeAdd vals) dn (LDIFContent v xs) Nothing = LDIFContent v (xs++[ContentRecord dn vals]) -applyChange2Record (ChangeAdd vals) dn (LDIFContent v xs) (Just _) = error ("ADD: Already exists: "++(show dn))-applyChange2Record ChangeDelete dn (LDIFContent v xs) (Just _) = let rn = filter (\x -> dn /= (reDN x)) xs- in LDIFContent v rn-applyChange2Record ChangeDelete dn (LDIFContent v xs) Nothing = error ("DELETE: Entry not found: "++(show dn))-applyChange2Record (ChangeModify ops) dn (LDIFContent v xs) (Just r) = let pre = takeWhile (\x -> dn /= (reDN x)) xs- post = filter (\x -> dn /= (reDN x)) $ dropWhile (\x -> dn /= (reDN x)) xs- rn = foldr applyMod2Record r ops- in LDIFContent v (pre++[rn]++post)-applyChange2Record ChangeModDN _ _ _ = error "Operation ModDN is not supported"+applyChange2Record (ChangeAdd vals) dn ld Nothing = LDIF (lcVersion ld) ((lcEntries ld)++[ContentRecord dn vals]) +applyChange2Record (ChangeAdd _) dn _ (Just _) = error ("ADD: Already exists: "++(dn2str dn))+applyChange2Record ChangeDelete dn ld (Just _) = LDIF (lcVersion ld) (filter (\x -> dn /= (reDN x)) (lcEntries ld))+applyChange2Record ChangeDelete dn _ Nothing = error ("DELETE: Entry not found: "++(dn2str dn))+applyChange2Record (ChangeModify _) dn _ Nothing = error ("MODIFY: Entry not found: "++(dn2str dn))+applyChange2Record (ChangeModify ops) dn ld (Just r) = let pre = takeWhile (\x -> dn /= (reDN x)) (lcEntries ld)+ post = filter (\x -> dn /= (reDN x)) $ dropWhile (\x -> dn /= (reDN x)) (lcEntries ld)+ rn = foldr applyMod2Record r ops+ in LDIF (lcVersion ld) (pre++[rn]++post)+applyChange2Record ChangeModDN _ _ _ = error "ModDN: Operation is not supported yet."+--applyChange2Record x _ y _ = error $ "Unexpected LDIF Content: " ++ (show x) ++ (show y) -- | Apply Attribute Modification (Add/Del/Replace) to ContentRecord and produce changed ContentRecord applyMod2Record :: Modify -> LDIFRecord -> LDIFRecord applyMod2Record (ModAdd name vals) (ContentRecord dn av) = let nav = map (\v -> (name,v)) vals -- new attr/values mav = av ++ nav -- merged attr/values verified = if (length $ nub mav) == (length mav) then mav- else error ("ModAdd: Values already exists: "++(show av)++" vs "++(show vals)++" DN:"++(show dn))+ else error ("ModAdd: Values already exists: "++(show av)++" vs "++(show vals)++" DN:"++(dn2str dn)) in ContentRecord dn verified-applyMod2Record (ModDelete name []) (ContentRecord dn av) = let nav = filter (\(n,v) -> n /= name) av -- new attr/values+applyMod2Record (ModDelete name []) (ContentRecord dn av) = let nav = filter (\(n,_) -> n /= name) av -- new attr/values verified = if (length $ nub nav) /= (length av) then nav- else error ("ModDel: Attribute not found: "++(show name)++" DN:"++(show dn))+ else error ("ModDel: Attribute not found: "++(show name)++" DN:"++(dn2str dn)) in ContentRecord dn verified applyMod2Record (ModDelete name vals) (ContentRecord dn av) = let mav = filter (\(n,v) -> n /= name || v `notElem` vals) av verified = if (length av) - (length mav) == (length vals) then mav- else error ("ModDel: Attribute/Value not found: "++(show name)++"vals"++(show vals)++" DN:"++(show dn))+ else error ("ModDel: Attribute/Value not found: "++(show name)++"vals"++(show vals)++" DN:"++(dn2str dn)) in ContentRecord dn verified applyMod2Record (ModReplace name vals) (ContentRecord dn av) = let nav = map (\v -> (name,v)) vals -- new attr/values- mav = (filter (\(n,v) -> n /= name) av) ++ nav -- merged+ mav = (filter (\(n,_) -> n /= name) av) ++ nav -- merged in ContentRecord dn mav+applyMod2Record _ x = error $ "Unexpected LDIF Record:" ++ (show x)
src/Text/LDIF/Diff.hs view
@@ -5,10 +5,7 @@ where import Text.LDIF.Types import Text.LDIF.Utils-import Text.LDIF.Printer import Data.Maybe-import Data.Either-import Data.List (nub) -- | Create Change LDIF between to LDIF contents. If any -- | of input argument is not LDIFContent it returns Nothing. @@ -18,8 +15,14 @@ -- | Unsing following strategy: -- | 1. Iterate over L1 DN's and Modify / Remove Content -- | 2. Iterate over L2 and Add Content not in L1-diffLDIF :: LDIF -> LDIF -> Maybe LDIF-diffLDIF l1@(LDIFContent _ c1) l2@(LDIFContent v2 c2) = Just (LDIFChanges v2 (changes ++ adds))+diffLDIF :: LDIF -> LDIF -> Either String LDIF+diffLDIF l1 l2 | getLDIFType l1 == LDIFContentType && getLDIFType l2 == LDIFContentType = Right (diffLDIF' l1 l2)+ | otherwise = Left ("Diff supported only on Content LDIFs but SRC="+ ++(show $ getLDIFType l1)++" and DST="+ ++(show $ getLDIFType l2))++diffLDIF' :: LDIF -> LDIF -> LDIF+diffLDIF' l1@(LDIF _ c1) l2@(LDIF v2 c2) = LDIF v2 (changes ++ adds) where adds = map (content2add) $ filter (not . isEntryIn l1) c2 changes = filter (not . isDummyRecord) $ foldl (processEntry) [] c1@@ -33,7 +36,7 @@ Nothing -> False Just _ -> True content2add (ContentRecord dn vals) = ChangeRecord dn (ChangeAdd vals)-diffLDIF _ _ = Nothing+ content2add (ChangeRecord _ _) = error "Unexpected record type" -- | Diff two AttrVal Records if any of provided. -- | Implementation uses inefficient algorithm for large count of attributes within ContentRecord.
src/Text/LDIF/Parser.hs view
@@ -7,9 +7,8 @@ where import Text.LDIF.Types import Text.ParserCombinators.Parsec-import Data.Either import Data.Char-import Data.List (isPrefixOf, foldl')+import Data.List (isPrefixOf) -- | Parse string as LDIF content and return LDIF or ParseError parseLDIFStr :: String -> Either ParseError LDIF@@ -26,6 +25,7 @@ -- | and mismatch generates ParseError parseLDIFStrAs' :: String -> Maybe LDIFType -> String -> Either ParseError LDIF parseLDIFStrAs' nm Nothing = parse pLdif nm . preproc+parseLDIFStrAs' nm (Just LDIFMixedType) = parse pLdif nm . preproc parseLDIFStrAs' nm (Just LDIFContentType) = parse pLdifContent nm . preproc parseLDIFStrAs' nm (Just LDIFChangesType) = parse pLdifChanges nm . preproc @@ -56,12 +56,12 @@ takeLine :: [String] -> (String, [String]) takeLine [] = ([],[]) takeLine (x:[]) = (x,[])-takeLine (x:xs) = let isCont x = " " `isPrefixOf` x +takeLine (x:xs) = let isCont z = " " `isPrefixOf` z in (x ++ (concat $ map (tail) $ takeWhile (isCont) xs), dropWhile (isCont) xs) -- | Parsec ldif parser pLdif :: CharParser st LDIF-pLdif = try pLdifChanges <|> pLdifContent+pLdif = try pLdifChanges <|> try pLdifMixed pLdifChanges :: CharParser st LDIF pLdifChanges = do@@ -69,15 +69,26 @@ ver <- optionMaybe pVersionSpec pSEPs recs <- sepEndBy1 pChangeRec pSEPs- return $ LDIFChanges ver recs+ eof+ return $ LDIF ver recs +pLdifMixed:: CharParser st LDIF+pLdifMixed = do+ pSEPs+ ver <- optionMaybe pVersionSpec+ pSEPs+ recs <- sepEndBy1 pRec pSEPs+ eof+ return $ LDIF ver recs+ pLdifContent :: CharParser st LDIF pLdifContent = do pSEPs ver <- optionMaybe pVersionSpec pSEPs recs <- sepEndBy1 pAttrValRec pSEPs- return $ LDIFContent ver recs+ eof+ return $ LDIF ver recs pAttrValRec :: CharParser st LDIFRecord pAttrValRec = do@@ -86,6 +97,9 @@ attrVals <- sepEndBy1 pAttrValSpec pSEP return $ ContentRecord dn attrVals +pRec :: CharParser st LDIFRecord+pRec = try pChangeRec <|> pAttrValRec+ pChangeRec :: CharParser st LDIFRecord pChangeRec = try pChangeAdd <|> try pChangeDel@@ -96,9 +110,9 @@ pChangeAdd = do dn <- pDNSpec pSEP- string "changetype:"+ _ <- string "changetype:" pFILL- string "add"+ _ <- string "add" pSEP vals <- sepEndBy1 pAttrValSpec pSEP return $ ChangeRecord dn (ChangeAdd vals)@@ -107,9 +121,9 @@ pChangeDel = do dn <- pDNSpec pSEP- string "changetype:"+ _ <- string "changetype:" pFILL- string "delete"+ _ <- string "delete" pSEP return $ ChangeRecord dn ChangeDelete @@ -117,9 +131,9 @@ pChangeMod = do dn <- pDNSpec pSEP- string "changetype:"+ _ <- string "changetype:" pFILL- string "modify"+ _ <- string "modify" pSEP mods <- sepEndBy1 pModSpec (char '-' >> pSEP) return $ ChangeRecord dn (ChangeModify mods)@@ -128,17 +142,17 @@ pChangeModDN = do dn <- pDNSpec pSEP- string "changetype:"+ _ <- string "changetype:" pFILL- string "modrdn" + _ <- string "modrdn" pSEP- string "newrdn:"+ _ <- string "newrdn:" pFILL - pRDN+ _ <- pRDN pSEP- string "deleteoldrdn:"+ _ <- string "deleteoldrdn:" pFILL- oneOf "01"+ _ <- oneOf "01" pSEP return $ ChangeRecord dn ChangeModDN @@ -147,7 +161,7 @@ pDNSpec :: CharParser st DN pDNSpec = do- string "dn:"+ _ <- string "dn:" pDN pDN :: CharParser st DN@@ -160,7 +174,7 @@ pAttrEqValue = do pFILL att <- pAttributeType- char '='+ _ <- char '=' val <- pAttrValueDN return (att,val) @@ -168,12 +182,13 @@ pAttrValueDN = do many (noneOf stringChars) where - specialChars = [',','=','+','<','>','#',';','\n','\r']+ specialChars = [',','=','+','#',';','\n','\r']+ -- specialChars = [',','=','+','<','>','#',';','\n','\r'] stringChars = '\\':'"':specialChars pVersionSpec :: CharParser st String pVersionSpec = do- string "version:"+ _ <- string "version:" pFILL many1 digit @@ -213,7 +228,7 @@ pValueSpec :: CharParser st Value pValueSpec = try (char ':' >> char ':' >> pFILL >> pBase64String)- <|> try (char ':' >> pFILL >> pSafeString) + <|> try (char ':' >> pFILL >> pSafeString') <|> (char ':' >> char '<' >> pFILL >> pURL) pURL :: CharParser st String@@ -224,6 +239,11 @@ c <- noneOf "\n\r :<" r <- many (noneOf "\n\r") return $ c:r++pSafeString' :: CharParser st String+pSafeString' = do+ r <- many (noneOf "\n\r")+ return r pBase64String :: CharParser st String pBase64String = pSafeString@@ -234,11 +254,11 @@ pLdapOid :: CharParser st Attribute pLdapOid = do num <- many1 digit- rest <- many (do { string "."; n <- many1 digit; return $ '.':n})+ rest <- many (do { _ <- string "."; n <- many1 digit; return $ '.':n}) return (Attribute $ num ++ concat rest) pFILL :: CharParser st ()-pFILL = spaces+pFILL = skipMany (oneOf [' ', '\t']) pSEP :: CharParser st () pSEP = try (char '\r' >> char '\n' >> return () )
src/Text/LDIF/Printer.hs view
@@ -10,8 +10,7 @@ -- | Serialize LDIF in LDIF Format ldif2str :: LDIF -> String-ldif2str (LDIFContent v xs) = unlines $ (ver2str v) ++ (map (record2str) xs)-ldif2str (LDIFChanges v xs) = unlines $ (ver2str v) ++ (map (record2str) xs)+ldif2str (LDIF v xs) = unlines $ (ver2str v) ++ (map (record2str) xs) -- | Serialize version to LDIF Format Lines ver2str :: Maybe String -> [String]
src/Text/LDIF/Types.hs view
@@ -23,12 +23,18 @@ type AttrValue = (Attribute, Value) -- | Type of LDIF Files (Content, Changes)-data LDIFType = LDIFContentType | LDIFChangesType deriving (Show, Eq) -- Maybe LDIFMixedType +data LDIFType = LDIFContentType + | LDIFChangesType + | LDIFMixedType deriving Eq +instance Show LDIFType where+ show LDIFChangesType = "Delta"+ show LDIFContentType = "Content"+ show LDIFMixedType = "Mixed"+ -- | Represents LDIF structure, it can be either simply LDIF data dump or -- | changes LDIF with LDAP operations -data LDIF = LDIFContent { lcVersion :: Maybe String, lcEntries :: [LDIFRecord] }- | LDIFChanges { lcVersion :: Maybe String, lcChanges :: [LDIFRecord] } deriving (Show, Eq)+data LDIF = LDIF { lcVersion :: Maybe String, lcEntries :: [LDIFRecord] } deriving (Show, Eq) -- | Represents one data record within LDIF file with DN and content -- | Represents one change record within LDIF file with DN and content
src/Text/LDIF/Utils.hs view
@@ -7,19 +7,21 @@ takeDNPrefix, leafOfDN, rootOfDN,- isDummyRecord+ lookupAttr,+ filterAttr,+ isDummyRecord,+ ldif2tree,+ getLDIFType,+ isContentRecord,+ isChangeRecord ) where import Text.LDIF.Types-import Text.LDIF.Printer-import Data.Maybe-import Data.Either-import Data.List (nub)+import Data.Tree -- | Find all Contents with given DN findRecordsByDN :: LDIF -> DN -> [LDIFRecord]-findRecordsByDN (LDIFContent _ entries) dn = filter (\x -> (reDN x) == dn) entries-findRecordsByDN (LDIFChanges _ entries) dn = filter (\x -> (reDN x) == dn) entries+findRecordsByDN (LDIF _ entries) dn = filter (\x -> (reDN x) == dn) entries -- | Find first Content with given DN findRecordByDN :: LDIF -> DN -> Maybe LDIFRecord@@ -28,6 +30,14 @@ xs -> Just (head xs) +-- | Find fist Attribute within attributes pairs list+lookupAttr :: String -> [AttrValue] -> Maybe Value+lookupAttr attr xs = lookup (Attribute attr) xs++-- | Filter Attribute Value list according Attribute name+filterAttr :: String -> [AttrValue] -> [AttrValue]+filterAttr attr xs = filter (\x -> (Attribute attr) == fst x) xs+ -- | Change record without any impact isDummyRecord :: LDIFRecord -> Bool isDummyRecord (ChangeRecord _ (ChangeModify [])) = True @@ -46,11 +56,47 @@ getDNValue (DN vals) idx = vals !! idx takeDNPrefix :: DN -> Int -> DN-takeDNPrefix (DN vals) n = (DN (take n vals))+takeDNPrefix (DN vals) n = (DN (reverse $ take n (reverse vals))) -- | Check if the dn1 is prefix of dn2 isDNPrefixOf :: DN -> DN -> Bool isDNPrefixOf dn1 dn2 | (sizeOfDN dn1) >= (sizeOfDN dn2) = False- | otherwise = let n = (sizeOfDN dn2)- in (takeDNPrefix dn1 n) == dn2+ | otherwise = let n = (sizeOfDN dn1)+ in (takeDNPrefix dn2 n) == dn1 +dummyRootDN :: DN+dummyRootDN = DN [(Attribute "dc", "root")]++ldif2tree :: LDIF -> Tree LDIFRecord+ldif2tree (LDIF _ entries) = Node (ChangeRecord dummyRootDN (ChangeAdd [])) (ldifRecs2tree entries)++isParentRecordOf :: LDIFRecord -> LDIFRecord -> Bool+isParentRecordOf a b = isDNPrefixOf (reDN a) (reDN b)++ldifRoots :: [LDIFRecord] -> [LDIFRecord]+ldifRoots xs = let isRoot x = all (\y -> not $ isParentRecordOf y x) xs+ in filter (isRoot) xs++ldifRecs2tree :: [LDIFRecord] -> [Tree LDIFRecord]+ldifRecs2tree xs = let roots = (ldifRoots xs)+ subtr x = ldifRecs2tree $ filter (isParentRecordOf x) xs+ in map (\x -> Node x (subtr x)) roots++isContentRecord :: LDIFRecord -> Bool+isContentRecord (ContentRecord _ _) = True+isContentRecord _ = False++isChangeRecord :: LDIFRecord -> Bool+isChangeRecord (ChangeRecord _ _) = True+isChangeRecord _ = False++getLDIFType :: LDIF -> LDIFType+getLDIFType (LDIF _ []) = LDIFContentType+getLDIFType (LDIF _ xs) = getLDIFType' con chg+ where+ con = filter (isContentRecord) xs+ chg = filter (not . isContentRecord) xs+ getLDIFType' [] [] = error "Unexpected"+ getLDIFType' [] _ = LDIFChangesType+ getLDIFType' _ [] = LDIFContentType+ getLDIFType' _ _ = LDIFMixedType
src/diffLDIF.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveDataTypeable #-} -- | Make Change LDIF based on two Content LDIFs import Data.List import Data.Either@@ -6,11 +7,31 @@ import System.FilePath import System.Environment import Text.LDIF+import System.Console.CmdArgs +progDesc = "Create delta LDIF between Source LDIF and Target LDIF"++data DiffLdif = DiffLdif { srcFile :: FilePath+ , dstFile :: FilePath } deriving (Show, Data, Typeable)++defaultCfg = mode $ DiffLdif { srcFile = def &= typFile & flag "s" & text "Source LDIF File"+ , dstFile = def &= typFile & flag "t" & text "Target LDIF File" }++verifyCfg :: DiffLdif -> IO ()+verifyCfg (DiffLdif [] []) = do + msg <- cmdArgsHelp progDesc [defaultCfg] Text+ error msg+verifyCfg (DiffLdif [] _) = error "Missing Source LDIF File parameter (-s)"+verifyCfg (DiffLdif _ []) = error "Missing Target LDIF File parameter (-t)"+verifyCfg (DiffLdif _ _ ) = return ()+ main = do- args <- getArgs- ml1 <- parseLDIFFile (args !! 0)- ml2 <- parseLDIFFile (args !! 1)+ cfg <- cmdArgs progDesc [defaultCfg]+ verifyCfg cfg+ ml1 <- parseLDIFFile (srcFile cfg)+ ml2 <- parseLDIFFile (dstFile cfg) case rights [ml1,ml2] of - [l1,l2] -> putStrLn $ ldif2str $ fromJust $ diffLDIF l1 l2 + [l1,l2] -> case diffLDIF l1 l2 of+ Left err -> putStrLn err+ Right delta -> putStrLn $ ldif2str delta _ -> print $ lefts [ml1,ml2]
src/ldif2html.hs view
@@ -9,8 +9,7 @@ import qualified Data.Set as Set ldif2html :: Set.Set String -> LDIF -> String-ldif2html idx (LDIFContent v xs) = unlines $ (ver2str v) ++ (map (record2html idx) xs)-ldif2html idx (LDIFChanges v xs) = unlines $ (ver2str v) ++ (map (record2html idx) xs)+ldif2html idx (LDIF v xs) = unlines $ (ver2str v) ++ (map (record2html idx) xs) -- | Serialize version to LDIF Format Lines ver2str :: Maybe String -> [String]@@ -73,5 +72,4 @@ xs -> mapM_ print xs dns :: LDIF -> Set.Set String-dns (LDIFContent _ xs) = Set.fromList $ nub $ map (dn2last . reDN) xs-dns (LDIFChanges _ xs) = Set.fromList $ nub $ map (dn2last . reDN) xs+dns (LDIF _ xs) = Set.fromList $ nub $ map (dn2last . reDN) xs
src/ldifmodify.hs view
@@ -10,6 +10,8 @@ import Text.LDIF import System.Console.CmdArgs +progDesc = "Apply LDAP operations from LDIF to LDIF (like ldapmodify)"+ data LdifModify = LdifModify { baseFile :: FilePath , modFiles :: [FilePath] , outFile :: FilePath } deriving (Show, Data, Typeable)@@ -19,12 +21,15 @@ , outFile = def &= typFile & flag "o" & text "Output LDIF File" } verifyCfg :: LdifModify -> IO ()+verifyCfg (LdifModify [] [] []) = do+ msg <- cmdArgsHelp progDesc [defaultCfg] Text+ error msg verifyCfg (LdifModify bf mfx ouf) | length bf == 0 = error "Missing Base LDIF File parameter (-f)" | length mfx == 0 = error "Missing LDIF Files for applying as arguments" | otherwise = return () main = do- cfg <- cmdArgs "LDIF Modify. Apply LDAP operations from LDIF to LDIF" [defaultCfg]+ cfg <- cmdArgs progDesc [defaultCfg] verifyCfg cfg baseLDIF <- safeParseLDIFFile (baseFile cfg) modLDIFs <- mapM (safeParseLDIFFile) (modFiles cfg)
+ src/ldifparse.hs view
@@ -0,0 +1,18 @@+-- | Make Change LDIF based on two Content LDIFs+import Data.List+import Data.Either+import Data.Maybe+import Control.Monad +import System.FilePath +import System.Environment+import Text.LDIF++main = do+ args <- getArgs+ ml1 <- parseLDIFFile (args !! 0)+ case ml1 of + Right l1 -> do+ print l1+ putStrLn "========================================================"+ putStrLn $ ldif2str l1+ Left err -> print err
tests/TestMain.hs view
@@ -12,14 +12,21 @@ ls <- getLDIFs ldifDir runTestTT (tests ls) -tests ls = TestList $ (testCasesParseOK ls) ++ (testCasesDIFF)+tests ls = TestList $ (testCasesParseOK ls) ++ testCasesDIFF ++ testCasesUtils -- -- Test Cases -- testCasesDIFF = [TestCase (assertEqual "dummy" True True)] testCasesParseOK ls = map (\x -> TestCase (assertParsedOK x)) $ filter (isOK) ls-+testCasesUtils = [ TestCase (assertBool "DN1Root is Prefix of DN2Root" (not $ isDNPrefixOf dn1root dn2root))+ , TestCase (assertBool "DN1Root is Prefix of DN1Child" (isDNPrefixOf dn1root dn1child))+ , TestCase (assertBool "DN Size 1" (1 == sizeOfDN dn1root))+ , TestCase (assertBool "DN Size 2" (2 == sizeOfDN dn1child))]+ where+ dn1root = head $ rights [ parseDNStr "dc=sk" ]+ dn2root = head $ rights [ parseDNStr "dc=de" ]+ dn1child = head $ rights [ parseDNStr "o=green,dc=sk" ] -- -- Support Methods --@@ -38,9 +45,9 @@ | (isSuffixOf ".content.ldif" name) = assertTypeContent name ldif | otherwise = assertFailure $ "Unexpected filename: (not .modify.ldif or .content.ldif " ++ name -assertTypeContent n l@(LDIFContent _ _) = assertBool "Valid Content Type" True -- >> (putStrLn $ "\n\n" ++ n ++ "\n\n" ++ (show l))-assertTypeContent n x = assertFailure $ n ++ " is not type of LDIFContent"+assertTypeContent n l | (getLDIFType l == LDIFContentType) = assertBool "Valid Content Type" True -- >> (putStrLn $ "\n\n" ++ n ++ "\n\n" ++ (show l))+ | otherwise = assertFailure $ n ++ " is not type of LDIFContent" -assertTypeChanges n l@(LDIFChanges _ _) = assertBool "Valid Changes Type" True -- >> (putStrLn $ "\n\n" ++ n ++ "\n\n" ++ (show l))-assertTypeChanges n x = assertFailure $ n ++ " is not type of LDIFChanges"+assertTypeChanges n l | (getLDIFType l /= LDIFContentType) = assertBool "Valid Changes Type" True -- >> (putStrLn $ "\n\n" ++ n ++ "\n\n" ++ (show l))+ | otherwise = assertFailure $ n ++ " is not type of LDIFChanges"