import Data.List (intercalate)
import System.Exit
import System.Process
+import System.Time (CalendarTime(..), ClockTime, getClockTime, Month, toCalendarTime)
import System.IO
type Header = String
-- |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
+instance Show Message where
show m =
concat [ if (length (headers m) == 0) then "" else (intercalate "\n" (headers m)) ++ "\n",
"Subject: " ++ (subject m) ++ "\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,
+--
+-- <http://cr.yp.to/immhf/date.html>
+--
+-- 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)
import Control.Concurrent (forkIO, threadDelay)
import Control.Monad (forever, when)
import System.Exit (ExitCode(..), exitWith)
-import System.Time (getClockTime)
import System.IO (hPutStrLn, stderr)
import CommandLine
recurse username new_latest_status_id maybe_message
return ()
Just message -> do
- date <- getClockTime
- let messages = map (message_from_status message (show date)) new_statuses
+ date_header <- construct_date_header
+ let messages = map (message_from_status message (date_header)) new_statuses
sendmail_results <- mapM sendmail messages
mapM print_sendmail_result sendmail_results
recurse username new_latest_status_id maybe_message