1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
{- Utilities for working with Maildir format.
-
- Copyright 2013 Raúl Benencia <rul@kalgan.cc>
-
- Licensed under the GNU GPL version 3 or higher
-
-}
module Maildir where
import Control.Monad.Loops(allM)
import Control.Monad (forM, filterM)
import Data.List(isPrefixOf)
import System.Directory (doesDirectoryExist, getDirectoryContents)
import System.FilePath ((</>))
import System.IO(IOMode(..), hGetContents, openFile)
import Types(Maildir, Flag(..), Flags)
isMaildir :: FilePath -> IO Bool
isMaildir fp = allM doesDirectoryExist [ fp
, fp </> "cur"
, fp </> "new"
, fp </> "tmp"]
getMaildirEmails md = do
r <- (getReadEmails md)
n <- (getNewEmails md)
return $ r ++ n
getReadEmails md = getEmails $ md </> "cur"
getNewEmails md = getEmails $ md </> "new"
getEmails fp = do
contents <- getDirectoryContents fp
return $ map (fp </>) $ filter (`notElem` [".", ".."]) contents
{- | Returns information about specific messages. -}
getMessages :: Maildir -> [FilePath] -> IO [(FilePath, Flags, String)]
getMessages mb list = do
messages <- getAll mb
return $ filter (\(id, f, m) -> id `elem` list) messages
--
-- | Based on getRecursiveContents from Real World Haskell
--
getMaildirsRecursively :: FilePath -> IO [Maildir]
getMaildirsRecursively topdir = do
result <- search topdir
includeTopDir <- isMaildir topdir
if includeTopDir
then return (topdir:result)
else return result
where
search topdir = do
names <- getDirectoryContents topdir
let properNames = filter (`notElem` [".", ".."]) names
paths <- forM properNames $ \name -> do
let path = topdir </> name
isDirectory <- doesDirectoryExist path
if isDirectory
then do
result <- search path
return ([path] ++ result)
else return []
filterM isMaildir (concat paths)
{- The following code is an implementation of the Mailbox interface -}
listIDs :: Maildir -> IO [FilePath]
listIDs md = getNewIDs md `appendM` getReadIDs md
where mxs `appendM` mxs' = do
xs <- mxs
xs' <- mxs'
return (xs ++ xs')
getNewIDs :: Maildir -> IO [FilePath]
getNewIDs md = getIDs (md </> "new")
getReadIDs :: Maildir -> IO [FilePath]
getReadIDs md = getIDs (md </> "cur")
getIDs :: FilePath -> IO [FilePath]
getIDs fp = do
names <-getDirectoryContents fp
let properNames = filter (`notElem` [".", ".."]) names
return $ map (fp </>) properNames
listMessageFlags :: Maildir -> IO [(FilePath, Flags)]
listMessageFlags fp = do
ids <- (listIDs fp)
let flags = map getFlags ids
return (zip ids flags)
getFlags :: FilePath -> Flags
getFlags fp = addNew $ map toFlag $ strip fp
where strip x
| null x = []
| ":2," `isPrefixOf` x = drop 3 x
| otherwise = let (discard, analyze) = span (/= ':') fp
in strip analyze
addNew flags = if elem SEEN flags then flags else (NEW:flags)
isNew :: FilePath -> Bool
isNew fp = elem NEW $ getFlags fp
toFlag :: Char -> Flag
toFlag c | c == 'S' = SEEN
| c == 'A' = ANSWERED
| c == 'F' = FLAGGED
| c == 'D' = DRAFT
| c == 'P' = FORWARDED
| c == 'T' = DELETED
| otherwise = OTHERFLAG [c]
getAll :: Maildir -> IO [(FilePath, Flags, String)]
getAll fp = do
ids <- listIDs fp
msgs <- mapM (\x -> hGetContents =<< openFile x ReadMode) ids
let flags = map getFlags ids
return $ zip3 ids flags msgs
|