summaryrefslogtreecommitdiff
path: root/src/Propellor/Property/Postfix.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Propellor/Property/Postfix.hs')
-rw-r--r--src/Propellor/Property/Postfix.hs136
1 files changed, 136 insertions, 0 deletions
diff --git a/src/Propellor/Property/Postfix.hs b/src/Propellor/Property/Postfix.hs
index 1c8684c7..df244061 100644
--- a/src/Propellor/Property/Postfix.hs
+++ b/src/Propellor/Property/Postfix.hs
@@ -129,6 +129,142 @@ dedupCf ls =
Just n | n > 1 -> dedup c (M.insert k (n - 1) kc) rest
_ -> dedup (fmt k v:c) kc rest
+-- | The master config file for postfix.
+masterCfFile :: FilePath
+masterCfFile = "/etc/postfix/master.cf"
+
+-- | A service that can be present in the master config file.
+data Service = Service
+ { serviceType :: ServiceType
+ , serviceCommand :: String
+ , serviceOpts :: ServiceOpts
+ }
+ deriving (Show, Eq)
+
+data ServiceType
+ = InetService (Maybe HostName) ServicePort
+ | UnixService FilePath PrivateService
+ | FifoService FilePath PrivateService
+ | PassService FilePath PrivateService
+ deriving (Show, 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
+ { serviceUnprivileged :: Maybe Bool
+ , serviceChroot :: Maybe Bool
+ , serviceWakeupTime :: Maybe Int
+ , serviceProcessLimit :: Maybe Int
+ }
+ deriving (Show, Eq)
+
+defServiceOpts :: ServiceOpts
+defServiceOpts = ServiceOpts
+ { serviceUnprivileged = Nothing
+ , serviceChroot = Nothing
+ , serviceWakeupTime = Nothing
+ , serviceProcessLimit = Nothing
+ }
+
+formatServiceLine :: Service -> File.Line
+formatServiceLine s = unwords $ map pad
+ [ (10, case serviceType s of
+ InetService (Just h) p -> h ++ ":" ++ p
+ InetService Nothing p -> p
+ UnixService f _ -> f
+ FifoService f _ -> f
+ PassService f _ -> f)
+ , (6, case serviceType s of
+ InetService _ _ -> "inet"
+ UnixService _ _ -> "unix"
+ FifoService _ _ -> "fifo"
+ PassService _ _ -> "pass")
+ , (8, case serviceType s of
+ InetService _ _ -> bool False
+ UnixService _ b -> bool b
+ FifoService _ b -> bool b
+ PassService _ b -> bool b)
+ , (8, v bool serviceUnprivileged)
+ , (8, v bool serviceChroot)
+ , (8, v show serviceWakeupTime)
+ , (8, v show serviceProcessLimit)
+ , (0, serviceCommand s)
+ ]
+ where
+ v f sel = maybe "-" f (sel (serviceOpts s))
+ bool True = "y"
+ bool False = "n"
+ pad (n, t) = t ++ replicate (n - 1 - length 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 ('#':_) = Nothing
+parseServiceLine (' ':_) = Nothing -- continuation of multiline entry
+parseServiceLine l = Service
+ <$> parsetype
+ <*> parsecommand
+ <*> parseopts
+ where
+ parsetype = do
+ t <- getword 2
+ case t of
+ "inet" -> do
+ v <- getword 1
+ let (h,p) = separate (== ':') v
+ if null p
+ then Nothing
+ else Just $ InetService
+ (if null h then Nothing else Just h) p
+ "unix" -> UnixService <$> getword 1 <*> parseprivate
+ "fifo" -> FifoService <$> getword 1 <*> parseprivate
+ "pass" -> PassService <$> getword 1 <*> parseprivate
+ _ -> Nothing
+ parseprivate = join . bool =<< getword 3
+
+ parsecommand = case unwords (drop 7 ws) of
+ "" -> Nothing
+ s -> Just s
+
+ parseopts = ServiceOpts
+ <$> (bool =<< getword 4)
+ <*> (bool =<< getword 5)
+ <*> (int =<< getword 6)
+ <*> (int =<< getword 7)
+
+ bool "-" = Just Nothing
+ bool "y" = Just (Just True)
+ bool "n" = Just (Just False)
+ bool _ = Nothing
+
+ int "-" = Just Nothing
+ int n = maybe Nothing (Just . Just) (readish n)
+
+ getword n
+ | nws >= n = Just (ws !! (n -1))
+ | otherwise = Nothing
+ ws = words l
+ nws = length ws
+
+-- | Enables a `Service` in postfix's `masterCfFile`.
+service :: Service -> RevertableProperty NoInfo
+service s = (enable <!> disable)
+ `describe` desc
+ where
+ desc = "enabled postfix service " ++ show (serviceType s)
+ enable = masterCfFile `File.containsLine` (formatServiceLine s)
+ `onChange` reloaded
+ disable = File.fileProperty desc (filter (not . matches)) masterCfFile
+ `onChange` reloaded
+ matches l = case parseServiceLine l of
+ Just s' | s' == s -> True
+ _ -> False
+
-- | Installs saslauthd and configures it for postfix, authenticating
-- against PAM.
--