ldif 0.0.3 → 0.0.4
raw patch · 6 files changed
+135/−21 lines, 6 filesdep +containersnew-component:exe:ldif2html
Dependencies added: containers
Files
- TODO.org +9/−0
- ldif.cabal +10/−3
- src/Text/LDIF/Parser.hs +32/−16
- src/Text/LDIF/Proc.hs +1/−1
- src/Text/LDIF/Types.hs +4/−1
- src/ldif2html.hs +79/−0
TODO.org view
@@ -11,3 +11,12 @@ * TODO add module tests for versioned LDIFs * TODO add module tests for base64 parsing * TODO Implement Attribute Name case-insensitive matching at DN+* TODO Use getopt for input parameters on diffLDIF cmd+* TODO Implement ldifmodify (like ldapmodify)+ offline applying of Change LDIF on Content LDIF+ ldifmodify -h <contentLDIF> -f <changeLDIF> [-o <contentLDIFAfter>] +* DONE Change interface for providing parser method with expected type of LDIF+it allows parse changeldif as contentldif regardless the content.++* DONE Add ldif2html which generate hypertext LDIF representation+* DONE Update unwrap method in preprocessor for feasible resource consuption
ldif.cabal view
@@ -1,5 +1,5 @@ Name: ldif-Version: 0.0.3+Version: 0.0.4 License: BSD3 License-File: LICENSE Synopsis: The LDAP Data Interchange Format (LDIF) parser @@ -15,7 +15,9 @@ It includes following tool: . - diffLDIF command generates change LDIF between two - content LDIF files. + content LDIF files.+ - ldif2html command generates hypertext HTML browsable+ LDIF file. . Category: Text Stability: experimental@@ -39,7 +41,7 @@ Source-Repository head type: darcs- location: http://rampa.sk/repo/ldif+ location: http://patch-tag.com/r/dixiecko/ldif flag test description: Build test program.@@ -50,6 +52,7 @@ base < 5, filepath, haskell98,+ containers, parsec >= 2.1.0, Cabal >= 1.5 && < 1.9 Hs-Source-Dirs: src@@ -66,6 +69,10 @@ Executable diffLDIF Hs-Source-Dirs: src Main-Is: diffLDIF.hs++Executable ldif2html+ Hs-Source-Dirs: src+ Main-Is: ldif2html.hs Executable test Hs-Source-Dirs: src, tests
src/Text/LDIF/Parser.hs view
@@ -1,5 +1,6 @@ module Text.LDIF.Parser ( parseLDIFStr,+ parseLDIFStrAs, parseLDIFFile, parseDNStr )@@ -8,25 +9,36 @@ import Text.ParserCombinators.Parsec import Data.Either import Data.Char-import Data.List (isPrefixOf)+import Data.List (isPrefixOf, foldl') -- | Parse string as LDIF content and return LDIF or ParseError parseLDIFStr :: String -> Either ParseError LDIF-parseLDIFStr = parse pLdif "(param)" . preproc +parseLDIFStr = parseLDIFStrAs Nothing -- | Read and parse provided file and return LDIF or ParseError parseLDIFFile :: String -> IO (Either ParseError LDIF) parseLDIFFile name = do input <- readFile name- return $ parse pLdif name (preproc input)+ return $ parseLDIFStrAs' name Nothing input +-- | Read and parse provided string and return LDIF or ParserError+-- | If LDIF type is specified than given type is expected for parsing +-- | and mismatch generates ParseError+parseLDIFStrAs' :: String -> Maybe LDIFType -> String -> Either ParseError LDIF+parseLDIFStrAs' nm Nothing = parse pLdif nm . preproc+parseLDIFStrAs' nm (Just LDIFContentType) = parse pLdifContent nm . preproc+parseLDIFStrAs' nm (Just LDIFChangesType) = parse pLdifChanges nm . preproc++parseLDIFStrAs :: Maybe LDIFType -> String -> Either ParseError LDIF+parseLDIFStrAs = parseLDIFStrAs' "(param)"+ -- | Parse string as DN and return DN type or ParseError parseDNStr :: String -> Either ParseError DN parseDNStr = parse pDN "(param)" -- | Preprocessing for concat wrapped lines and remove comment lines preproc :: String -> String-preproc = unwrap . stripComments+preproc = stripComments . unwrap -- | Remove Comment Lines stripComments :: String -> String@@ -34,32 +46,36 @@ -- | Unwrap lines, lines with space at begin is continue of previous line unwrap :: String -> String-unwrap input = unlines $ preprocLines $ lines input- where - preprocLines xs = unbox $ foldl (preprocLine) ([],Nothing) xs- preprocLine (ys,r) [] = (addLineMaybe ys r,Just []) - preprocLine (ys,r) (l:lx) | l == ' ' = (ys,concatLineMaybe r lx)- | otherwise = (addLineMaybe ys r, Just $ l:lx)- concatLineMaybe Nothing x = Just x- concatLineMaybe (Just y) x = Just (y++x)- addLineMaybe xs Nothing = xs- addLineMaybe xs (Just x) = xs++[x]- unbox (ys,Nothing) = ys- unbox (ys,Just x) = ys++[x]+unwrap xs = unlines $ takeLines $ lines xs +takeLines :: [String] -> [String]+takeLines [] = []+takeLines xs = let (ln,ys) = takeLine xs+ in ln:takeLines ys++takeLine :: [String] -> (String, [String])+takeLine [] = ([],[])+takeLine (x:[]) = (x,[])+takeLine (x:xs) = let isCont x = " " `isPrefixOf` x + in (x ++ (concat $ map (tail) $ takeWhile (isCont) xs), dropWhile (isCont) xs) + -- | Parsec ldif parser pLdif :: CharParser st LDIF pLdif = try pLdifChanges <|> pLdifContent pLdifChanges :: CharParser st LDIF pLdifChanges = do+ pSEPs ver <- optionMaybe pVersionSpec+ pSEPs recs <- sepEndBy1 pChangeRec pSEPs return $ LDIFChanges ver recs pLdifContent :: CharParser st LDIF pLdifContent = do+ pSEPs ver <- optionMaybe pVersionSpec+ pSEPs recs <- sepEndBy1 pAttrValRec pSEPs return $ LDIFContent ver recs
src/Text/LDIF/Proc.hs view
@@ -34,7 +34,7 @@ -- | 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 v1 c1) l2@(LDIFContent v2 c2) = Just (LDIFChanges v2 changes) +diffLDIF (LDIFContent _ c1) l2@(LDIFContent v2 _) = Just (LDIFChanges v2 changes) where changes = filter (not . isDummyChangeRecord) $ foldl (processEntry) [] c1 processEntry xs e1 = let me2 = findContentByDN l2 (coDN e1)
src/Text/LDIF/Types.hs view
@@ -6,14 +6,17 @@ Change(..), Modify(..), DN(..), + LDIFType(..), Attribute, Value, AttrValue ) where-import Data.Char type Attribute = String type Value = String type AttrValue = (Attribute, Value)++-- | Type of LDIF Files (Content, Changes)+data LDIFType = LDIFContentType | LDIFChangesType deriving (Show, Eq) -- Maybe LDIFMixedType -- | Represents LDIF structure, it can be either simply LDIF data dump or -- | changes LDIF with LDAP operations
+ src/ldif2html.hs view
@@ -0,0 +1,79 @@+-- | 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+import qualified Data.Set as Set++ldif2html :: Set.Set String -> LDIF -> String+ldif2html idx (LDIFContent v xs) = unlines $ (ver2Str v) ++ (map (content2html idx) xs)+ldif2html idx (LDIFChanges v xs) = unlines $ (ver2Str v) ++ (map (change2html idx) xs)++-- | Serialize version to LDIF Format Lines+ver2Str :: Maybe String -> [String]+ver2Str Nothing = []+ver2Str (Just v) = ["version: "++v]++-- | Serialize DN to LDIF Format+dn2html :: DN -> String+dn2html ys@(DN xs) = concat [ "<div id=\"abc"++nm++"\"></div><b>dn:</b> <font color=\"green\"><b>", intercalate "," $ map (\(n,v) -> n++"="++v) xs, "</b></font>"]+ where+ nm = dn2last ys++dn2last (DN xs) = snd $ head xs++-- | Serialize Content Record in LDIF Format+content2html :: Set.Set String -> ContentRecord -> String+content2html idx (ContentRecord dn xs) = unlines $ [ (dn2html dn) ] ++ (attrVals2Ln idx xs)++-- | Serialize Change Record in LDIF Format+change2html :: Set.Set String -> ChangeRecord -> String+change2html idx (ChangeRecord dn (ChangeDelete)) = unlines [ (dn2Str dn), "changetype: delete" ]+change2html idx (ChangeRecord dn (ChangeAdd xs)) = unlines $ [ (dn2html dn), "changetype: add" ] ++ (attrVals2Ln idx xs)+change2html idx (ChangeRecord dn (ChangeModify xs)) = unlines $ [ (dn2Str dn), "changetype: modify" ] ++ (mods2Ln idx xs)+change2html idx (ChangeRecord dn (ChangeModDN)) = unlines $ [ (dn2Str dn), "changetype: moddn" ]++attrVals2Ln :: Set.Set String -> [AttrValue] -> [String]+attrVals2Ln idx xs = map (attrVal2Ln idx) xs++attrVal2Ln :: Set.Set String -> AttrValue -> String+attrVal2Ln idx (n,v) = if Set.member v idx then "<b>"++n++"</b> : <a href=\"#abc"++v++"\">"++v++"</a>"+ else "<b>"++n++"</b> : "++v++mods2Ln :: Set.Set String -> [Modify] -> [String]+mods2Ln idx xs = intercalate ["-"] $ map (mod2Ln idx) xs++mod2Ln :: Set.Set String -> Modify -> [String]+mod2Ln idx (ModAdd nm xs) = [ attrVal2Ln idx ("add",nm) ] ++ (map (\v -> attrVal2Ln idx (nm,v)) xs) +mod2Ln idx (ModDelete nm xs) = [ attrVal2Ln idx ("delete",nm) ] ++ (map (\v -> attrVal2Ln idx (nm,v)) xs)+mod2Ln idx (ModReplace nm xs) = [ attrVal2Ln idx ("replace",nm) ] ++ (map (\v -> attrVal2Ln idx (nm,v)) xs)++-- Dummy ldif2html implementation but it should+-- display LDIF file as HTML with values as href to+-- DN which has the value as the leaf RDN value+main = do+ args <- getArgs+ case args of + [] -> putStrLn "Usage: ldif2html <input.ldif> [<input2.ldif> <input3.ldif> ... <inputN.ldif> <output.ldif>]"+ [inp] -> do+ doLDIF2HTML [inp] (putStrLn)+ putStrLn "# Done."+ xs -> do+ doLDIF2HTML (init xs) (writeFile (last xs)) + putStrLn $ "## Processed : " ++ (unlines $ init xs)+ putStrLn $ "## Written : " ++ (last xs)++doLDIF2HTML names outF = do+ mls <- mapM (parseLDIFFile) names+ case lefts mls of + [] -> do+ let idx = Set.unions $ map (dns) (rights mls)+ outF $ concat ["<html><body bgcolor=lightyellow><pre>", concat $ map (ldif2html idx) (rights mls), "</pre></body></html>" ]+ xs -> mapM_ print xs++dns :: LDIF -> Set.Set String+dns (LDIFContent _ xs) = Set.fromList $ nub $ map (dn2last . coDN) xs+dns (LDIFChanges _ xs) = Set.fromList $ nub $ map (dn2last . chDN) xs