    {-# LANGUAGE FlexibleContexts #-}

    module Propellor.Property.Postfix where

    import Propellor.Base
    import qualified Propellor.Property.Apt as Apt
    import qualified Propellor.Property.File as File
    import qualified Propellor.Property.Service as Service
    import qualified Propellor.Property.User as User

    import qualified Data.Map as M
    import Data.List
    import Data.Char

    installed :: Property DebianLike
    installed :: Property DebianLike
    installed = Package -> Property DebianLike
    Apt.serviceInstalledRunning "postfix"

    restarted :: Property DebianLike
    restarted :: Property DebianLike
    restarted = Package -> Property DebianLike
    Service.restarted "postfix"

    reloaded :: Property DebianLike
    reloaded :: Property DebianLike
    reloaded = Package -> Property DebianLike
    Service.reloaded "postfix"

    -- | Configures postfix as a satellite system, which
    -- relays all mail through a relay host, which defaults to smtp.domain,
    -- but can be changed by @mainCf "relayhost"@.
    --
    -- The smarthost may refuse to relay mail on to other domains, without
    -- further configuration/keys. But this should be enough to get cron job
    -- mail flowing to a place where it will be seen.
    satellite :: Property DebianLike
    satellite :: Property DebianLike
    satellite = IO Bool -> Property DebianLike -> Property DebianLike
    forall (p :: * -> *) i (m :: * -> *).
    (Checkable p i, LiftPropellor m) =>
    m Bool -> p i -> Property i
    check (Bool -> Bool
    not (Bool -> Bool) -> IO Bool -> IO Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Package -> IO Bool
    mainCfIsSet "relayhost") Property DebianLike
    setup
    	Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    installed
      where
    	desc :: Package
    desc = "postfix satellite system"
    	setup :: Property DebianLike
    	setup :: Property DebianLike
    setup = Package
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
        -> Propellor Result)
    -> Property DebianLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Package
    -> (OuterMetaTypesWitness metatypes -> Propellor Result)
    -> Property (MetaTypes metatypes)
    property' Package
    desc ((OuterMetaTypesWitness
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
      -> Propellor Result)
     -> Property DebianLike)
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
        -> Propellor Result)
    -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $ \w :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
    w -> do
    		Package
    hn <- (Host -> Package) -> Propellor Package
    forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
    asks Host -> Package
    hostName
    		let (_, domain :: Package
    domain) = (Char -> Bool) -> Package -> (Package, Package)
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    separate (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == '.') Package
    hn
    		OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
    -> Property DebianLike -> Propellor Result
    forall (inner :: [MetaType]) (outer :: [MetaType]).
    EnsurePropertyAllowed inner outer =>
    OuterMetaTypesWitness outer
    -> Property (MetaTypes inner) -> Propellor Result
    ensureProperty OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
    w (Property DebianLike -> Propellor Result)
    -> Property DebianLike -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ Package -> Props DebianLike -> Property DebianLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Package
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties Package
    desc (Props DebianLike -> Property DebianLike)
    -> Props DebianLike -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    			Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Package -> [(Package, Package, Package)] -> Property DebianLike
    Apt.reConfigure "postfix"
    				[ ("postfix/main_mailer_type", "select", "Satellite system")
    				, ("postfix/root_address", "string", "root")
    				, ("postfix/destinations", "string", "localhost")
    				, ("postfix/mailname", "string", Package
    hn)
    				]
    			Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & (Package, Package) -> Property UnixLike
    mainCf ("relayhost", "smtp." Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ Package
    domain)
    				Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    reloaded

    -- | Sets up a file by running a property (which the filename is passed
    -- to). If the setup property makes a change, postmap will be run on the
    -- file, and postfix will be reloaded.
    mappedFile
    	:: Combines (Property x) (Property UnixLike)
    	=> FilePath
    	-> (FilePath -> Property x)
    	-> CombinedType (Property x) (Property UnixLike)
    mappedFile :: Package
    -> (Package -> Property x)
    -> CombinedType (Property x) (Property UnixLike)
    mappedFile f :: Package
    f setup :: Package -> Property x
    setup = Package -> Property x
    setup Package
    f
    	Property x
    -> Property UnixLike
    -> CombinedType (Property x) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` (Package -> [Package] -> UncheckedProperty UnixLike
    cmdProperty "postmap" [Package
    f] UncheckedProperty UnixLike -> Result -> Property UnixLike
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Result -> Property i
    `assume` Result
    MadeChange)

    -- | Run newaliases command, which should be done after changing
    -- @/etc/aliases@.
    newaliases :: Property UnixLike
    newaliases :: Property UnixLike
    newaliases = IO Bool -> UncheckedProperty UnixLike -> Property UnixLike
    forall (p :: * -> *) i (m :: * -> *).
    (Checkable p i, LiftPropellor m) =>
    m Bool -> p i -> Property i
    check ("/etc/aliases" Package -> Package -> IO Bool
    `isNewerThan` "/etc/aliases.db")
    	(Package -> [Package] -> UncheckedProperty UnixLike
    cmdProperty "newaliases" [])

    -- | The main config file for postfix.
    mainCfFile :: FilePath
    mainCfFile :: Package
    mainCfFile = "/etc/postfix/main.cf"

    -- | Sets a main.cf @name=value@ pair. Does not reload postfix immediately.
    mainCf :: (String, String) -> Property UnixLike
    mainCf :: (Package, Package) -> Property UnixLike
    mainCf (name :: Package
    name, value :: Package
    value) = IO Bool -> UncheckedProperty UnixLike -> Property UnixLike
    forall (p :: * -> *) i (m :: * -> *).
    (Checkable p i, LiftPropellor m) =>
    m Bool -> p i -> Property i
    check IO Bool
    notset UncheckedProperty UnixLike
    set
    	Property UnixLike -> Package -> Property UnixLike
    forall p. IsProp p => p -> Package -> p
    `describe` ("postfix main.cf " Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ Package
    setting)
      where
    	setting :: Package
    setting = Package
    name Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ "=" Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ Package
    value
    	notset :: IO Bool
    notset = (Maybe Package -> Maybe Package -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Package -> Maybe Package
    forall a. a -> Maybe a
    Just Package
    value) (Maybe Package -> Bool) -> IO (Maybe Package) -> IO Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Package -> IO (Maybe Package)
    getMainCf Package
    name
    	set :: UncheckedProperty UnixLike
    set = Package -> [Package] -> UncheckedProperty UnixLike
    cmdProperty "postconf" ["-e", Package
    setting]

    -- | Gets a main.cf setting.
    getMainCf :: String -> IO (Maybe String)
    getMainCf :: Package -> IO (Maybe Package)
    getMainCf name :: Package
    name = [Package] -> Maybe Package
    parse ([Package] -> Maybe Package)
    -> (Package -> [Package]) -> Package -> Maybe Package
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Package -> [Package]
    lines (Package -> Maybe Package) -> IO Package -> IO (Maybe Package)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Package -> [Package] -> IO Package
    readProcess "postconf" [Package
    name]
      where
    	parse :: [Package] -> Maybe Package
    parse (l :: Package
    l:_) = Package -> Maybe Package
    forall a. a -> Maybe a
    Just (Package -> Maybe Package) -> Package -> Maybe Package
    forall a b. (a -> b) -> a -> b
    $
    		case (Char -> Bool) -> Package -> (Package, Package)
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    separate (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == '=') Package
    l of
    			(_, (' ':v :: Package
    v)) -> Package
    v
    			(_, v :: Package
    v) -> Package
    v
    	parse [] = Maybe Package
    forall a. Maybe a
    Nothing

    -- | Checks if a main.cf field is set. A field that is set to
    -- the empty string is considered not set.
    mainCfIsSet :: String -> IO Bool
    mainCfIsSet :: Package -> IO Bool
    mainCfIsSet name :: Package
    name = do
    	Maybe Package
    v <- Package -> IO (Maybe Package)
    getMainCf Package
    name
    	Bool -> IO Bool
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Bool -> IO Bool) -> Bool -> IO Bool
    forall a b. (a -> b) -> a -> b
    $ Maybe Package
    v Maybe Package -> Maybe Package -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Maybe Package
    forall a. Maybe a
    Nothing Bool -> Bool -> Bool
    && Maybe Package
    v Maybe Package -> Maybe Package -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Package -> Maybe Package
    forall a. a -> Maybe a
    Just ""

    -- | Parses main.cf, and removes any initial configuration lines that are
    -- overridden to other values later in the file.
    --
    -- For example, to add some settings, removing any old settings:
    --
    -- > mainCf `File.containsLines`
    -- >	[ "# I like bars."
    -- >	, "foo = bar"
    -- >	] `onChange` dedupMainCf
    --
    -- Note that multiline configurations that continue onto the next line
    -- are not currently supported.
    dedupMainCf :: Property UnixLike
    dedupMainCf :: Property UnixLike
    dedupMainCf = Package -> ([Package] -> [Package]) -> Package -> Property UnixLike
    forall c.
    (FileContent c, Eq c) =>
    Package -> (c -> c) -> Package -> Property UnixLike
    File.fileProperty "postfix main.cf dedupped" [Package] -> [Package]
    dedupCf Package
    mainCfFile

    dedupCf :: [String] -> [String]
    dedupCf :: [Package] -> [Package]
    dedupCf ls :: [Package]
    ls =
    	let parsed :: [Either Package (Package, Package)]
    parsed = (Package -> Either Package (Package, Package))
    -> [Package] -> [Either Package (Package, Package)]
    forall a b. (a -> b) -> [a] -> [b]
    map Package -> Either Package (Package, Package)
    parse [Package]
    ls
    	in [Package]
    -> Map Package Integer
    -> [Either Package (Package, Package)]
    -> [Package]
    forall a.
    (Ord a, Num a) =>
    [Package]
    -> Map Package a
    -> [Either Package (Package, Package)]
    -> [Package]
    dedup [] ([(Package, Package)] -> Map Package Integer
    forall b. [(Package, b)] -> Map Package Integer
    keycounts ([(Package, Package)] -> Map Package Integer)
    -> [(Package, Package)] -> Map Package Integer
    forall a b. (a -> b) -> a -> b
    $ [Either Package (Package, Package)] -> [(Package, Package)]
    forall a b. [Either a b] -> [b]
    rights [Either Package (Package, Package)]
    parsed) [Either Package (Package, Package)]
    parsed
      where
    	parse :: Package -> Either Package (Package, Package)
    parse l :: Package
    l
    		| "#" Package -> Package -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    `isPrefixOf` Package
    l = Package -> Either Package (Package, Package)
    forall a b. a -> Either a b
    Left Package
    l
    		| "=" Package -> Package -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    `isInfixOf` Package
    l =
    			let (k :: Package
    k, v :: Package
    v) = (Char -> Bool) -> Package -> (Package, Package)
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    separate (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == '=') Package
    l
    			in (Package, Package) -> Either Package (Package, Package)
    forall a b. b -> Either a b
    Right (((Char -> Bool) -> Package -> Package
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Bool -> Bool
    not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Char -> Bool
    isSpace) Package
    k), Package
    v)
    		| Bool
    otherwise = Package -> Either Package (Package, Package)
    forall a b. a -> Either a b
    Left Package
    l
    	fmt :: Package -> Package -> Package
    fmt k :: Package
    k v :: Package
    v = Package
    k Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ " =" Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ Package
    v

    	keycounts :: [(Package, b)] -> Map Package Integer
    keycounts = (Integer -> Integer -> Integer)
    -> [(Package, Integer)] -> Map Package Integer
    forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
    M.fromListWith Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    (+) ([(Package, Integer)] -> Map Package Integer)
    -> ([(Package, b)] -> [(Package, Integer)])
    -> [(Package, b)]
    -> Map Package Integer
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ((Package, b) -> (Package, Integer))
    -> [(Package, b)] -> [(Package, Integer)]
    forall a b. (a -> b) -> [a] -> [b]
    map (\(k :: Package
    k, _v :: b
    _v) -> (Package
    k, (1 :: Integer)))

    	dedup :: [Package]
    -> Map Package a
    -> [Either Package (Package, Package)]
    -> [Package]
    dedup c :: [Package]
    c _ [] = [Package] -> [Package]
    forall a. [a] -> [a]
    reverse [Package]
    c
    	dedup c :: [Package]
    c kc :: Map Package a
    kc ((Left v :: Package
    v):rest :: [Either Package (Package, Package)]
    rest) = [Package]
    -> Map Package a
    -> [Either Package (Package, Package)]
    -> [Package]
    dedup (Package
    vPackage -> [Package] -> [Package]
    forall a. a -> [a] -> [a]
    :[Package]
    c) Map Package a
    kc [Either Package (Package, Package)]
    rest
    	dedup c :: [Package]
    c kc :: Map Package a
    kc ((Right (k :: Package
    k, v :: Package
    v)):rest :: [Either Package (Package, Package)]
    rest) = case Package -> Map Package a -> Maybe a
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup Package
    k Map Package a
    kc of
    		Just n :: a
    n | a
    n a -> a -> Bool
    forall a. Ord a => a -> a -> Bool
    > 1 -> [Package]
    -> Map Package a
    -> [Either Package (Package, Package)]
    -> [Package]
    dedup [Package]
    c (Package -> a -> Map Package a -> Map Package a
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    M.insert Package
    k (a
    n a -> a -> a
    forall a. Num a => a -> a -> a
    - 1) Map Package a
    kc) [Either Package (Package, Package)]
    rest
    		_ -> [Package]
    -> Map Package a
    -> [Either Package (Package, Package)]
    -> [Package]
    dedup (Package -> Package -> Package
    fmt Package
    k Package
    vPackage -> [Package] -> [Package]
    forall a. a -> [a] -> [a]
    :[Package]
    c) Map Package a
    kc [Either Package (Package, Package)]
    rest

    -- | The master config file for postfix.
    masterCfFile :: FilePath
    masterCfFile :: Package
    masterCfFile = "/etc/postfix/master.cf"

    -- | A service that can be present in the master config file.
    data Service = Service
    	{ Service -> ServiceType
    serviceType :: ServiceType
    	, Service -> Package
    serviceCommand :: String
    	, Service -> ServiceOpts
    serviceOpts :: ServiceOpts
    	}
    	deriving (Int -> Service -> Package -> Package
    [Service] -> Package -> Package
    Service -> Package
    (Int -> Service -> Package -> Package)
    -> (Service -> Package)
    -> ([Service] -> Package -> Package)
    -> Show Service
    forall a.
    (Int -> a -> Package -> Package)
    -> (a -> Package) -> ([a] -> Package -> Package) -> Show a
    showList :: [Service] -> Package -> Package
    $cshowList :: [Service] -> Package -> Package
    show :: Service -> Package
    $cshow :: Service -> Package
    showsPrec :: Int -> Service -> Package -> Package
    $cshowsPrec :: Int -> Service -> Package -> Package
    Show, Service -> Service -> Bool
    (Service -> Service -> Bool)
    -> (Service -> Service -> Bool) -> Eq Service
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    /= :: Service -> Service -> Bool
    $c/= :: Service -> Service -> Bool
    == :: Service -> Service -> Bool
    $c== :: Service -> Service -> Bool
    Eq)

    data ServiceType
    	= InetService (Maybe HostName) ServicePort
    	| UnixService FilePath PrivateService
    	| FifoService FilePath PrivateService
    	| PassService FilePath PrivateService
    	deriving (Int -> ServiceType -> Package -> Package
    [ServiceType] -> Package -> Package
    ServiceType -> Package
    (Int -> ServiceType -> Package -> Package)
    -> (ServiceType -> Package)
    -> ([ServiceType] -> Package -> Package)
    -> Show ServiceType
    forall a.
    (Int -> a -> Package -> Package)
    -> (a -> Package) -> ([a] -> Package -> Package) -> Show a
    showList :: [ServiceType] -> Package -> Package
    $cshowList :: [ServiceType] -> Package -> Package
    show :: ServiceType -> Package
    $cshow :: ServiceType -> Package
    showsPrec :: Int -> ServiceType -> Package -> Package
    $cshowsPrec :: Int -> ServiceType -> Package -> Package
    Show, ServiceType -> ServiceType -> Bool
    (ServiceType -> ServiceType -> Bool)
    -> (ServiceType -> ServiceType -> Bool) -> Eq ServiceType
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    /= :: ServiceType -> ServiceType -> Bool
    $c/= :: ServiceType -> ServiceType -> Bool
    == :: ServiceType -> ServiceType -> Bool
    $c== :: ServiceType -> ServiceType -> Bool
    Eq)

    -- Can be a port number or service name such as "smtp".
    type ServicePort = String

    type PrivateService = Bool

    -- | Options for a service.
    data ServiceOpts = ServiceOpts
    	{ ServiceOpts -> Maybe Bool
    serviceUnprivileged :: Maybe Bool
    	, ServiceOpts -> Maybe Bool
    serviceChroot :: Maybe Bool
    	, ServiceOpts -> Maybe Int
    serviceWakeupTime :: Maybe Int
    	, ServiceOpts -> Maybe Int
    serviceProcessLimit :: Maybe Int
    	}
    	deriving (Int -> ServiceOpts -> Package -> Package
    [ServiceOpts] -> Package -> Package
    ServiceOpts -> Package
    (Int -> ServiceOpts -> Package -> Package)
    -> (ServiceOpts -> Package)
    -> ([ServiceOpts] -> Package -> Package)
    -> Show ServiceOpts
    forall a.
    (Int -> a -> Package -> Package)
    -> (a -> Package) -> ([a] -> Package -> Package) -> Show a
    showList :: [ServiceOpts] -> Package -> Package
    $cshowList :: [ServiceOpts] -> Package -> Package
    show :: ServiceOpts -> Package
    $cshow :: ServiceOpts -> Package
    showsPrec :: Int -> ServiceOpts -> Package -> Package
    $cshowsPrec :: Int -> ServiceOpts -> Package -> Package
    Show, ServiceOpts -> ServiceOpts -> Bool
    (ServiceOpts -> ServiceOpts -> Bool)
    -> (ServiceOpts -> ServiceOpts -> Bool) -> Eq ServiceOpts
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    /= :: ServiceOpts -> ServiceOpts -> Bool
    $c/= :: ServiceOpts -> ServiceOpts -> Bool
    == :: ServiceOpts -> ServiceOpts -> Bool
    $c== :: ServiceOpts -> ServiceOpts -> Bool
    Eq)

    defServiceOpts :: ServiceOpts
    defServiceOpts :: ServiceOpts
    defServiceOpts = ServiceOpts :: Maybe Bool -> Maybe Bool -> Maybe Int -> Maybe Int -> ServiceOpts
    ServiceOpts
    	{ serviceUnprivileged :: Maybe Bool
    serviceUnprivileged = Maybe Bool
    forall a. Maybe a
    Nothing
    	, serviceChroot :: Maybe Bool
    serviceChroot = Maybe Bool
    forall a. Maybe a
    Nothing
    	, serviceWakeupTime :: Maybe Int
    serviceWakeupTime = Maybe Int
    forall a. Maybe a
    Nothing
    	, serviceProcessLimit :: Maybe Int
    serviceProcessLimit = Maybe Int
    forall a. Maybe a
    Nothing
    	}

    formatServiceLine :: Service -> File.Line
    formatServiceLine :: Service -> Package
    formatServiceLine s :: Service
    s = [Package] -> Package
    unwords ([Package] -> Package) -> [Package] -> Package
    forall a b. (a -> b) -> a -> b
    $ ((Int, Package) -> Package) -> [(Int, Package)] -> [Package]
    forall a b. (a -> b) -> [a] -> [b]
    map (Int, Package) -> Package
    pad
    	[ (10, case Service -> ServiceType
    serviceType Service
    s of
    		InetService (Just h :: Package
    h) p :: Package
    p -> Package
    h Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ ":" Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ Package
    p
    		InetService Nothing p :: Package
    p -> Package
    p
    		UnixService f :: Package
    f _ -> Package
    f
    		FifoService f :: Package
    f _ -> Package
    f
    		PassService f :: Package
    f _ -> Package
    f)
    	, (6, case Service -> ServiceType
    serviceType Service
    s of
    		InetService _ _ -> "inet"
    		UnixService _ _ -> "unix"
    		FifoService _ _ -> "fifo"
    		PassService _ _ -> "pass")
    	, (8, case Service -> ServiceType
    serviceType Service
    s of
    		InetService _ _ -> Bool -> Package
    bool Bool
    False
    		UnixService _ b :: Bool
    b -> Bool -> Package
    bool Bool
    b
    		FifoService _ b :: Bool
    b -> Bool -> Package
    bool Bool
    b
    		PassService _ b :: Bool
    b -> Bool -> Package
    bool Bool
    b)
    	, (8, (Bool -> Package) -> (ServiceOpts -> Maybe Bool) -> Package
    forall a. (a -> Package) -> (ServiceOpts -> Maybe a) -> Package
    v Bool -> Package
    bool ServiceOpts -> Maybe Bool
    serviceUnprivileged)
    	, (8, (Bool -> Package) -> (ServiceOpts -> Maybe Bool) -> Package
    forall a. (a -> Package) -> (ServiceOpts -> Maybe a) -> Package
    v Bool -> Package
    bool ServiceOpts -> Maybe Bool
    serviceChroot)
    	, (8, (Int -> Package) -> (ServiceOpts -> Maybe Int) -> Package
    forall a. (a -> Package) -> (ServiceOpts -> Maybe a) -> Package
    v Int -> Package
    forall a. Show a => a -> Package
    show ServiceOpts -> Maybe Int
    serviceWakeupTime)
    	, (8, (Int -> Package) -> (ServiceOpts -> Maybe Int) -> Package
    forall a. (a -> Package) -> (ServiceOpts -> Maybe a) -> Package
    v Int -> Package
    forall a. Show a => a -> Package
    show ServiceOpts -> Maybe Int
    serviceProcessLimit)
    	, (0, Service -> Package
    serviceCommand Service
    s)
    	]
      where
    	v :: (a -> Package) -> (ServiceOpts -> Maybe a) -> Package
    v f :: a -> Package
    f sel :: ServiceOpts -> Maybe a
    sel = Package -> (a -> Package) -> Maybe a -> Package
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe "-" a -> Package
    f (ServiceOpts -> Maybe a
    sel (Service -> ServiceOpts
    serviceOpts Service
    s))
    	bool :: Bool -> Package
    bool True = "y"
    	bool False = "n"
    	pad :: (Int, Package) -> Package
    pad (n :: Int
    n, t :: Package
    t) = Package
    t Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ Int -> Char -> Package
    forall a. Int -> a -> [a]
    replicate (Int
    n Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - 1 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Package -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length Package
    t) ' '

    -- | Note that this does not handle multi-line service entries,
    -- in which subsequent lines are indented. `serviceLine` does not generate
    -- such entries.
    parseServiceLine :: File.Line -> Maybe Service
    parseServiceLine :: Package -> Maybe Service
    parseServiceLine ('#':_) = Maybe Service
    forall a. Maybe a
    Nothing
    parseServiceLine (' ':_) = Maybe Service
    forall a. Maybe a
    Nothing -- continuation of multiline entry
    parseServiceLine l :: Package
    l = ServiceType -> Package -> ServiceOpts -> Service
    Service
    	(ServiceType -> Package -> ServiceOpts -> Service)
    -> Maybe ServiceType -> Maybe (Package -> ServiceOpts -> Service)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe ServiceType
    parsetype
    	Maybe (Package -> ServiceOpts -> Service)
    -> Maybe Package -> Maybe (ServiceOpts -> Service)
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> Maybe Package
    parsecommand
    	Maybe (ServiceOpts -> Service)
    -> Maybe ServiceOpts -> Maybe Service
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> Maybe ServiceOpts
    parseopts
      where
    	parsetype :: Maybe ServiceType
    parsetype = do
    		Package
    t <- Int -> Maybe Package
    getword 2
    		case Package
    t of
    			"inet" -> do
    				Package
    v <- Int -> Maybe Package
    getword 1
    				let (h :: Package
    h,p :: Package
    p) = (Char -> Bool) -> Package -> (Package, Package)
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    separate (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == ':') Package
    v
    				if Package -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null Package
    p
    					then Maybe ServiceType
    forall a. Maybe a
    Nothing
    					else ServiceType -> Maybe ServiceType
    forall a. a -> Maybe a
    Just (ServiceType -> Maybe ServiceType)
    -> ServiceType -> Maybe ServiceType
    forall a b. (a -> b) -> a -> b
    $ Maybe Package -> Package -> ServiceType
    InetService
    						(if Package -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null Package
    h then Maybe Package
    forall a. Maybe a
    Nothing else Package -> Maybe Package
    forall a. a -> Maybe a
    Just Package
    h) Package
    p
    			"unix" -> Package -> Bool -> ServiceType
    UnixService (Package -> Bool -> ServiceType)
    -> Maybe Package -> Maybe (Bool -> ServiceType)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Int -> Maybe Package
    getword 1 Maybe (Bool -> ServiceType) -> Maybe Bool -> Maybe ServiceType
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> Maybe Bool
    parseprivate
    			"fifo" -> Package -> Bool -> ServiceType
    FifoService (Package -> Bool -> ServiceType)
    -> Maybe Package -> Maybe (Bool -> ServiceType)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Int -> Maybe Package
    getword 1 Maybe (Bool -> ServiceType) -> Maybe Bool -> Maybe ServiceType
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> Maybe Bool
    parseprivate
    			"pass" -> Package -> Bool -> ServiceType
    PassService (Package -> Bool -> ServiceType)
    -> Maybe Package -> Maybe (Bool -> ServiceType)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Int -> Maybe Package
    getword 1 Maybe (Bool -> ServiceType) -> Maybe Bool -> Maybe ServiceType
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> Maybe Bool
    parseprivate
    			_ -> Maybe ServiceType
    forall a. Maybe a
    Nothing
    	parseprivate :: Maybe Bool
    parseprivate = Maybe (Maybe Bool) -> Maybe Bool
    forall (m :: * -> *) a. Monad m => m (m a) -> m a
    join (Maybe (Maybe Bool) -> Maybe Bool)
    -> (Package -> Maybe (Maybe Bool)) -> Package -> Maybe Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Package -> Maybe (Maybe Bool)
    bool (Package -> Maybe Bool) -> Maybe Package -> Maybe Bool
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< Int -> Maybe Package
    getword 3

    	parsecommand :: Maybe Package
    parsecommand = case [Package] -> Package
    unwords (Int -> [Package] -> [Package]
    forall a. Int -> [a] -> [a]
    drop 7 [Package]
    ws) of
    		"" -> Maybe Package
    forall a. Maybe a
    Nothing
    		s :: Package
    s -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just Package
    s

    	parseopts :: Maybe ServiceOpts
    parseopts = Maybe Bool -> Maybe Bool -> Maybe Int -> Maybe Int -> ServiceOpts
    ServiceOpts
    		(Maybe Bool -> Maybe Bool -> Maybe Int -> Maybe Int -> ServiceOpts)
    -> Maybe (Maybe Bool)
    -> Maybe (Maybe Bool -> Maybe Int -> Maybe Int -> ServiceOpts)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> (Package -> Maybe (Maybe Bool)
    bool (Package -> Maybe (Maybe Bool))
    -> Maybe Package -> Maybe (Maybe Bool)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< Int -> Maybe Package
    getword 4)
    		Maybe (Maybe Bool -> Maybe Int -> Maybe Int -> ServiceOpts)
    -> Maybe (Maybe Bool)
    -> Maybe (Maybe Int -> Maybe Int -> ServiceOpts)
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> (Package -> Maybe (Maybe Bool)
    bool (Package -> Maybe (Maybe Bool))
    -> Maybe Package -> Maybe (Maybe Bool)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< Int -> Maybe Package
    getword 5)
    		Maybe (Maybe Int -> Maybe Int -> ServiceOpts)
    -> Maybe (Maybe Int) -> Maybe (Maybe Int -> ServiceOpts)
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> (Package -> Maybe (Maybe Int)
    forall a. Read a => Package -> Maybe (Maybe a)
    int (Package -> Maybe (Maybe Int))
    -> Maybe Package -> Maybe (Maybe Int)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< Int -> Maybe Package
    getword 6)
    		Maybe (Maybe Int -> ServiceOpts)
    -> Maybe (Maybe Int) -> Maybe ServiceOpts
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> (Package -> Maybe (Maybe Int)
    forall a. Read a => Package -> Maybe (Maybe a)
    int (Package -> Maybe (Maybe Int))
    -> Maybe Package -> Maybe (Maybe Int)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< Int -> Maybe Package
    getword 7)

    	bool :: Package -> Maybe (Maybe Bool)
    bool "-" = Maybe Bool -> Maybe (Maybe Bool)
    forall a. a -> Maybe a
    Just Maybe Bool
    forall a. Maybe a
    Nothing
    	bool "y" = Maybe Bool -> Maybe (Maybe Bool)
    forall a. a -> Maybe a
    Just (Bool -> Maybe Bool
    forall a. a -> Maybe a
    Just Bool
    True)
    	bool "n" = Maybe Bool -> Maybe (Maybe Bool)
    forall a. a -> Maybe a
    Just (Bool -> Maybe Bool
    forall a. a -> Maybe a
    Just Bool
    False)
    	bool _ = Maybe (Maybe Bool)
    forall a. Maybe a
    Nothing

    	int :: Package -> Maybe (Maybe a)
    int "-" = Maybe a -> Maybe (Maybe a)
    forall a. a -> Maybe a
    Just Maybe a
    forall a. Maybe a
    Nothing
    	int n :: Package
    n = Maybe (Maybe a)
    -> (a -> Maybe (Maybe a)) -> Maybe a -> Maybe (Maybe a)
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe Maybe (Maybe a)
    forall a. Maybe a
    Nothing (Maybe a -> Maybe (Maybe a)
    forall a. a -> Maybe a
    Just (Maybe a -> Maybe (Maybe a))
    -> (a -> Maybe a) -> a -> Maybe (Maybe a)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . a -> Maybe a
    forall a. a -> Maybe a
    Just) (Package -> Maybe a
    forall a. Read a => Package -> Maybe a
    readish Package
    n)

    	getword :: Int -> Maybe Package
    getword n :: Int
    n
    		| Int
    nws Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int
    n = Package -> Maybe Package
    forall a. a -> Maybe a
    Just ([Package]
    ws [Package] -> Int -> Package
    forall a. [a] -> Int -> a
    !! (Int
    n Int -> Int -> Int
    forall a. Num a => a -> a -> a
    -1))
    		| Bool
    otherwise = Maybe Package
    forall a. Maybe a
    Nothing
    	ws :: [Package]
    ws = Package -> [Package]
    words Package
    l
    	nws :: Int
    nws = [Package] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [Package]
    ws

    -- | Enables a `Service` in postfix's `masterCfFile`.
    service :: Service -> RevertableProperty DebianLike DebianLike
    service :: Service -> RevertableProperty DebianLike DebianLike
    service s :: Service
    s = (Property DebianLike
    enable Property DebianLike
    -> Property DebianLike -> RevertableProperty DebianLike DebianLike
    forall setupmetatypes undometatypes.
    Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    <!> Property DebianLike
    disable)
    	RevertableProperty DebianLike DebianLike
    -> Package -> RevertableProperty DebianLike DebianLike
    forall p. IsProp p => p -> Package -> p
    `describe` Package
    desc
      where
    	desc :: Package
    desc = "enabled postfix service " Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ ServiceType -> Package
    forall a. Show a => a -> Package
    show (Service -> ServiceType
    serviceType Service
    s)
    	enable :: CombinedType (Property UnixLike) (Property DebianLike)
    enable = Package
    masterCfFile Package -> Package -> Property UnixLike
    `File.containsLine` (Service -> Package
    formatServiceLine Service
    s)
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    reloaded
    	disable :: CombinedType (Property UnixLike) (Property DebianLike)
    disable = Package -> ([Package] -> [Package]) -> Package -> Property UnixLike
    forall c.
    (FileContent c, Eq c) =>
    Package -> (c -> c) -> Package -> Property UnixLike
    File.fileProperty Package
    desc ((Package -> Bool) -> [Package] -> [Package]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Bool -> Bool
    not (Bool -> Bool) -> (Package -> Bool) -> Package -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Package -> Bool
    matches)) Package
    masterCfFile
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    reloaded
    	matches :: Package -> Bool
    matches l :: Package
    l = case Package -> Maybe Service
    parseServiceLine Package
    l of
    		Just s' :: Service
    s' | Service
    s' Service -> Service -> Bool
    forall a. Eq a => a -> a -> Bool
    == Service
    s -> Bool
    True
    		_ -> Bool
    False

    -- | Installs saslauthd and configures it for postfix, authenticating
    -- against PAM.
    --
    -- Does not configure postfix to use it; eg @smtpd_sasl_auth_enable = yes@
    -- needs to be set to enable use. See
    -- <https://wiki.debian.org/PostfixAndSASL>.
    --
    -- Password brute force attacks are possible when SASL auth is enabled.
    -- It would be wise to enable fail2ban, for example:
    --
    -- > Fail2Ban.jailEnabled "postfix-sasl"
    saslAuthdInstalled :: Property DebianLike
    saslAuthdInstalled :: Property DebianLike
    saslAuthdInstalled = Property DebianLike
    setupdaemon
    	Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Package -> Property DebianLike
    Service.running "saslauthd"
    	Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    postfixgroup
    	Property DebianLike
    -> Property UnixLike
    -> CombinedType (Property DebianLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property UnixLike
    dirperm
    	Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` [Package] -> Property DebianLike
    Apt.installed ["sasl2-bin"]
    	Property DebianLike
    -> Property UnixLike
    -> CombinedType (Property DebianLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property UnixLike
    smtpdconf
      where
    	setupdaemon :: CombinedType (Property UnixLike) (Property DebianLike)
    setupdaemon = "/etc/default/saslauthd" Package -> [Package] -> Property UnixLike
    `File.containsLines`
    		[ "START=yes"
    		, "OPTIONS=\"-c -m " Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ Package
    dir Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ "\""
    		]
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Package -> Property DebianLike
    Service.restarted "saslauthd"
    	smtpdconf :: Property UnixLike
    smtpdconf = "/etc/postfix/sasl/smtpd.conf" Package -> [Package] -> Property UnixLike
    `File.containsLines`
    		[ "pwcheck_method: saslauthd"
    		, "mech_list: PLAIN LOGIN"
    		]
    	dirperm :: Property UnixLike
    dirperm = IO Bool -> UncheckedProperty UnixLike -> Property UnixLike
    forall (p :: * -> *) i (m :: * -> *).
    (Checkable p i, LiftPropellor m) =>
    m Bool -> p i -> Property i
    check (Bool -> Bool
    not (Bool -> Bool) -> IO Bool -> IO Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Package -> IO Bool
    doesDirectoryExist Package
    dir) (UncheckedProperty UnixLike -> Property UnixLike)
    -> UncheckedProperty UnixLike -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $
    		Package -> [Package] -> UncheckedProperty UnixLike
    cmdProperty "dpkg-statoverride"
    			[ "--add", "root", "sasl", "710", Package
    dir ]
    	postfixgroup :: CombinedType (Property DebianLike) (Property DebianLike)
    postfixgroup = (Package -> User
    User "postfix") User -> Group -> Property DebianLike
    `User.hasGroup` (Package -> Group
    Group "sasl")
    		Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    restarted
    	dir :: Package
    dir = "/var/spool/postfix/var/run/saslauthd"

    -- | Uses `saslpasswd2` to set the password for a user in the sasldb2 file.
    --
    -- The password is taken from the privdata.
    saslPasswdSet :: Domain -> User -> Property (HasInfo + UnixLike)
    saslPasswdSet :: Package -> User -> Property (HasInfo + UnixLike)
    saslPasswdSet domain :: Package
    domain (User user :: Package
    user) = Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    go Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Package
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Package -> Property i
    `changesFileContent` "/etc/sasldb2"
      where
    	go :: Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    go = PrivDataSource
    -> Context
    -> (((PrivData -> Propellor Result) -> Propellor Result)
        -> Property
             (MetaTypes
                '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                   'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall c s metatypes.
    (IsContext c, IsPrivDataSource s,
     IncludesInfo metatypes ~ 'True) =>
    s
    -> c
    -> (((PrivData -> Propellor Result) -> Propellor Result)
        -> Property metatypes)
    -> Property metatypes
    withPrivData PrivDataSource
    src Context
    ctx ((((PrivData -> Propellor Result) -> Propellor Result)
      -> Property
           (MetaTypes
              '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                 'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
     -> Property
          (MetaTypes
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> (((PrivData -> Propellor Result) -> Propellor Result)
        -> Property
             (MetaTypes
                '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                   'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall a b. (a -> b) -> a -> b
    $ \getpw :: (PrivData -> Propellor Result) -> Propellor Result
    getpw ->
    		Package
    -> Propellor Result
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall k (metatypes :: k).
    SingI metatypes =>
    Package -> Propellor Result -> Property (MetaTypes metatypes)
    property Package
    desc (Propellor Result
     -> Property
          (MetaTypes
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> Propellor Result
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall a b. (a -> b) -> a -> b
    $ (PrivData -> Propellor Result) -> Propellor Result
    getpw ((PrivData -> Propellor Result) -> Propellor Result)
    -> (PrivData -> Propellor Result) -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ \pw :: PrivData
    pw -> IO Result -> Propellor Result
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Result -> Propellor Result) -> IO Result -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $
    			StdHandle
    -> CreateProcessRunner
    -> CreateProcess
    -> (Handle -> IO Result)
    -> IO Result
    forall a.
    StdHandle
    -> CreateProcessRunner -> CreateProcess -> (Handle -> IO a) -> IO a
    withHandle StdHandle
    StdinHandle CreateProcessRunner
    createProcessSuccess CreateProcess
    p ((Handle -> IO Result) -> IO Result)
    -> (Handle -> IO Result) -> IO Result
    forall a b. (a -> b) -> a -> b
    $ \h :: Handle
    h -> do
    				Handle -> Package -> IO ()
    hPutStrLn Handle
    h (PrivData -> Package
    privDataVal PrivData
    pw)
    				Handle -> IO ()
    hClose Handle
    h
    				Result -> IO Result
    forall (m :: * -> *) a. Monad m => a -> m a
    return Result
    NoChange
    	desc :: Package
    desc = "sasl password for " Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ Package
    uatd
    	uatd :: Package
    uatd = Package
    user Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ "@" Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ Package
    domain
    	ps :: [Package]
    ps = ["-p", "-c", "-u", Package
    domain, Package
    user]
    	p :: CreateProcess
    p = Package -> [Package] -> CreateProcess
    proc "saslpasswd2" [Package]
    ps
    	ctx :: Context
    ctx = Package -> Context
    Context "sasl"
    	src :: PrivDataSource
    src = PrivDataField -> Package -> PrivDataSource
    PrivDataSource (Package -> PrivDataField
    Password Package
    uatd) "enter password"