{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE BangPatterns #-} {- | Module : Main Portability : unportable Probably not at all portable. Makes use of functions from the @unistd.h@ header file. TODO: Tidy this up, shift some of the logic out to another module. -} module Main where import Control.Arrow (left) import Control.Exception (bracket, throw, assert) import Control.Monad (when) import Control.Monad.Error.Class (Error(..), MonadError(..) ) import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Maybe (MaybeT(..)) import Data.ConfigFile (emptyCP, readfile , optionxform, get ) import Data.Either.Utils (eitherToMonadError) import Data.Monoid import Data.Maybe (fromJust) import Data.Time.Clock.POSIX (getPOSIXTime) import Foreign.C.Types (CInt(..)) import Foreign.C.Error (throwErrnoIfMinus1_) import Network.BSD (getHostName) import System.Directory (setCurrentDirectory, makeAbsolute) import System.Environment (withArgs) import System.IO (Handle, hClose, stderr, hPutStrLn, hPutStr) import System.IO.Error (modifyIOError) import qualified System.Posix.Files as F import System.Posix.IO (exclusive, defaultFileFlags, openFd , OpenMode(..), fdToHandle ) import System.Posix.User (getUserEntryForName, userID, userGroupID ) import System.Posix.Types (CGid(..), CUid(..), UserID , GroupID ) import System.Random (getStdRandom, randomR) import ConfigLocation (configFileLocn) import CmdArgs (AttCmdArgs(..), withCmdArgs) import EmailAddress (EmailAddress(..), validateFromString) import qualified DeliveryHeaders as DH import DeliveryHeaders (Addr(..)) -- * C functions and wrappers around them -- | unistd funcs: -- -- @ -- #include -- -- int setresuid(uid_t ruid, uid_t euid, uid_t suid); -- int setresgid(gid_t rgid, gid_t egid, gid_t sgid); -- @ foreign import ccall "setresgid" setresgid_c :: CGid -> CGid -> CGid -> IO CInt foreign import ccall "setresuid" setresuid_c :: CUid -> CUid -> CUid -> IO CInt -- | wrapper around C func @setresgid@. setResGid :: CGid -> CGid -> CGid -> IO () setResGid r e s = throwErrnoIfMinus1_ "setResGid" $ setresgid_c r e s -- | wrapper around C func @setresuid@. setResUid :: CUid -> CUid -> CUid -> IO () setResUid r e s = throwErrnoIfMinus1_ "setResUid" $ setresuid_c r e s -- * low-level, Posix-specific functions -- | opens w/ mode 0644, but gives error if exists -- -- i.e. @-rw-r--r--@ openIfNExist :: String -> IO Handle openIfNExist !filePath = do let mode = foldl F.unionFileModes F.nullFileMode [F.ownerReadMode, F.ownerWriteMode, F.groupReadMode, F.otherReadMode] openFileFlags = defaultFileFlags { exclusive = True } !fd <- openFd filePath WriteOnly (Just mode) openFileFlags !hdl <- fdToHandle fd return hdl -- | opens and closes a file created with openIfNExist, -- and executes the action f on it in between. -- withMailFile :: String -> (Handle -> IO a) -> IO a withMailFile !filePath !f = do let open = openIfNExist filePath let close = hClose bracket open close f -- | get the uid and gid for a username getUserIDs :: String -> IO (UserID, GroupID) getUserIDs userName = do userEntry <- getUserEntryForName userName let uid = userID userEntry gid = userGroupID userEntry return (uid, gid) -- | Return an allegedly unique filename; useful to add new mail files in a maildir. Name is of format