summaryrefslogtreecommitdiff
path: root/lib/Postdelay
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Postdelay')
-rw-r--r--lib/Postdelay/Queue.hs108
-rw-r--r--lib/Postdelay/Scan.hs1
-rw-r--r--lib/Postdelay/TimeSpec.hs2
3 files changed, 109 insertions, 2 deletions
diff --git a/lib/Postdelay/Queue.hs b/lib/Postdelay/Queue.hs
new file mode 100644
index 0000000..ac760ec
--- /dev/null
+++ b/lib/Postdelay/Queue.hs
@@ -0,0 +1,108 @@
1{-# LANGUAGE RankNTypes, RecordWildCards, ApplicativeDo, FlexibleContexts #-}
2
3module Postdelay.Queue
4 ( sendmail
5 , StoredMailF(..)
6 , StoredMail, StoredMail'
7 , sendStored, sendStored'
8 , StoreConfig, storeConfig
9 , pushStore, popStore
10 ) where
11
12import Postdelay.Types
13
14import Control.Monad.IO.Class (MonadIO(..))
15import Control.Monad.Catch (handle, MonadThrow(throwM))
16import Control.Monad.Trans.Maybe
17import Control.Monad.Reader
18import Control.Monad.Trans.Class
19import Control.Monad
20
21import System.IO (hClose, stderr)
22import System.IO.Error
23import GHC.IO.Exception
24import System.Process
25
26import Data.ByteString.Lazy as Lazy (ByteString)
27import qualified Data.ByteString.Lazy as Lazy.BS
28
29import Foreign.C.Error (Errno(..), ePIPE)
30
31import Data.Bool
32import Data.Conduit
33import Data.Time
34
35import qualified Options.Applicative as Opt (Parser)
36import Options.Applicative hiding (Parser)
37
38
39data StoredMailF a = StoredMail
40 { sendDelay :: Delay
41 , sender :: String
42 , recipients :: [String]
43 , message :: a
44 }
45
46type StoredMail = StoredMailF Lazy.ByteString
47type StoredMail' = StoredMailF (IO Lazy.ByteString)
48
49data StoreConfig = StoreConfig
50 { storeLocation :: FilePath
51 }
52
53
54storeConfig :: Opt.Parser StoreConfig
55storeConfig = do
56 storeLocation <- strOption $ mconcat
57 [ long "spool"
58 , short 's'
59 , metavar "PATH"
60 , help "Where to expect the mail spool"
61 ]
62 pure StoreConfig{..}
63
64
65sendmail' :: MonadIO m
66 => [String] -- ^ Arguments to sendmail
67 -> Lazy.ByteString -- ^ Message
68 -> m ()
69sendmail' args mail = liftIO . withCreateProcess cp $ \(Just inh) _ _ pHandle -> do
70 ignoreSigPipe $ do
71 Lazy.BS.hPutStr inh mail
72 hClose inh
73
74 ret <- waitForProcess pHandle
75 bool throwM (\_ -> return ()) (ret == ExitSuccess) $ ret
76 where
77 cp = (proc "sendmail" args)
78 { std_in = CreatePipe
79 }
80 ignoreSigPipe = handle $ \e -> case e of
81 IOError { ioe_type = ResourceVanished
82 , ioe_errno = Just ioe }
83 | Errno ioe == ePIPE -> return ()
84 _ -> throwM e
85
86sendmail :: MonadIO m
87 => String -- ^ Sender
88 -> [String] -- ^ Recipients
89 -> Lazy.ByteString -- ^ Message
90 -> m ()
91sendmail sender recipients = sendmail' $ ["-G", "-i", "-f", sender, "--"] ++ recipients
92
93sendStored :: MonadIO m => StoredMail -> m ()
94sendStored StoredMail{ sendDelay = Until{..}, ..} = void . runMaybeT $ do
95 now <- liftIO getCurrentTime
96 guard $ now >= releaseTime
97 lift $ sendmail sender recipients message
98
99sendStored' :: MonadIO m => StoredMail' -> m ()
100sendStored' StoredMail{..} = do
101 message' <- liftIO message
102 sendStored StoredMail{ message = message', .. }
103
104popStore :: (MonadIO m, MonadReader StoreConfig m) => Source m StoredMail'
105popStore = undefined
106
107pushStore :: (MonadIO m, MonadReader StoreConfig m) => Source m StoredMail -> m ()
108pushStore = undefined
diff --git a/lib/Postdelay/Scan.hs b/lib/Postdelay/Scan.hs
index 7f80818..6355928 100644
--- a/lib/Postdelay/Scan.hs
+++ b/lib/Postdelay/Scan.hs
@@ -3,7 +3,6 @@
3module Postdelay.Scan 3module Postdelay.Scan
4 ( scan 4 ( scan
5 , Delay(..) 5 , Delay(..)
6 , ParseError(..)
7 ) where 6 ) where
8 7
9import Postdelay.Types 8import Postdelay.Types
diff --git a/lib/Postdelay/TimeSpec.hs b/lib/Postdelay/TimeSpec.hs
index 951b1c0..f61d84e 100644
--- a/lib/Postdelay/TimeSpec.hs
+++ b/lib/Postdelay/TimeSpec.hs
@@ -124,7 +124,7 @@ dateSpec = label "Date" $ (>> scribe (prio' Default . time) midnight) <$> choice
124 scribe (prio' Assign . date.days) 1 124 scribe (prio' Assign . date.days) 1
125 ] 125 ]
126 , try $ do 126 , try $ do
127 let daySuffix = optional $ choice [ string' "st", string' "nd", string' "rd", string' "th" ] 127 let daySuffix = optional $ choice [ string ".", string' "st", string' "nd", string' "rd", string' "th" ]
128 (m, d) <- choice [ try $ (,) <$> monthName <* spaces <*> dayNumber False <* daySuffix 128 (m, d) <- choice [ try $ (,) <$> monthName <* spaces <*> dayNumber False <* daySuffix
129 , try $ flip (,) <$> dayNumber False <* daySuffix <* spaces <*> monthName 129 , try $ flip (,) <$> dayNumber False <* daySuffix <* spaces <*> monthName
130 ] 130 ]