]> gitweb.michael.orlitzky.com - dead/halcyon.git/blob - src/Mail.hs
Fix remaining hlint suggestions.
[dead/halcyon.git] / src / Mail.hs
1 -- |Email functions and data types.
2
3 module Mail
4 where
5
6 import Control.Concurrent
7 import Control.Exception (evaluate)
8 import Control.Monad (liftM)
9 import Data.List (intercalate)
10 import Data.Time (formatTime, getZonedTime)
11 import System.Exit
12 import System.Locale (defaultTimeLocale, rfc822DateFormat)
13 import System.Process
14 import System.IO (hClose, hGetContents, hPutStr)
15
16
17 type Header = String
18
19 -- | A crude model of an RFC821 email message.
20 data Message = Message { headers :: [Header],
21 subject :: String,
22 body :: String,
23 from :: String,
24 to :: String }
25 deriving (Eq)
26
27 -- | The default headers attached to each message. The MIME junk is
28 -- needed for UTF-8 to work properly. Note that your mail server
29 -- should support the 8BITMIME extension.
30 default_headers :: [Header]
31 default_headers = ["MIME-Version: 1.0",
32 "Content-Type: text/plain; charset=UTF-8",
33 "Content-Transfer-Encoding: 8bit"]
34
35 -- | Showing a message will print it in roughly RFC-compliant
36 -- form. This form is sufficient for handing the message off to
37 -- sendmail (or compatible).
38 instance Show Message where
39 show m =
40 concat [ formatted_headers,
41 "Subject: " ++ (subject m) ++ "\n",
42 "From: " ++ (from m) ++ "\n",
43 "To: " ++ (to m) ++ "\n",
44 "\n",
45 (body m) ]
46 where
47 formatted_headers =
48 if null (headers m)
49 then ""
50 else (intercalate "\n" (headers m)) ++ "\n"
51
52
53 -- |Pad a string on the left with zeros until the entire string has
54 -- length n.
55 pad_left :: String -> Int -> String
56 pad_left str n
57 | n < (length str) = str
58 | otherwise = (replicate num_zeros '0') ++ str
59 where num_zeros = n - (length str)
60
61
62
63 -- | Constructs a 'String' in RFC822 date format for the current
64 -- date/time.
65 rfc822_now :: IO String
66 rfc822_now =
67 liftM (formatTime defaultTimeLocale rfc822DateFormat) getZonedTime
68
69
70
71
72 -- |Takes a message as an argument, and passes it to the system's
73 -- sendmail (or compatible) binary.
74 sendmail :: FilePath -> Message -> IO (String, String, ExitCode)
75 sendmail sendmail_path message = do
76 let sendmail_args = ["-f",
77 (from message),
78 (to message)]
79
80 (inh, outh, errh, ph) <-
81 runInteractiveProcess sendmail_path sendmail_args Nothing Nothing
82
83 outm <- newEmptyMVar
84 outs <- hGetContents outh
85
86 errm <- newEmptyMVar
87 errs <- hGetContents errh
88
89 _ <- forkIO $ hPutStr inh (show message) >> hClose inh
90 _ <- forkIO $ evaluate (length outs) >> putMVar outm ()
91 _ <- forkIO $ evaluate (length errs) >> putMVar errm ()
92
93 readMVar outm
94 readMVar errm
95
96 ec <- waitForProcess ph
97 return (outs, errs, ec)
98
99
100 -- |The 'sendmail' function returns a three-tuple of its outputs,
101 -- errors, and exit codes. This function pretty-prints one of those
102 -- three-tuples.
103 print_sendmail_result :: (String, String, ExitCode) -> IO ()
104 print_sendmail_result (outs, errs, ec) =
105 case ec of
106 ExitSuccess -> return ()
107 _ -> putStrLn $ concat ["Output: " ++ outs,
108 "\nErrors: " ++ errs,
109 "\nExit Code: " ++ (show ec)]