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