X-Git-Url: http://gitweb.michael.orlitzky.com/?p=dead%2Fhalcyon.git;a=blobdiff_plain;f=src%2FMail.hs;h=03b7284665fa40b742cc6d36fa3a9209c2a6c838;hp=2b5445c4ddcbd96020ddb2010b035db1235699d7;hb=6ab78e47075e9a60edee16bcd5f302189600352e;hpb=17dd116706c4a971e1f5c68daa1656af5eff5cd2 diff --git a/src/Mail.hs b/src/Mail.hs index 2b5445c..03b7284 100644 --- a/src/Mail.hs +++ b/src/Mail.hs @@ -1,16 +1,20 @@ +-- |Email functions and data types. + module Mail where import Control.Concurrent -import Control.Concurrent.MVar import Control.Exception (evaluate) import Data.List (intercalate) import System.Exit import System.Process -import System.IO +import System.Time (CalendarTime(..), ClockTime, getClockTime, Month, toCalendarTime) +import System.IO (hClose, hGetContents, hPutStr) + type Header = String +-- |A crude model of an RFC821 email message. data Message = Message { headers :: [Header], subject :: String, body :: String, @@ -18,6 +22,17 @@ data Message = Message { headers :: [Header], to :: String } deriving (Eq) +-- |The default headers attached to each message. +-- The MIME junk is needed for UTF-8 to work properly. +-- Note that your mail server should support the 8BITMIME extension. +default_headers :: [Header] +default_headers = ["MIME-Version: 1.0", + "Content-Type: text/plain; charset=UTF-8", + "Content-Transfer-Encoding: 8bit"] + +-- |Showing a message will print it in roughly RFC-compliant +-- form. This form is sufficient for handing the message off to +-- sendmail. instance Show Message where show m = concat [ if (length (headers m) == 0) then "" else (intercalate "\n" (headers m)) ++ "\n", @@ -27,7 +42,76 @@ instance Show Message where "\n", (body m) ] - + + +-- |Pad a string on the left with zeros until the entire string has +-- length n. +pad_left :: String -> Int -> String +pad_left str n + | n < (length str) = str + | otherwise = (replicate num_zeros '0') ++ str + where num_zeros = n - (length str) + + + +-- |Formats a month name according to RFC822. +format_month :: Month -> String +format_month month = take 3 (show month) + + +-- |Takes an offset from UTC (in seconds) and returns the four-digit +-- offset as a 'String' in +hhmm format. +format_timezone :: Int -> String +format_timezone seconds = + sign ++ pad_hh ++ pad_mm + where + seconds_norm = abs seconds + hh = seconds_norm `div` 3600 + mm = (seconds_norm - (hh*3600)) `div` 60 + pad_hh = pad_left (show hh) 2 + pad_mm = pad_left (show mm) 2 + sign = if seconds < 0 then "-" else "+" + + +-- |Takes a 'ClockTime' as an argument, and formats it as an RFC822 Date header. +-- +-- See, +-- +-- +-- +-- for information. +format_clocktime :: ClockTime -> IO String +format_clocktime ct = do + caltime <- (toCalendarTime ct) + + let days = pad_left (show (ctDay caltime)) 2 + let month = format_month (ctMonth caltime) + let year = show $ ctYear caltime + let hours = pad_left (show (ctHour caltime)) 2 + let minutes = pad_left (show (ctMin caltime)) 2 + let seconds = pad_left (show (ctSec caltime)) 2 + let timezone = format_timezone (ctTZ caltime) + + return $ concat [(show $ ctWDay caltime) ++ ", ", + days ++ " ", + month ++ " ", + year ++ " ", + hours ++ ":", + minutes ++ ":", + seconds ++ " ", + timezone] + + +-- |Constructs an RFC822 Date header with the current date/time. +construct_date_header :: IO String +construct_date_header = do + date <- getClockTime + format_clocktime date + + + +-- |Takes a message as an argument, and passes it to the system's +-- sendmail binary. sendmail :: Message -> IO (String, String, ExitCode) sendmail message = do let sendmail_args = ["-f", @@ -42,9 +126,9 @@ sendmail message = do errm <- newEmptyMVar errs <- hGetContents errh - forkIO $ hPutStr inh (show message) >> hClose inh - forkIO $ evaluate (length outs) >> putMVar outm () - forkIO $ evaluate (length errs) >> putMVar errm () + _ <- forkIO $ hPutStr inh (show message) >> hClose inh + _ <- forkIO $ evaluate (length outs) >> putMVar outm () + _ <- forkIO $ evaluate (length errs) >> putMVar errm () readMVar outm readMVar errm @@ -53,6 +137,9 @@ sendmail message = do return (outs, errs, ec) +-- |The 'sendmail' function returns a three-tuple of its outputs, +-- errors, and exit codes. This function pretty-prints one of those +-- three-tuples. print_sendmail_result :: (String, String, ExitCode) -> IO () print_sendmail_result (outs, errs, ec) = do case ec of