    {-# LANGUAGE CPP, DeriveDataTypeable, FlexibleContexts, OverloadedStrings, ScopedTypeVariables #-}
    {-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-orphans #-}
    module Debian.Apt.Index
        ( update
        , Fetcher
        , CheckSums(..)
        , Compression(..)
        , FileTuple
        , Size
        , controlFromIndex
        , controlFromIndex'
        , findContentsFiles
        , findIndexes
        , indexesInRelease
        , tupleFromFilePath
        ) where

    import qualified Codec.Compression.GZip as GZip
    import qualified Codec.Compression.BZip as BZip
    import Control.Lens (over, to, view)
    import Control.Monad
    import qualified Data.ByteString.Char8 as B
    import qualified Data.ByteString.Lazy.Char8 as L
    import qualified Data.Digest.Pure.MD5 as MD5
    import qualified Data.Digest.Pure.SHA as SHA
    import Data.Either (partitionEithers)
    import Data.Function
    import Data.List as List (null, intercalate, sortBy, isSuffixOf, isPrefixOf)
    import qualified Data.Map as M
    import Data.Text as Text (Text, unpack, concat, lines, words)
    import Data.Time
    import Debian.Apt.Methods
    import Debian.Codename (Codename, codename)
    import Debian.Control (formatControl)
    import Debian.Control.ByteString
    --import Debian.Control.Common
    import Debian.Control.Text (decodeControl)
    import Debian.Release
    import Debian.Sources
    import Debian.URI (uriPathLens, uriToString')
    import Debian.VendorURI (VendorURI, vendorURI)
    import Network.URI
    import System.Directory
    import System.FilePath ((</>))
    import System.Posix.Files
    import System.FilePath (takeBaseName)
    --import qualified System.Unix.Misc as Misc
    import Text.ParserCombinators.Parsec.Error
    import Text.PrettyPrint (render)
    import Distribution.Pretty (pretty)
    import Text.Read (readMaybe)

    -- |Package indexes on the server are uncompressed or compressed with
    -- gzip or bzip2. We do not know what will exist on the server until we
    -- actually look. This type is used to mark the compression status of
    -- what was actually found.
    data Compression
        = BZ2 | GZ | Uncompressed
          deriving (ReadPrec [Compression]
    ReadPrec Compression
    Int -> ReadS Compression
    ReadS [Compression]
    (Int -> ReadS Compression)
    -> ReadS [Compression]
    -> ReadPrec Compression
    -> ReadPrec [Compression]
    -> Read Compression
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    $creadsPrec :: Int -> ReadS Compression
    readsPrec :: Int -> ReadS Compression
    $creadList :: ReadS [Compression]
    readList :: ReadS [Compression]
    $creadPrec :: ReadPrec Compression
    readPrec :: ReadPrec Compression
    $creadListPrec :: ReadPrec [Compression]
    readListPrec :: ReadPrec [Compression]
    Read, Int -> Compression -> ShowS
    [Compression] -> ShowS
    Compression -> [Char]
    (Int -> Compression -> ShowS)
    -> (Compression -> [Char])
    -> ([Compression] -> ShowS)
    -> Show Compression
    forall a.
    (Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> Compression -> ShowS
    showsPrec :: Int -> Compression -> ShowS
    $cshow :: Compression -> [Char]
    show :: Compression -> [Char]
    $cshowList :: [Compression] -> ShowS
    showList :: [Compression] -> ShowS
    Show, Compression -> Compression -> Bool
    (Compression -> Compression -> Bool)
    -> (Compression -> Compression -> Bool) -> Eq Compression
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: Compression -> Compression -> Bool
    == :: Compression -> Compression -> Bool
    $c/= :: Compression -> Compression -> Bool
    /= :: Compression -> Compression -> Bool
    Eq, Eq Compression
    Eq Compression =>
    (Compression -> Compression -> Ordering)
    -> (Compression -> Compression -> Bool)
    -> (Compression -> Compression -> Bool)
    -> (Compression -> Compression -> Bool)
    -> (Compression -> Compression -> Bool)
    -> (Compression -> Compression -> Compression)
    -> (Compression -> Compression -> Compression)
    -> Ord Compression
    Compression -> Compression -> Bool
    Compression -> Compression -> Ordering
    Compression -> Compression -> Compression
    forall a.
    Eq a =>
    (a -> a -> Ordering)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> a)
    -> (a -> a -> a)
    -> Ord a
    $ccompare :: Compression -> Compression -> Ordering
    compare :: Compression -> Compression -> Ordering
    $c< :: Compression -> Compression -> Bool
    < :: Compression -> Compression -> Bool
    $c<= :: Compression -> Compression -> Bool
    <= :: Compression -> Compression -> Bool
    $c> :: Compression -> Compression -> Bool
    > :: Compression -> Compression -> Bool
    $c>= :: Compression -> Compression -> Bool
    >= :: Compression -> Compression -> Bool
    $cmax :: Compression -> Compression -> Compression
    max :: Compression -> Compression -> Compression
    $cmin :: Compression -> Compression -> Compression
    min :: Compression -> Compression -> Compression
    Ord, Int -> Compression
    Compression -> Int
    Compression -> [Compression]
    Compression -> Compression
    Compression -> Compression -> [Compression]
    Compression -> Compression -> Compression -> [Compression]
    (Compression -> Compression)
    -> (Compression -> Compression)
    -> (Int -> Compression)
    -> (Compression -> Int)
    -> (Compression -> [Compression])
    -> (Compression -> Compression -> [Compression])
    -> (Compression -> Compression -> [Compression])
    -> (Compression -> Compression -> Compression -> [Compression])
    -> Enum Compression
    forall a.
    (a -> a)
    -> (a -> a)
    -> (Int -> a)
    -> (a -> Int)
    -> (a -> [a])
    -> (a -> a -> [a])
    -> (a -> a -> [a])
    -> (a -> a -> a -> [a])
    -> Enum a
    $csucc :: Compression -> Compression
    succ :: Compression -> Compression
    $cpred :: Compression -> Compression
    pred :: Compression -> Compression
    $ctoEnum :: Int -> Compression
    toEnum :: Int -> Compression
    $cfromEnum :: Compression -> Int
    fromEnum :: Compression -> Int
    $cenumFrom :: Compression -> [Compression]
    enumFrom :: Compression -> [Compression]
    $cenumFromThen :: Compression -> Compression -> [Compression]
    enumFromThen :: Compression -> Compression -> [Compression]
    $cenumFromTo :: Compression -> Compression -> [Compression]
    enumFromTo :: Compression -> Compression -> [Compression]
    $cenumFromThenTo :: Compression -> Compression -> Compression -> [Compression]
    enumFromThenTo :: Compression -> Compression -> Compression -> [Compression]
    Enum, Compression
    Compression -> Compression -> Bounded Compression
    forall a. a -> a -> Bounded a
    $cminBound :: Compression
    minBound :: Compression
    $cmaxBound :: Compression
    maxBound :: Compression
    Bounded)

    data CheckSums
        = CheckSums { CheckSums -> Maybe [Char]
    md5sum :: Maybe String
                    , CheckSums -> Maybe [Char]
    sha1   :: Maybe String
                    , CheckSums -> Maybe [Char]
    sha256 :: Maybe String
                    }
          deriving (ReadPrec [CheckSums]
    ReadPrec CheckSums
    Int -> ReadS CheckSums
    ReadS [CheckSums]
    (Int -> ReadS CheckSums)
    -> ReadS [CheckSums]
    -> ReadPrec CheckSums
    -> ReadPrec [CheckSums]
    -> Read CheckSums
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    $creadsPrec :: Int -> ReadS CheckSums
    readsPrec :: Int -> ReadS CheckSums
    $creadList :: ReadS [CheckSums]
    readList :: ReadS [CheckSums]
    $creadPrec :: ReadPrec CheckSums
    readPrec :: ReadPrec CheckSums
    $creadListPrec :: ReadPrec [CheckSums]
    readListPrec :: ReadPrec [CheckSums]
    Read, Int -> CheckSums -> ShowS
    [CheckSums] -> ShowS
    CheckSums -> [Char]
    (Int -> CheckSums -> ShowS)
    -> (CheckSums -> [Char])
    -> ([CheckSums] -> ShowS)
    -> Show CheckSums
    forall a.
    (Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> CheckSums -> ShowS
    showsPrec :: Int -> CheckSums -> ShowS
    $cshow :: CheckSums -> [Char]
    show :: CheckSums -> [Char]
    $cshowList :: [CheckSums] -> ShowS
    showList :: [CheckSums] -> ShowS
    Show, CheckSums -> CheckSums -> Bool
    (CheckSums -> CheckSums -> Bool)
    -> (CheckSums -> CheckSums -> Bool) -> Eq CheckSums
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: CheckSums -> CheckSums -> Bool
    == :: CheckSums -> CheckSums -> Bool
    $c/= :: CheckSums -> CheckSums -> Bool
    /= :: CheckSums -> CheckSums -> Bool
    Eq)

    -- |function-type for a function that downloads a file
    -- The timestamp is optional. If the local file is as new or newer
    -- than the remote copy, the download may be skipped.
    --
    -- A good choice might be a partially parameterized call to
    -- 'Debian.Apt.Methods.fetch'
    type Fetcher =
        URI ->              -- remote URI
        FilePath ->         -- local file name
        Maybe UTCTime ->    -- optional time stamp for local file
        IO Bool             -- True on success, False on failure

    -- |update - similar to apt-get update

    -- downloads the index files associated with a sources.list. The
    -- downloaded index files will have the same basenames that apt-get uses
    -- in \/var\/lib\/apt\/lists. You can almost use this function instead of
    -- calling apt-get update. However there are a few key differences:
    --  1. apt-get update also updates the binary cache files
    --  2. apt-get update uses the partial directory and lock file in\ /var\/lib\/apt\/lists
    --  3. apt-get update downloads the Release and Release.gpg files
    update :: Fetcher -- ^ function that will do actually downloading
           -> FilePath -- ^ download indexes to the directory (must already exist)
           -> String -- ^ binary architecture
           -> [DebSource] -- ^ sources.list
           -> IO [Maybe (FilePath, Compression)] -- ^ (basename of index file, compression status)
    update :: Fetcher
    -> [Char]
    -> [Char]
    -> [DebSource]
    -> IO [Maybe ([Char], Compression)]
    update Fetcher
    fetcher [Char]
    basePath [Char]
    arch [DebSource]
    sourcesList =
        ((URI, [Char]) -> IO (Maybe ([Char], Compression)))
    -> [(URI, [Char])] -> IO [Maybe ([Char], Compression)]
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
    mapM ((URI -> [Char] -> IO (Maybe ([Char], Compression)))
    -> (URI, [Char]) -> IO (Maybe ([Char], Compression))
    forall a b c. (a -> b -> c) -> (a, b) -> c
    uncurry ((URI -> [Char] -> IO (Maybe ([Char], Compression)))
     -> (URI, [Char]) -> IO (Maybe ([Char], Compression)))
    -> (URI -> [Char] -> IO (Maybe ([Char], Compression)))
    -> (URI, [Char])
    -> IO (Maybe ([Char], Compression))
    forall a b. (a -> b) -> a -> b
    $ Fetcher -> URI -> [Char] -> IO (Maybe ([Char], Compression))
    fetchIndex Fetcher
    fetcher) (((URI, [Char], DebSource) -> (URI, [Char]))
    -> [(URI, [Char], DebSource)] -> [(URI, [Char])]
    forall a b. (a -> b) -> [a] -> [b]
    map (\(URI
    uri, [Char]
    fp, DebSource
    _) -> (URI
    uri, ([Char]
    basePath [Char] -> ShowS
    </> [Char]
    fp))) ((DebSource -> [(URI, [Char], DebSource)])
    -> [DebSource] -> [(URI, [Char], DebSource)]
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap ([Char] -> DebSource -> [(URI, [Char], DebSource)]
    indexURIs [Char]
    arch) [DebSource]
    sourcesList))

    -- | download possibly compressed files
    -- NOTE: index uri must not include the .bz2 or .gz extension
    fetchIndex :: Fetcher -- ^ function that will do the actual fetch
               -> URI -- ^ remote URI of package index, without .bz2 or .gz extension
               -> FilePath -- ^ name to save downloaded file as, without .bz2 or .gz extension
               -> IO (Maybe (FilePath, Compression)) -- ^ (downloaded file name + extension, compression status)
    fetchIndex :: Fetcher -> URI -> [Char] -> IO (Maybe ([Char], Compression))
    fetchIndex Fetcher
    fetcher URI
    uri [Char]
    localPath =
        do let localPath' :: [Char]
    localPath' = [Char]
    localPath [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    ".bz2"
           --lm <- getLastModified localPath'
           res <- Fetcher
    fetcher (URI
    uri { uriPath = (uriPath uri) ++ ".bz2" }) [Char]
    localPath' Maybe UTCTime
    forall a. Maybe a
    Nothing
           if res
              then return $ Just (localPath', BZ2)
              else do let localPath' = [Char]
    localPath [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    ".gz"
                      lm <- getLastModified localPath'
                      res <- fetcher (uri { uriPath = (uriPath uri) ++ ".gz" }) localPath' lm
                      if res
                         then return $ Just (localPath', GZ)
                         else do lm <- getLastModified localPath
                                 res <- fetcher (uri { uriPath = (uriPath uri) }) localPath lm
                                 if res
                                    then return (Just (localPath, Uncompressed))
                                    else return Nothing

    -- |examine a DebSource line, and calculate for each section:
    --  - the URI to the uncompressed index file
    --  - the basename that apt-get would name the downloaded index
    -- FIXME: ExactPath dist will fail with error at runtime :(
    indexURIs :: String -- ^ which binary architecture
              -> DebSource -- ^ line from sources.list
              -> [(URI, FilePath, DebSource)] -- ^ (remote uri, local name, deb source for just this section)
    indexURIs :: [Char] -> DebSource -> [(URI, [Char], DebSource)]
    indexURIs [Char]
    arch DebSource
    debSource =
        (Section -> (URI, [Char], DebSource))
    -> [Section] -> [(URI, [Char], DebSource)]
    forall a b. (a -> b) -> [a] -> [b]
    map (\ Section
    section -> let (URI
    uri, [Char]
    fp) = SourceType
    -> [Char] -> VendorURI -> Codename -> Section -> (URI, [Char])
    calcPath (Getting SourceType DebSource SourceType -> DebSource -> SourceType
    forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
    view Getting SourceType DebSource SourceType
    Lens' DebSource SourceType
    sourceType DebSource
    debSource) [Char]
    arch VendorURI
    baseURI Codename
    release Section
    section
                          in (URI
    uri,[Char]
    fp, DebSource
    debSource { _sourceDist = (Right (release, [section])) }) ) [Section]
    sections
        where
          baseURI :: VendorURI
    baseURI = Getting VendorURI DebSource VendorURI -> DebSource -> VendorURI
    forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
    view Getting VendorURI DebSource VendorURI
    Lens' DebSource VendorURI
    sourceUri DebSource
    debSource
          (Codename
    release, [Section]
    sections) =
              ([Char] -> (Codename, [Section]))
    -> ((Codename, [Section]) -> (Codename, [Section]))
    -> Either [Char] (Codename, [Section])
    -> (Codename, [Section])
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either ([Char] -> [Char] -> (Codename, [Section])
    forall a. HasCallStack => [Char] -> a
    error ([Char] -> [Char] -> (Codename, [Section]))
    -> [Char] -> [Char] -> (Codename, [Section])
    forall a b. (a -> b) -> a -> b
    $ [Char]
    "indexURIs: support not implemented for exact path: " [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Doc -> [Char]
    render (DebSource -> Doc
    forall a. Pretty a => a -> Doc
    pretty DebSource
    debSource)) (Codename, [Section]) -> (Codename, [Section])
    forall a. a -> a
    id (Getting
      (Either [Char] (Codename, [Section]))
      DebSource
      (Either [Char] (Codename, [Section]))
    -> DebSource -> Either [Char] (Codename, [Section])
    forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
    view Getting
      (Either [Char] (Codename, [Section]))
      DebSource
      (Either [Char] (Codename, [Section]))
    Lens' DebSource (Either [Char] (Codename, [Section]))
    sourceDist DebSource
    debSource)

    -- |return a tuple for the section
    --  - the URI to the uncompressed index file
    --  - the basename that apt-get uses for the downloaded index
    -- FIXME: support for Release and Release.gpg
    calcPath :: SourceType -- ^ do we want Packages or Sources
             -> String  -- ^ The binary architecture to use for Packages
             -> VendorURI -- ^ base URI as it appears in sources.list
             -> Codename -- ^ the release (e.g., unstable, testing, stable, sid, etc)
             -> Section -- ^ the section (main, contrib, non-free, etc)
             -> (URI, [Char]) -- ^ (uri to index file, basename for the downloaded file)
    calcPath :: SourceType
    -> [Char] -> VendorURI -> Codename -> Section -> (URI, [Char])
    calcPath SourceType
    srcType [Char]
    arch VendorURI
    baseURI Codename
    release Section
    section =
              let indexPath :: [Char]
    indexPath = case SourceType
    srcType of
                          SourceType
    DebSrc -> [Char]
    "source/Sources"
                          SourceType
    Deb -> [Char]
    "binary-" [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    arch [Char] -> ShowS
    </> [Char]
    "Packages"
                  uri' :: URI
    uri' = ASetter URI URI [Char] [Char] -> ShowS -> URI -> URI
    forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
    over ASetter URI URI [Char] [Char]
    Lens' URI [Char]
    uriPathLens (\[Char]
    path -> [Char]
    path [Char] -> ShowS
    </> [Char]
    "dists" [Char] -> ShowS
    </> Codename -> [Char]
    codename Codename
    release [Char] -> ShowS
    </> Section -> [Char]
    sectionName' Section
    section [Char] -> ShowS
    </> [Char]
    indexPath) (Getting URI VendorURI URI -> VendorURI -> URI
    forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
    view Getting URI VendorURI URI
    Iso' VendorURI URI
    vendorURI VendorURI
    baseURI)
                  path :: [Char]
    path = Getting [Char] URI [Char] -> URI -> [Char]
    forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
    view Getting [Char] URI [Char]
    Lens' URI [Char]
    uriPathLens URI
    uri'
              in (URI
    uri', ShowS
    addPrefix (ShowS
    escapePath [Char]
    path))
              where
                addPrefix :: ShowS
    addPrefix [Char]
    s = [Char] -> Maybe [Char] -> Maybe [Char] -> Maybe [Char] -> ShowS
    forall {a} {a}.
    (Eq a, IsString a) =>
    a -> Maybe [Char] -> Maybe a -> Maybe [Char] -> ShowS
    prefix [Char]
    scheme Maybe [Char]
    user' Maybe [Char]
    pass' Maybe [Char]
    reg [Char]
    port [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ {- "_" ++ -} [Char]
    s
                prefix :: a -> Maybe [Char] -> Maybe a -> Maybe [Char] -> ShowS
    prefix a
    "http:" (Just [Char]
    user) Maybe a
    Nothing (Just [Char]
    host) [Char]
    port = [Char]
    user [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    host [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    port
                prefix a
    "http:" Maybe [Char]
    _ Maybe a
    _ (Just [Char]
    host) [Char]
    port = [Char]
    host [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    port
                prefix a
    "ftp:" Maybe [Char]
    _ Maybe a
    _ (Just [Char]
    host) [Char]
    _ = [Char]
    host
                prefix a
    "file:" Maybe [Char]
    Nothing Maybe a
    Nothing Maybe [Char]
    Nothing [Char]
    "" = [Char]
    ""
                prefix a
    "ssh:" (Just [Char]
    user) Maybe a
    Nothing (Just [Char]
    host) [Char]
    port = [Char]
    user [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    host [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    port
                prefix a
    "ssh:" Maybe [Char]
    _ Maybe a
    _ (Just [Char]
    host) [Char]
    port = [Char]
    host [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    port
                prefix a
    _ Maybe [Char]
    _ Maybe a
    _ Maybe [Char]
    _ [Char]
    _ = ShowS
    forall a. HasCallStack => [Char] -> a
    error ([Char]
    "calcPath: unsupported uri: " [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Getting [Char] VendorURI [Char] -> VendorURI -> [Char]
    forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
    view ((URI -> Const [Char] URI) -> VendorURI -> Const [Char] VendorURI
    Iso' VendorURI URI
    vendorURI ((URI -> Const [Char] URI) -> VendorURI -> Const [Char] VendorURI)
    -> Getting [Char] URI [Char] -> Getting [Char] VendorURI [Char]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (URI -> [Char]) -> Getting [Char] URI [Char]
    forall (p :: * -> * -> *) (f :: * -> *) s a.
    (Profunctor p, Contravariant f) =>
    (s -> a) -> Optic' p f s a
    to URI -> [Char]
    uriToString') VendorURI
    baseURI)
                user' :: Maybe [Char]
    user' = [Char] -> Maybe [Char]
    maybeOfString [Char]
    user
                pass' :: Maybe [Char]
    pass' = [Char] -> Maybe [Char]
    maybeOfString [Char]
    pass
                ([Char]
    user, [Char]
    pass) = (Char -> Bool) -> [Char] -> ([Char], [Char])
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    break (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    ':') [Char]
    userpass
                userpass :: [Char]
    userpass = [Char] -> (URIAuth -> [Char]) -> Maybe URIAuth -> [Char]
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe [Char]
    "" URIAuth -> [Char]
    uriUserInfo Maybe URIAuth
    auth
                reg :: Maybe [Char]
    reg = [Char] -> Maybe [Char]
    maybeOfString ([Char] -> Maybe [Char]) -> [Char] -> Maybe [Char]
    forall a b. (a -> b) -> a -> b
    $ [Char] -> (URIAuth -> [Char]) -> Maybe URIAuth -> [Char]
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe [Char]
    "" URIAuth -> [Char]
    uriRegName Maybe URIAuth
    auth
                port :: [Char]
    port = [Char] -> (URIAuth -> [Char]) -> Maybe URIAuth -> [Char]
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe [Char]
    "" URIAuth -> [Char]
    uriPort Maybe URIAuth
    auth
                scheme :: [Char]
    scheme = Getting [Char] VendorURI [Char] -> VendorURI -> [Char]
    forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
    view ((URI -> Const [Char] URI) -> VendorURI -> Const [Char] VendorURI
    Iso' VendorURI URI
    vendorURI ((URI -> Const [Char] URI) -> VendorURI -> Const [Char] VendorURI)
    -> Getting [Char] URI [Char] -> Getting [Char] VendorURI [Char]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (URI -> [Char]) -> Getting [Char] URI [Char]
    forall (p :: * -> * -> *) (f :: * -> *) s a.
    (Profunctor p, Contravariant f) =>
    (s -> a) -> Optic' p f s a
    to URI -> [Char]
    uriScheme) VendorURI
    baseURI
                auth :: Maybe URIAuth
    auth = Getting (Maybe URIAuth) VendorURI (Maybe URIAuth)
    -> VendorURI -> Maybe URIAuth
    forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
    view ((URI -> Const (Maybe URIAuth) URI)
    -> VendorURI -> Const (Maybe URIAuth) VendorURI
    Iso' VendorURI URI
    vendorURI ((URI -> Const (Maybe URIAuth) URI)
     -> VendorURI -> Const (Maybe URIAuth) VendorURI)
    -> ((Maybe URIAuth -> Const (Maybe URIAuth) (Maybe URIAuth))
        -> URI -> Const (Maybe URIAuth) URI)
    -> Getting (Maybe URIAuth) VendorURI (Maybe URIAuth)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (URI -> Maybe URIAuth)
    -> (Maybe URIAuth -> Const (Maybe URIAuth) (Maybe URIAuth))
    -> URI
    -> Const (Maybe URIAuth) URI
    forall (p :: * -> * -> *) (f :: * -> *) s a.
    (Profunctor p, Contravariant f) =>
    (s -> a) -> Optic' p f s a
    to URI -> Maybe URIAuth
    uriAuthority) VendorURI
    baseURI
                --path = uriPath baseURI

                escapePath :: String -> String
                escapePath :: ShowS
    escapePath [Char]
    s = [Char] -> [[Char]] -> [Char]
    forall a. [a] -> [[a]] -> [a]
    intercalate [Char]
    "_" ([[Char]] -> [Char]) -> [[Char]] -> [Char]
    forall a b. (a -> b) -> a -> b
    $ (Char -> Bool) -> [Char] -> [[Char]]
    forall a. Eq a => (a -> Bool) -> [a] -> [[a]]
    wordsBy (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    '/') [Char]
    s

                maybeOfString :: String -> Maybe String
                maybeOfString :: [Char] -> Maybe [Char]
    maybeOfString [Char]
    "" = Maybe [Char]
    forall a. Maybe a
    Nothing
                maybeOfString [Char]
    s = [Char] -> Maybe [Char]
    forall a. a -> Maybe a
    Just [Char]
    s

                wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
                wordsBy :: forall a. Eq a => (a -> Bool) -> [a] -> [[a]]
    wordsBy a -> Bool
    p [a]
    s =
                    case ((a -> Bool) -> [a] -> ([a], [a])
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    break a -> Bool
    p [a]
    s) of
                      ([a]
    s, []) -> [[a]
    s]
                      ([a]
    h, [a]
    t) -> [a]
    h [a] -> [[a]] -> [[a]]
    forall a. a -> [a] -> [a]
    : (a -> Bool) -> [a] -> [[a]]
    forall a. Eq a => (a -> Bool) -> [a] -> [[a]]
    wordsBy a -> Bool
    p (Int -> [a] -> [a]
    forall a. Int -> [a] -> [a]
    drop Int
    1 [a]
    t)

    -- |Parse a possibly compressed index file.
    controlFromIndex :: Compression -> FilePath -> L.ByteString -> Either ParseError (Control' Text)
    controlFromIndex :: Compression
    -> [Char] -> ByteString -> Either ParseError (Control' Text)
    controlFromIndex Compression
    GZ [Char]
    path ByteString
    s = (ParseError -> Either ParseError (Control' Text))
    -> (Control -> Either ParseError (Control' Text))
    -> Either ParseError Control
    -> Either ParseError (Control' Text)
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either ParseError -> Either ParseError (Control' Text)
    forall a b. a -> Either a b
    Left (Control' Text -> Either ParseError (Control' Text)
    forall a b. b -> Either a b
    Right (Control' Text -> Either ParseError (Control' Text))
    -> (Control -> Control' Text)
    -> Control
    -> Either ParseError (Control' Text)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Control -> Control' Text
    decodeControl) (Either ParseError Control -> Either ParseError (Control' Text))
    -> (ByteString -> Either ParseError Control)
    -> ByteString
    -> Either ParseError (Control' Text)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Char] -> ByteString -> Either ParseError Control
    forall a.
    ControlFunctions a =>
    [Char] -> a -> Either ParseError (Control' a)
    parseControl [Char]
    path (ByteString -> Either ParseError Control)
    -> (ByteString -> ByteString)
    -> ByteString
    -> Either ParseError Control
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [ByteString] -> ByteString
    B.concat ([ByteString] -> ByteString)
    -> (ByteString -> [ByteString]) -> ByteString -> ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> [ByteString]
    L.toChunks (ByteString -> [ByteString])
    -> (ByteString -> ByteString) -> ByteString -> [ByteString]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> ByteString
    GZip.decompress (ByteString -> Either ParseError (Control' Text))
    -> ByteString -> Either ParseError (Control' Text)
    forall a b. (a -> b) -> a -> b
    $ ByteString
    s
    controlFromIndex Compression
    BZ2 [Char]
    path ByteString
    s = (ParseError -> Either ParseError (Control' Text))
    -> (Control -> Either ParseError (Control' Text))
    -> Either ParseError Control
    -> Either ParseError (Control' Text)
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either ParseError -> Either ParseError (Control' Text)
    forall a b. a -> Either a b
    Left (Control' Text -> Either ParseError (Control' Text)
    forall a b. b -> Either a b
    Right (Control' Text -> Either ParseError (Control' Text))
    -> (Control -> Control' Text)
    -> Control
    -> Either ParseError (Control' Text)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Control -> Control' Text
    decodeControl) (Either ParseError Control -> Either ParseError (Control' Text))
    -> (ByteString -> Either ParseError Control)
    -> ByteString
    -> Either ParseError (Control' Text)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Char] -> ByteString -> Either ParseError Control
    forall a.
    ControlFunctions a =>
    [Char] -> a -> Either ParseError (Control' a)
    parseControl [Char]
    path (ByteString -> Either ParseError Control)
    -> (ByteString -> ByteString)
    -> ByteString
    -> Either ParseError Control
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [ByteString] -> ByteString
    B.concat ([ByteString] -> ByteString)
    -> (ByteString -> [ByteString]) -> ByteString -> ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> [ByteString]
    L.toChunks (ByteString -> [ByteString])
    -> (ByteString -> ByteString) -> ByteString -> [ByteString]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> ByteString
    BZip.decompress (ByteString -> Either ParseError (Control' Text))
    -> ByteString -> Either ParseError (Control' Text)
    forall a b. (a -> b) -> a -> b
    $ ByteString
    s
    controlFromIndex Compression
    Uncompressed [Char]
    path ByteString
    s = (ParseError -> Either ParseError (Control' Text))
    -> (Control -> Either ParseError (Control' Text))
    -> Either ParseError Control
    -> Either ParseError (Control' Text)
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either ParseError -> Either ParseError (Control' Text)
    forall a b. a -> Either a b
    Left (Control' Text -> Either ParseError (Control' Text)
    forall a b. b -> Either a b
    Right (Control' Text -> Either ParseError (Control' Text))
    -> (Control -> Control' Text)
    -> Control
    -> Either ParseError (Control' Text)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Control -> Control' Text
    decodeControl) (Either ParseError Control -> Either ParseError (Control' Text))
    -> (ByteString -> Either ParseError Control)
    -> ByteString
    -> Either ParseError (Control' Text)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Char] -> ByteString -> Either ParseError Control
    forall a.
    ControlFunctions a =>
    [Char] -> a -> Either ParseError (Control' a)
    parseControl [Char]
    path (ByteString -> Either ParseError Control)
    -> (ByteString -> ByteString)
    -> ByteString
    -> Either ParseError Control
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [ByteString] -> ByteString
    B.concat ([ByteString] -> ByteString)
    -> (ByteString -> [ByteString]) -> ByteString -> ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> [ByteString]
    L.toChunks (ByteString -> Either ParseError (Control' Text))
    -> ByteString -> Either ParseError (Control' Text)
    forall a b. (a -> b) -> a -> b
    $ ByteString
    s

    -- |parse an index possibly compressed file
    controlFromIndex' :: Compression -> FilePath -> IO (Either ParseError (Control' Text))
    controlFromIndex' :: Compression -> [Char] -> IO (Either ParseError (Control' Text))
    controlFromIndex' Compression
    compression [Char]
    path = [Char] -> IO ByteString
    L.readFile [Char]
    path IO ByteString
    -> (ByteString -> IO (Either ParseError (Control' Text)))
    -> IO (Either ParseError (Control' Text))
    forall a b. IO a -> (a -> IO b) -> IO b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= Either ParseError (Control' Text)
    -> IO (Either ParseError (Control' Text))
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either ParseError (Control' Text)
     -> IO (Either ParseError (Control' Text)))
    -> (ByteString -> Either ParseError (Control' Text))
    -> ByteString
    -> IO (Either ParseError (Control' Text))
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Compression
    -> [Char] -> ByteString -> Either ParseError (Control' Text)
    controlFromIndex Compression
    compression [Char]
    path

    type Size = Integer
    type FileTuple = (CheckSums, Size, FilePath)

    -- |A release file contains a list of indexes (Packages\/Sources). Each
    -- Package or Source index may appear multiple times because it may be
    -- compressed several different ways. This function will return an
    -- assoc list where the key is the name of the uncompressed package
    -- index name and the value is the list of (file, compression) which
    -- decompress to the key.
    groupIndexes :: [FileTuple] -> [(FilePath, [(FileTuple, Compression)])]
    groupIndexes :: [FileTuple] -> [([Char], [(FileTuple, Compression)])]
    groupIndexes [FileTuple]
    indexFiles =
        Map [Char] [(FileTuple, Compression)]
    -> [([Char], [(FileTuple, Compression)])]
    forall k a. Map k a -> [(k, a)]
    M.toList (Map [Char] [(FileTuple, Compression)]
     -> [([Char], [(FileTuple, Compression)])])
    -> Map [Char] [(FileTuple, Compression)]
    -> [([Char], [(FileTuple, Compression)])]
    forall a b. (a -> b) -> a -> b
    $ ([(FileTuple, Compression)]
     -> [(FileTuple, Compression)] -> [(FileTuple, Compression)])
    -> [([Char], [(FileTuple, Compression)])]
    -> Map [Char] [(FileTuple, Compression)]
    forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
    M.fromListWith [(FileTuple, Compression)]
    -> [(FileTuple, Compression)] -> [(FileTuple, Compression)]
    forall {a}.
    [(a, Compression)] -> [(a, Compression)] -> [(a, Compression)]
    combine ([([Char], [(FileTuple, Compression)])]
     -> Map [Char] [(FileTuple, Compression)])
    -> [([Char], [(FileTuple, Compression)])]
    -> Map [Char] [(FileTuple, Compression)]
    forall a b. (a -> b) -> a -> b
    $ (FileTuple -> ([Char], [(FileTuple, Compression)]))
    -> [FileTuple] -> [([Char], [(FileTuple, Compression)])]
    forall a b. (a -> b) -> [a] -> [b]
    map FileTuple -> ([Char], [(FileTuple, Compression)])
    forall {a} {b}.
    (a, b, [Char]) -> ([Char], [((a, b, [Char]), Compression)])
    makeKV [FileTuple]
    indexFiles
        where
          makeKV :: (a, b, [Char]) -> ([Char], [((a, b, [Char]), Compression)])
    makeKV fileTuple :: (a, b, [Char])
    fileTuple@(a
    _,b
    _,[Char]
    fp) =
              let ([Char]
    name, Compression
    compressionMethod) = [Char] -> ([Char], Compression)
    uncompressedName [Char]
    fp
              in
                ([Char]
    name, [((a, b, [Char])
    fileTuple, Compression
    compressionMethod)])
          combine :: [(a, Compression)] -> [(a, Compression)] -> [(a, Compression)]
    combine = (\[(a, Compression)]
    x [(a, Compression)]
    y -> ((a, Compression) -> (a, Compression) -> Ordering)
    -> [(a, Compression)] -> [(a, Compression)]
    forall a. (a -> a -> Ordering) -> [a] -> [a]
    sortBy (Compression -> Compression -> Ordering
    forall a. Ord a => a -> a -> Ordering
    compare (Compression -> Compression -> Ordering)
    -> ((a, Compression) -> Compression)
    -> (a, Compression)
    -> (a, Compression)
    -> Ordering
    forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
    `on` (a, Compression) -> Compression
    forall a b. (a, b) -> b
    snd) ([(a, Compression)]
    x [(a, Compression)] -> [(a, Compression)] -> [(a, Compression)]
    forall a. [a] -> [a] -> [a]
    ++ [(a, Compression)]
    y))
    {-
          with t@(_,_,fp) m =
              let (un, compression) =
              in
                M.insertWith
    -}

    {-
    groupIndexes' :: String ->[FileTuple] -> [(FilePath, [(FileTuple, Compression)])]
    groupIndexes' iType indexFiles =
        M.toList (foldr (insertType iType) M.empty indexFiles)
        where
          insertType iType t@(_,_,fp) m =
              case uncompressedName' iType fp of
                Nothing -> m
                (Just (un, compression)) ->
                    M.insertWith (\x y -> sortBy (compare `on` snd) (x ++ y)) un [(t, compression)] m
    -}

    -- |The release file contains the checksums for the uncompressed
    -- package indexes, even if the uncompressed package indexes are not
    -- stored on the server. This function returns the list of files that
    -- actually exist.
    filterExists :: FilePath -> (FilePath, [(FileTuple, Compression)]) -> IO (FilePath, [(FileTuple, Compression)])
    filterExists :: [Char]
    -> ([Char], [(FileTuple, Compression)])
    -> IO ([Char], [(FileTuple, Compression)])
    filterExists [Char]
    distDir ([Char]
    fp, [(FileTuple, Compression)]
    alternatives) =
              do e <- ((FileTuple, Compression) -> IO Bool)
    -> [(FileTuple, Compression)] -> IO [(FileTuple, Compression)]
    forall (m :: * -> *) a.
    Applicative m =>
    (a -> m Bool) -> [a] -> m [a]
    filterM ( \((CheckSums
    _,Size
    _,[Char]
    fp),Compression
    _) -> [Char] -> IO Bool
    fileExist ([Char]
    distDir [Char] -> ShowS
    </> [Char]
    fp)) [(FileTuple, Compression)]
    alternatives
                 -- when (null e) (error $ "None of these files exist: " ++ show alternatives)
                 return (fp, e)

    findIndexes :: FilePath -> String -> [FileTuple] -> IO [(FileTuple, Compression)]
    findIndexes :: [Char] -> [Char] -> [FileTuple] -> IO [(FileTuple, Compression)]
    findIndexes [Char]
    distDir [Char]
    iType [FileTuple]
    controlFiles =
        let indexes :: [([Char], [(FileTuple, Compression)])]
    indexes = [FileTuple] -> [([Char], [(FileTuple, Compression)])]
    groupIndexes [FileTuple]
    controlFiles
        in
          do indexes' <- (([Char], [(FileTuple, Compression)])
     -> IO ([Char], [(FileTuple, Compression)]))
    -> [([Char], [(FileTuple, Compression)])]
    -> IO [([Char], [(FileTuple, Compression)])]
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
    mapM ([Char]
    -> ([Char], [(FileTuple, Compression)])
    -> IO ([Char], [(FileTuple, Compression)])
    filterExists [Char]
    distDir) ((([Char], [(FileTuple, Compression)]) -> Bool)
    -> [([Char], [(FileTuple, Compression)])]
    -> [([Char], [(FileTuple, Compression)])]
    forall a. (a -> Bool) -> [a] -> [a]
    filter ([Char] -> ([Char], [(FileTuple, Compression)]) -> Bool
    forall {a} {b}. Eq a => [a] -> ([a], b) -> Bool
    isType [Char]
    iType) [([Char], [(FileTuple, Compression)])]
    indexes)
             return $ map (head . snd) (filter (not . List.null . snd) indexes')
        where
          isType :: [a] -> ([a], b) -> Bool
    isType [a]
    iType ([a]
    fp, b
    _) = [a]
    iType [a] -> [a] -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    `isSuffixOf` [a]
    fp

    {-
    findIndexes' :: FilePath -> String -> [FileTuple] -> IO [(FileTuple, Compression)]
    findIndexes' distDir iType controlFiles =
        let m = groupIndexes' iType controlFiles
        in
          do m' <- mapM (filterExists distDir) m
             return $ map (head . snd) (filter (not . null . snd) m')
    -}

          -- insertType :: String -> (CheckSums, Integer, FilePath) -> M.Map FilePath ((CheckSums, Integer, FilePath), Compression) -> M.Map FilePath ((CheckSums, Integer, FilePath), Compression)

    {-
    uncompressedName' :: String -> FilePath -> Maybe (FilePath, Compression)
    uncompressedName' iType fp
              | isSuffixOf iType fp = Just (fp, Uncompressed)
              | isSuffixOf (iType ++".gz") fp = Just (reverse . (drop 3) . reverse $ fp, GZ)
              | isSuffixOf (iType ++".bz2") fp = Just (reverse . (drop 4) . reverse $ fp, BZ2)
              | otherwise = Nothing
    -}

    uncompressedName :: FilePath -> (FilePath, Compression)
    uncompressedName :: [Char] -> ([Char], Compression)
    uncompressedName [Char]
    fp
              | [Char] -> [Char] -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    isSuffixOf [Char]
    ".gz"  [Char]
    fp = (ShowS
    forall a. [a] -> [a]
    reverse ShowS -> ShowS -> ShowS
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Int -> ShowS
    forall a. Int -> [a] -> [a]
    drop Int
    3) ShowS -> ShowS -> ShowS
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ShowS
    forall a. [a] -> [a]
    reverse ShowS -> ShowS
    forall a b. (a -> b) -> a -> b
    $ [Char]
    fp, Compression
    GZ)
              | [Char] -> [Char] -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    isSuffixOf [Char]
    ".bz2" [Char]
    fp = (ShowS
    forall a. [a] -> [a]
    reverse ShowS -> ShowS -> ShowS
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Int -> ShowS
    forall a. Int -> [a] -> [a]
    drop Int
    4) ShowS -> ShowS -> ShowS
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ShowS
    forall a. [a] -> [a]
    reverse ShowS -> ShowS
    forall a b. (a -> b) -> a -> b
    $ [Char]
    fp, Compression
    BZ2)
              | Bool
    otherwise            = ([Char]
    fp, Compression
    Uncompressed)

    indexesInRelease :: (FilePath -> Bool)
                     -> Control' Text -- ^ A release file
                     -> [(CheckSums, Integer, FilePath)] -- ^
    indexesInRelease :: ([Char] -> Bool) -> Control' Text -> [FileTuple]
    indexesInRelease [Char] -> Bool
    filterp (Control [Paragraph' Text
    p]) =
        -- In a release file we should find one or more of the fields
        -- "SHA256", "SHA1", or "MD5Sum", each containing a list of triples
        case ([[Char]], [[FileTuple]])
    attempts of
          ([[Char]]
    _, [FileTuple]
    fps:[[FileTuple]]
    _) -> (FileTuple -> Bool) -> [FileTuple] -> [FileTuple]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (\(CheckSums
    _,Size
    _,[Char]
    fp) -> [Char] -> Bool
    filterp [Char]
    fp) [FileTuple]
    fps
          ([[Char]]
    errs,  [[FileTuple]]
    _) -> [Char] -> [FileTuple]
    forall a. HasCallStack => [Char] -> a
    error ([Char] -> [FileTuple]) -> [Char] -> [FileTuple]
    forall a b. (a -> b) -> a -> b
    $ [Char]
    "No indexes in release: " [Char] -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> [Char] -> [[Char]] -> [Char]
    forall a. [a] -> [[a]] -> [a]
    intercalate [Char]
    ", " [[Char]]
    errs
        where
          attempts :: ([[Char]], [[FileTuple]])
    attempts = [Either [Char] [FileTuple]] -> ([[Char]], [[FileTuple]])
    forall a b. [Either a b] -> ([a], [b])
    partitionEithers
            [ ([Char] -> CheckSums) -> [Char] -> Either [Char] [FileTuple]
    attempt [Char] -> CheckSums
    makeSHA256 [Char]
    "SHA256"
            , ([Char] -> CheckSums) -> [Char] -> Either [Char] [FileTuple]
    attempt [Char] -> CheckSums
    makeSHA1 [Char]
    "SHA1"
            , ([Char] -> CheckSums) -> [Char] -> Either [Char] [FileTuple]
    attempt [Char] -> CheckSums
    makeMD5 [Char]
    "MD5Sum" ]

          attempt :: ([Char] -> CheckSums) -> [Char] -> Either [Char] [FileTuple]
    attempt [Char] -> CheckSums
    mksum [Char]
    fn = ([Char] -> CheckSums)
    -> [(Text, Text, Text)] -> Either [Char] [FileTuple]
    makeTuples [Char] -> CheckSums
    mksum ([(Text, Text, Text)] -> Either [Char] [FileTuple])
    -> Either [Char] [(Text, Text, Text)] -> Either [Char] [FileTuple]
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< Text -> Either [Char] [(Text, Text, Text)]
    makeTriples (Text -> Either [Char] [(Text, Text, Text)])
    -> Either [Char] Text -> Either [Char] [(Text, Text, Text)]
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<<
            Either [Char] Text
    -> (Text -> Either [Char] Text) -> Maybe Text -> Either [Char] Text
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe ([Char] -> Either [Char] Text
    forall a b. a -> Either a b
    Left ([Char] -> Either [Char] Text) -> [Char] -> Either [Char] Text
    forall a b. (a -> b) -> a -> b
    $ [Char]
    "No " [Char] -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> [Char]
    fn [Char] -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> [Char]
    " Field") Text -> Either [Char] Text
    forall a b. b -> Either a b
    Right ([Char] -> Paragraph' Text -> Maybe Text
    forall a. ControlFunctions a => [Char] -> Paragraph' a -> Maybe a
    fieldValue [Char]
    fn Paragraph' Text
    p)

          makeSHA256 :: [Char] -> CheckSums
    makeSHA256 [Char]
    s = CheckSums {md5sum :: Maybe [Char]
    md5sum = Maybe [Char]
    forall a. Maybe a
    Nothing, sha1 :: Maybe [Char]
    sha1 = Maybe [Char]
    forall a. Maybe a
    Nothing, sha256 :: Maybe [Char]
    sha256 = [Char] -> Maybe [Char]
    forall a. a -> Maybe a
    Just [Char]
    s}
          makeSHA1 :: [Char] -> CheckSums
    makeSHA1 [Char]
    s = CheckSums {md5sum :: Maybe [Char]
    md5sum = Maybe [Char]
    forall a. Maybe a
    Nothing, sha1 :: Maybe [Char]
    sha1 = [Char] -> Maybe [Char]
    forall a. a -> Maybe a
    Just [Char]
    s, sha256 :: Maybe [Char]
    sha256 = Maybe [Char]
    forall a. Maybe a
    Nothing}
          makeMD5 :: [Char] -> CheckSums
    makeMD5 [Char]
    s = CheckSums {md5sum :: Maybe [Char]
    md5sum = [Char] -> Maybe [Char]
    forall a. a -> Maybe a
    Just [Char]
    s, sha1 :: Maybe [Char]
    sha1 = Maybe [Char]
    forall a. Maybe a
    Nothing, sha256 :: Maybe [Char]
    sha256 = Maybe [Char]
    forall a. Maybe a
    Nothing}

          makeTuples :: (String -> CheckSums) -> [(Text, Text, Text)] -> Either String [(CheckSums, Integer, FilePath)]
          makeTuples :: ([Char] -> CheckSums)
    -> [(Text, Text, Text)] -> Either [Char] [FileTuple]
    makeTuples [Char] -> CheckSums
    mk [(Text, Text, Text)]
    triples =
              case [Either [Char] FileTuple] -> ([[Char]], [FileTuple])
    forall a b. [Either a b] -> ([a], [b])
    partitionEithers (((Text, Text, Text) -> Either [Char] FileTuple)
    -> [(Text, Text, Text)] -> [Either [Char] FileTuple]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (([Char] -> CheckSums)
    -> (Text, Text, Text) -> Either [Char] FileTuple
    makeTuple [Char] -> CheckSums
    mk) [(Text, Text, Text)]
    triples) of
                ([], [FileTuple]
    tuples) -> [FileTuple] -> Either [Char] [FileTuple]
    forall a b. b -> Either a b
    Right [FileTuple]
    tuples
                ([Char]
    s : [[Char]]
    _, [FileTuple]
    _) -> [Char] -> Either [Char] [FileTuple]
    forall a b. a -> Either a b
    Left [Char]
    s

          makeTuple :: (String -> CheckSums) -> (Text, Text, Text) -> Either String (CheckSums, Integer, FilePath)
          makeTuple :: ([Char] -> CheckSums)
    -> (Text, Text, Text) -> Either [Char] FileTuple
    makeTuple [Char] -> CheckSums
    mk (Text
    sum, Text
    size, Text
    fp) =
              (,,) (CheckSums -> Size -> [Char] -> FileTuple)
    -> Either [Char] CheckSums
    -> Either [Char] (Size -> [Char] -> FileTuple)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> CheckSums -> Either [Char] CheckSums
    forall a. a -> Either [Char] a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure ([Char] -> CheckSums
    mk (Text -> [Char]
    Text.unpack Text
    sum))
                   Either [Char] (Size -> [Char] -> FileTuple)
    -> Either [Char] Size -> Either [Char] ([Char] -> FileTuple)
    forall a b.
    Either [Char] (a -> b) -> Either [Char] a -> Either [Char] b
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> Either [Char] Size
    -> (Size -> Either [Char] Size) -> Maybe Size -> Either [Char] Size
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe ([Char] -> Either [Char] Size
    forall a b. a -> Either a b
    Left ([Char]
    "Invalid size field: " [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Text -> [Char]
    forall a. Show a => a -> [Char]
    show Text
    size)) Size -> Either [Char] Size
    forall a b. b -> Either a b
    Right ([Char] -> Maybe Size
    forall a. Read a => [Char] -> Maybe a
    readMaybe (Text -> [Char]
    Text.unpack Text
    size))
                   Either [Char] ([Char] -> FileTuple)
    -> Either [Char] [Char] -> Either [Char] FileTuple
    forall a b.
    Either [Char] (a -> b) -> Either [Char] a -> Either [Char] b
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> [Char] -> Either [Char] [Char]
    forall a. a -> Either [Char] a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (Text -> [Char]
    Text.unpack Text
    fp)

          makeTriples :: Text -> Either String [(Text, Text, Text)]
          makeTriples :: Text -> Either [Char] [(Text, Text, Text)]
    makeTriples Text
    t = case [Either [Char] (Text, Text, Text)]
    -> ([[Char]], [(Text, Text, Text)])
    forall a b. [Either a b] -> ([a], [b])
    partitionEithers ((Text -> Either [Char] (Text, Text, Text))
    -> [Text] -> [Either [Char] (Text, Text, Text)]
    forall a b. (a -> b) -> [a] -> [b]
    map Text -> Either [Char] (Text, Text, Text)
    makeTriple (Text -> [Text]
    Text.lines Text
    t)) of
                            ([], [(Text, Text, Text)]
    xs) -> [(Text, Text, Text)] -> Either [Char] [(Text, Text, Text)]
    forall a b. b -> Either a b
    Right [(Text, Text, Text)]
    xs
                            ([Char]
    s : [[Char]]
    _, [(Text, Text, Text)]
    _) -> [Char] -> Either [Char] [(Text, Text, Text)]
    forall a b. a -> Either a b
    Left [Char]
    s

          makeTriple :: Text -> Either String (Text, Text, Text)
          makeTriple :: Text -> Either [Char] (Text, Text, Text)
    makeTriple Text
    t = case Text -> [Text]
    Text.words Text
    t of
                           [Text
    a, Text
    b, Text
    c] -> (Text, Text, Text) -> Either [Char] (Text, Text, Text)
    forall a b. b -> Either a b
    Right (Text
    a, Text
    b, Text
    c)
                           [Text]
    _ -> [Char] -> Either [Char] (Text, Text, Text)
    forall a b. a -> Either a b
    Left ([Char]
    "Invalid checksum line: " [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Text -> [Char]
    forall a. Show a => a -> [Char]
    show Text
    t)
    indexesInRelease [Char] -> Bool
    _ Control' Text
    x = [Char] -> [FileTuple]
    forall a. HasCallStack => [Char] -> a
    error ([Char] -> [FileTuple]) -> [Char] -> [FileTuple]
    forall a b. (a -> b) -> a -> b
    $ [Char]
    "Invalid release file: " [Char] -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> Text -> [Char]
    Text.unpack ([Text] -> Text
    Text.concat (Control' Text -> [Text]
    formatControl Control' Text
    x))

    -- |make a FileTuple for a file found on the local disk
    -- returns 'Nothing' if the file does not exist.
    tupleFromFilePath :: FilePath -> FilePath -> IO (Maybe FileTuple)
    tupleFromFilePath :: [Char] -> [Char] -> IO (Maybe FileTuple)
    tupleFromFilePath [Char]
    basePath [Char]
    fp =
              do e <- [Char] -> IO Bool
    fileExist ([Char]
    basePath [Char] -> ShowS
    </> [Char]
    fp)
                 if not e
                  then return Nothing
                  else do size <- getFileStatus (basePath </> fp) >>= return . fromIntegral . fileSize
                          md5 <- L.readFile (basePath </> fp) >>= return . show . MD5.md5
                          sha1 <- L.readFile (basePath </> fp) >>= return . show . SHA.sha1
                          sha256 <- L.readFile (basePath </> fp) >>= return . show . SHA.sha256
                          return $ Just (CheckSums { md5sum = Just md5, sha1 = Just sha1, sha256 = Just sha256 }, size, fp)

    -- |find the Contents-* files. These are not listed in the Release file
    findContentsFiles :: (FilePath -> Bool) -> FilePath -> IO [FilePath]
    findContentsFiles :: ([Char] -> Bool) -> [Char] -> IO [[Char]]
    findContentsFiles [Char] -> Bool
    filterP [Char]
    distDir =
              do files <- [Char] -> IO [[Char]]
    getDirectoryContents [Char]
    distDir
                 return $ filter filterP $ filter (isPrefixOf "Contents-" . takeBaseName) files