X-Git-Url: http://gitweb.michael.orlitzky.com/?p=dead%2Fhalcyon.git;a=blobdiff_plain;f=src%2FMail.hs;h=db5673f62c84bef2964e2e1cb4fb188ec3d43a49;hp=03b7284665fa40b742cc6d36fa3a9209c2a6c838;hb=d721869c5e7395c021cc79f40720bdb275d613d2;hpb=6ab78e47075e9a60edee16bcd5f302189600352e diff --git a/src/Mail.hs b/src/Mail.hs index 03b7284..db5673f 100644 --- a/src/Mail.hs +++ b/src/Mail.hs @@ -5,16 +5,18 @@ where import Control.Concurrent import Control.Exception (evaluate) +import Control.Monad (liftM) import Data.List (intercalate) +import Data.Time (formatTime, getZonedTime) import System.Exit +import System.Locale (defaultTimeLocale, rfc822DateFormat) import System.Process -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. +-- | A crude model of an RFC821 email message. data Message = Message { headers :: [Header], subject :: String, body :: String, @@ -22,103 +24,61 @@ 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. +-- | 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. +-- | Showing a message will print it in roughly RFC-compliant +-- form. This form is sufficient for handing the message off to +-- sendmail (or compatible). instance Show Message where - show m = - concat [ if (length (headers m) == 0) then "" else (intercalate "\n" (headers m)) ++ "\n", - "Subject: " ++ (subject m) ++ "\n", - "From: " ++ (from m) ++ "\n", - "To: " ++ (to m) ++ "\n", - "\n", - (body m) ] - + show m = + concat [ formatted_headers, + "Subject: " ++ (subject m) ++ "\n", + "From: " ++ (from m) ++ "\n", + "To: " ++ (to m) ++ "\n", + "\n", + (body m) ] + where + formatted_headers = + if null (headers m) + then "" + else (intercalate "\n" (headers m)) ++ "\n" -- |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) + | 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) +-- | Constructs a 'String' in RFC822 date format for the current +-- date/time. +rfc822_now :: IO String +rfc822_now = + liftM (formatTime defaultTimeLocale rfc822DateFormat) getZonedTime --- |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 +-- sendmail (or compatible) binary. +sendmail :: FilePath -> Message -> IO (String, String, ExitCode) +sendmail sendmail_path message = do let sendmail_args = ["-f", - (from message)] + (from message), + (to message)] (inh, outh, errh, ph) <- - runInteractiveProcess "/usr/bin/sendmail" sendmail_args Nothing Nothing + runInteractiveProcess sendmail_path sendmail_args Nothing Nothing outm <- newEmptyMVar outs <- hGetContents outh @@ -141,9 +101,9 @@ sendmail message = do -- 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 - ExitSuccess -> return () - _ -> putStrLn $ concat ["Output: " ++ outs, - "\nErrors: " ++ errs, - "\nExit Code: " ++ (show ec)] +print_sendmail_result (outs, errs, ec) = + case ec of + ExitSuccess -> return () + _ -> putStrLn $ concat ["Output: " ++ outs, + "\nErrors: " ++ errs, + "\nExit Code: " ++ (show ec)]