mbox-tools-0.2.0.0: mbox-partition.hs
{-# LANGUAGE TemplateHaskell, TypeOperators #-}
--------------------------------------------------------------------
-- |
-- Executable : mbox-partition
-- Copyright : (c) Nicolas Pouillard 2008, 2009, 2011
-- License : BSD3
--
-- Maintainer: Nicolas Pouillard <nicolas.pouillard@gmail.com>
-- Stability : provisional
-- Portability:
--
--------------------------------------------------------------------
import Control.Applicative
import Codec.Mbox (Mbox(..),Direction(..),parseMboxFile,mboxMsgBody,showMboxMessage)
import Email (Email(..),emailFields,readEmail)
import Text.ParserCombinators.Parsec.Rfc2822 (Field(MessageID))
import Data.Label
import Data.Maybe (listToMaybe, fromMaybe)
import Data.Set (fromList, member)
import qualified Data.ByteString.Lazy.Char8 as C
import System.Environment (getArgs)
import System.Console.GetOpt
import System.IO (Handle, IOMode(AppendMode), stderr, openFile, hPutStr, hFlush, hClose)
progressStr :: String -> IO ()
progressStr s = hPutStr stderr ('\r':s) >> hFlush stderr
progress_ :: [IO a] -> IO ()
progress_ = (>> progressStr "Finished\n") . sequence_ . zipWith (>>) (map (progressStr . show) [(1::Int)..])
-- should be in ByteString
hPutStrLnC :: Handle -> C.ByteString -> IO ()
hPutStrLnC h s = C.hPut h s >> C.hPut h (C.pack "\n")
data Settings = Settings { _help :: Bool
, _msgids :: String
, _inside :: String
, _outside :: String
}
$(mkLabels [''Settings])
type Flag = Settings -> Settings
partitionMbox :: Settings -> [String] -> IO ()
partitionMbox opts mboxfiles = do
msgids' <- (fromList . C.lines) <$> C.readFile (get msgids opts)
let predicate = fromMaybe False . fmap (`member` msgids') . emailMsgId . readEmail . get mboxMsgBody
hinside <- openFile (get inside opts) AppendMode
houtside <- openFile (get outside opts) AppendMode
let onFile fp =
progress_ . map (\m -> hPutStrLnC (if predicate m then hinside else houtside) (showMboxMessage m))
. mboxMessages
=<< parseMboxFile Forward fp
mapM_ onFile mboxfiles
mapM_ hClose [hinside, houtside]
emailMsgId :: Email -> Maybe C.ByteString
emailMsgId m = listToMaybe [ removeAngles $ C.pack i | MessageID i <- get emailFields m ]
removeAngles :: C.ByteString -> C.ByteString
removeAngles = C.takeWhile (/='>') . C.dropWhile (=='<')
defaultSettings :: Settings
defaultSettings = Settings { _help = False
, _msgids = ""
, _inside = ""
, _outside = "" }
usage :: String -> a
usage msg = error (msg ++ "\n" ++ usageInfo header options)
where header = "Usage: mbox-partition [OPTION...] <mbox-file>*"
options :: [OptDescr Flag]
options =
[ Option "m" ["msgids"] (ReqArg (set msgids) "FILE") "A file with message-IDs"
, Option "i" ["inside"] (ReqArg (set inside) "FILE") "Will receive messages referenced by the 'msgids' file"
, Option "o" ["outside"] (ReqArg (set outside) "FILE") "Will receive messages *NOT* referenced by the 'msgids' file"
, Option "?" ["help"] (NoArg (set help True)) "Show this help message"
]
main :: IO ()
main = do
args <- getArgs
let (flags, nonopts, errs) = getOpt Permute options args
let opts = foldr ($) defaultSettings flags
case (nonopts, errs) of
_ | get help opts -> usage ""
(_, _:_) -> usage (concat errs)
([], _) -> usage "Too few arguments (mbox-file missing)"
(mboxfiles, _) -> partitionMbox opts mboxfiles