-- |Types that represent a "slice" of a repository, as defined by a -- list of DebSource. This is called a slice because some sections -- may be omitted, and because different repositories may be combined -- in the list. module Debian.Repo.Slice ( sourceSlices , binarySlices , inexactPathSlices , releaseSlices , appendSliceLists , verifySourceLine , verifySourcesList , repoSources , parseNamedSliceList , parseNamedSliceList' ) where import Control.Exception (throw) import Control.Monad.Trans import qualified Data.ByteString.Lazy.Char8 as L import Data.List import Data.Maybe import Debian.Control --import Debian.Extra.CIO (vMessage) import Debian.Repo.IO import Debian.Repo.LocalRepository import Debian.Repo.Repository --import Debian.Shell import Debian.Repo.SourcesList import Debian.Repo.Types import Debian.URI --import Extra.Net (webServerDirectoryContents) import Extra.CIO (CIO, vPutStrBl) --import System.Unix.Process --import System.Directory import Text.Regex sourceSlices :: SliceList -> SliceList sourceSlices = SliceList . filter ((== DebSrc) . sourceType . sliceSource) . slices binarySlices :: SliceList -> SliceList binarySlices = SliceList . filter ((== Deb) . sourceType . sliceSource) . slices inexactPathSlices :: SliceList -> SliceList inexactPathSlices = SliceList . filter (either (const False) (const True) . sourceDist . sliceSource) . slices releaseSlices :: ReleaseName -> SliceList -> SliceList releaseSlices release list = SliceList . filter (isRelease . sourceDist . sliceSource) $ (slices list) where isRelease = either (const False) (\ (x, _) -> x == release) appendSliceLists :: [SliceList] -> SliceList appendSliceLists lists = SliceList { slices = concat (map slices lists) } -- |Examine the repository whose root is at the given URI and return a -- set of sources that includes all of its releases. This is used to -- ensure that a package we want to upload doesn't already exist in -- the repository. repoSources :: CIO m => Maybe EnvRoot -> URI -> AptIOT m SliceList repoSources chroot uri = do lift (vPutStrBl 3 $ "repoSources " ++ uriToString' uri) dirs <- lift (uriSubdirs chroot (uri {uriPath = uriPath uri ++ "/dists/"})) lift (vPutStrBl 3 $ " dirs: " ++ show dirs) releaseFiles <- mapM (lift . readRelease uri) dirs >>= return . catMaybes let codenames = map (maybe Nothing (zap (flip elem dirs))) . map (fieldValue "Codename") $ releaseFiles lift (vPutStrBl 3 $ " codenames: " ++ show (catMaybes codenames)) let sections = map (maybe Nothing (Just . map parseSection' . splitRegex (mkRegex "[ \t,]+")) . fieldValue "Components") $ releaseFiles lift (vPutStrBl 3 $ " sections: " ++ show (catMaybes codenames)) let result = concat $ map sources . nubBy (\ (a, _) (b, _) -> a == b) . zip codenames $ sections lift (vPutStrBl 2 $ "repoSources " ++ uriToString' uri ++ " ->\n [" ++ unwords (map show result) ++ "]") mapM (verifyDebSource Nothing) result >>= (\ list -> return $ SliceList { slices = list }) where sources (Just codename, Just components@(_ : _)) = [DebSource {sourceType = Deb, sourceUri = uri, sourceDist = Right (parseReleaseName codename, components)}, DebSource {sourceType = DebSrc, sourceUri = uri, sourceDist = Right (parseReleaseName codename, components)}] sources _ = [] -- Compute the list of sections for each dist on a remote server. zap p x = if p x then Just x else Nothing -- |Return the list of releases in a repository, which is the -- list of directories in the dists subdirectory. Currently -- this is only known to work with Apache. Note that some of -- the returned directories may be symlinks. uriSubdirs :: CIO m => (Maybe EnvRoot) -> URI -> m [String] uriSubdirs root uri = liftIO (dirFromURI uri') >>= either throw return where uri' = case uriScheme uri of "file:" -> uri {uriPath = maybe "" rootPath root ++ (uriPath uri)} _ -> uri readRelease :: CIO m => URI -> String -> m (Maybe Paragraph) readRelease uri name = do output <- liftIO (fileFromURI uri') case output of Left e -> throw e Right s -> case parseControl (show uri') (L.unpack s) of Right (Control [paragraph]) -> return (Just paragraph) _ -> return Nothing where uri' = uri {uriPath = uriPath uri ++ "/dists/" ++ name ++ "/Release"} parseNamedSliceList :: CIO m => String -> AptIOT m (Maybe NamedSliceList) parseNamedSliceList text = case matchRegex re text of Just [name, sources] -> (verifySourcesList Nothing . parseSourcesList) sources >>= \ sources -> return . Just $ NamedSliceList { sliceListName = SliceName name , sliceList = sources } _ -> return Nothing where re = mkRegexWithOpts "^[ \t\n]*([^ \t\n]+)[ \t\n]+(.*)$" False True -- |Create ReleaseCache info from an entry in the config file, which -- includes a dist name and the lines of the sources.list file. -- This also creates the basic parseNamedSliceList' :: CIO m => String -> AptIOT m NamedSliceList parseNamedSliceList' text = -- FIXME: This regexp is too permissive - it will match almost anything case matchRegex re text of Just [name, sources] -> do sources <- (verifySourcesList Nothing . parseSourcesList) sources return $ NamedSliceList { sliceListName = SliceName name, sliceList = sources } _ -> error "Syntax error in sources text" where re = mkRegexWithOpts "^[ \t\n]*([^ \t\n]+)[ \t\n]+(.*)$" False True verifySourcesList :: CIO m => Maybe EnvRoot -> [DebSource] -> AptIOT m SliceList verifySourcesList chroot list = mapM (verifyDebSource chroot) list >>= (\ list -> return $ SliceList { slices = list }) verifySourceLine :: CIO m => Maybe EnvRoot -> String -> AptIOT m Slice verifySourceLine chroot str = verifyDebSource chroot (parseSourceLine str) verifyDebSource :: CIO m => Maybe EnvRoot -> DebSource -> AptIOT m Slice verifyDebSource chroot line = do repo <- case uriScheme uri of "file:" -> let path = EnvPath (maybe (EnvRoot "") id chroot) (uriPath uri) in prepareLocalRepository path Nothing >>= return . LocalRepo _ -> prepareRepository uri return $ Slice { sliceRepo = repo, sliceSource = line } where uri = sourceUri line