1 //
2 // VMime library (http://www.vmime.org)
3 // Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 3 of
8 // the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 // Linking this library statically or dynamically with other modules is making
20 // a combined work based on this library. Thus, the terms and conditions of
21 // the GNU General Public License cover the whole combination.
22 //
23
24 #include "vmime/config.hpp"
25
26
27 #if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR
28
29
30 #include "vmime/net/maildir/maildirUtils.hpp"
31 #include "vmime/net/maildir/maildirStore.hpp"
32
33 #include "vmime/utility/random.hpp"
34 #include "vmime/platform.hpp"
35
36 #include "vmime/exception.hpp"
37
38
39 namespace vmime {
40 namespace net {
41 namespace maildir {
42
43
isMessageFile(const utility::file & file)44 bool maildirUtils::isMessageFile(const utility::file& file)
45 {
46 // Ignore files which name begins with '.'
47 if (file.isFile() &&
48 file.getFullPath().getLastComponent().getBuffer().length() >= 1 &&
49 file.getFullPath().getLastComponent().getBuffer()[0] != '.')
50 {
51 return (true);
52 }
53
54 return (false);
55 }
56
57
58 // NOTE ABOUT ID/FLAGS SEPARATOR
59 // -----------------------------
60 // In the maildir specification, the character ':' is used to separate
61 // the unique identifier and the message flags.
62 //
63 // On Windows (and particularly FAT file systems), ':' is not allowed
64 // in a filename, so we use a dash ('-') instead. This is the solution
65 // used by Mutt/Win32, so we also use it here.
66 //
67 // To be compatible between implementations, we check for both
68 // characters when reading file names.
69
70
extractId(const utility::file::path::component & filename)71 const utility::file::path::component maildirUtils::extractId
72 (const utility::file::path::component& filename)
73 {
74 size_t sep = filename.getBuffer().rfind(':'); // try colon
75
76 if (sep == string::npos)
77 {
78 sep = filename.getBuffer().rfind('-'); // try dash (Windows)
79 if (sep == string::npos) return (filename);
80 }
81
82 return (utility::path::component
83 (string(filename.getBuffer().begin(), filename.getBuffer().begin() + sep)));
84 }
85
86
extractFlags(const utility::file::path::component & comp)87 int maildirUtils::extractFlags(const utility::file::path::component& comp)
88 {
89 size_t sep = comp.getBuffer().rfind(':'); // try colon
90
91 if (sep == string::npos)
92 {
93 sep = comp.getBuffer().rfind('-'); // try dash (Windows)
94 if (sep == string::npos) return 0;
95 }
96
97 const string flagsString(comp.getBuffer().begin() + sep + 1, comp.getBuffer().end());
98 const size_t count = flagsString.length();
99
100 int flags = 0;
101
102 for (size_t i = 0 ; i < count ; ++i)
103 {
104 switch (flagsString[i])
105 {
106 case 'R': case 'r': flags |= message::FLAG_REPLIED; break;
107 case 'S': case 's': flags |= message::FLAG_SEEN; break;
108 case 'T': case 't': flags |= message::FLAG_DELETED; break;
109 case 'F': case 'f': flags |= message::FLAG_MARKED; break;
110 case 'P': case 'p': flags |= message::FLAG_PASSED; break;
111 case 'D': case 'd': flags |= message::FLAG_DRAFT; break;
112 }
113 }
114
115 return (flags);
116 }
117
118
buildFlags(const int flags)119 const utility::file::path::component maildirUtils::buildFlags(const int flags)
120 {
121 string str;
122 str.reserve(8);
123
124 str += "2,";
125
126 if (flags & message::FLAG_MARKED) str += "F";
127 if (flags & message::FLAG_PASSED) str += "P";
128 if (flags & message::FLAG_REPLIED) str += "R";
129 if (flags & message::FLAG_SEEN) str += "S";
130 if (flags & message::FLAG_DELETED) str += "T";
131 if (flags & message::FLAG_DRAFT) str += "D";
132
133 return (utility::file::path::component(str));
134 }
135
136
buildFilename(const utility::file::path::component & id,const int flags)137 const utility::file::path::component maildirUtils::buildFilename
138 (const utility::file::path::component& id, const int flags)
139 {
140 if (flags == message::FLAG_RECENT)
141 return id;
142 else
143 return (buildFilename(id, buildFlags(flags)));
144 }
145
146
buildFilename(const utility::file::path::component & id,const utility::file::path::component & flags)147 const utility::file::path::component maildirUtils::buildFilename
148 (const utility::file::path::component& id,
149 const utility::file::path::component& flags)
150 {
151 #if VMIME_PLATFORM_IS_WINDOWS
152 static const char DELIMITER[] = "-";
153 #else
154 static const char DELIMITER[] = ":";
155 #endif
156
157 return utility::path::component(id.getBuffer() + DELIMITER + flags.getBuffer());
158 }
159
160
generateId()161 const utility::file::path::component maildirUtils::generateId()
162 {
163 std::ostringstream oss;
164 oss.imbue(std::locale::classic());
165
166 oss << utility::random::getTime();
167 oss << ".";
168 oss << utility::random::getProcess();
169 oss << ".";
170 oss << utility::random::getString(6);
171 oss << ".";
172 oss << platform::getHandler()->getHostName();
173
174 return (utility::file::path::component(oss.str()));
175 }
176
177
recursiveFSDelete(shared_ptr<utility::file> dir)178 void maildirUtils::recursiveFSDelete(shared_ptr <utility::file> dir)
179 {
180 shared_ptr <utility::fileIterator> files = dir->getFiles();
181
182 // First, delete files and subdirectories in this directory
183 while (files->hasMoreElements())
184 {
185 shared_ptr <utility::file> file = files->nextElement();
186
187 if (file->isDirectory())
188 {
189 maildirUtils::recursiveFSDelete(file);
190 }
191 else
192 {
193 try
194 {
195 file->remove();
196 }
197 catch (exceptions::filesystem_exception&)
198 {
199 // Ignore
200 }
201 }
202 }
203
204 // Then, delete this (empty) directory
205 try
206 {
207 dir->remove();
208 }
209 catch (exceptions::filesystem_exception&)
210 {
211 // Ignore
212 }
213 }
214
215
216
217 class maildirMessageSetEnumerator : public messageSetEnumerator
218 {
219 public:
220
maildirMessageSetEnumerator(const size_t msgCount)221 maildirMessageSetEnumerator(const size_t msgCount)
222 : m_msgCount(msgCount)
223 {
224
225 }
226
enumerateNumberMessageRange(const vmime::net::numberMessageRange & range)227 void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range)
228 {
229 size_t last = range.getLast();
230 if (last == size_t(-1)) last = m_msgCount;
231
232 for (size_t i = range.getFirst() ; i <= last ; ++i)
233 list.push_back(i);
234 }
235
enumerateUIDMessageRange(const vmime::net::UIDMessageRange &)236 void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& /* range */)
237 {
238 // Not supported
239 }
240
241 public:
242
243 std::vector <size_t> list;
244
245 private:
246
247 size_t m_msgCount;
248 };
249
250
251 // static
messageSetToNumberList(const messageSet & msgs,const size_t msgCount)252 const std::vector <size_t> maildirUtils::messageSetToNumberList(const messageSet& msgs, const size_t msgCount)
253 {
254 maildirMessageSetEnumerator en(msgCount);
255 msgs.enumerate(en);
256
257 return en.list;
258 }
259
260
261
262 //
263 // messageIdComparator
264 //
265
messageIdComparator(const utility::file::path::component & comp)266 maildirUtils::messageIdComparator::messageIdComparator
267 (const utility::file::path::component& comp)
268 : m_comp(maildirUtils::extractId(comp))
269 {
270 }
271
272
operator ()(const utility::file::path::component & other) const273 bool maildirUtils::messageIdComparator::operator()
274 (const utility::file::path::component& other) const
275 {
276 return (m_comp == maildirUtils::extractId(other));
277 }
278
279
280 } // maildir
281 } // net
282 } // vmime
283
284
285 #endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR
286
287