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 "tests/testUtils.hpp"
25
26 #include "vmime/platform.hpp"
27
28 #include "vmime/net/maildir/maildirStore.hpp"
29 #include "vmime/net/maildir/maildirFormat.hpp"
30
31
32 // Shortcuts and helpers
33 typedef vmime::utility::file::path fspath;
34 typedef vmime::utility::file::path::component fspathc;
35
36 typedef vmime::net::folder::path fpath;
37 typedef vmime::net::folder::path::component fpathc;
38
39
operator /(const fpath & path,const std::string & c)40 const fpath operator/(const fpath& path, const std::string& c)
41 {
42 return path / fpathc(c);
43 }
44
45
46 /** Test messages */
47 static const vmime::string TEST_MESSAGE_1 =
48 "From: <test@vmime.org>\r\n"
49 "Subject: VMime Test\r\n"
50 "Date: Thu, 01 Mar 2007 09:49:35 +0100\r\n"
51 "\r\n"
52 "Hello, world!";
53
54
55 /** Maildir trees used in tests.
56 * Structure:
57 *
58 * .
59 * |-- Folder
60 * | `-- SubFolder
61 * | |-- SubSubFolder1
62 * | `-- SubSubFolder2
63 * `-- Folder2
64 *
65 */
66
67 // KMail format
68 static const vmime::string TEST_MAILDIR_KMAIL[] = // directories to create
69 {
70 "/Folder",
71 "/Folder/new",
72 "/Folder/tmp",
73 "/Folder/cur",
74 "/.Folder.directory",
75 "/.Folder.directory/SubFolder",
76 "/.Folder.directory/SubFolder/new",
77 "/.Folder.directory/SubFolder/tmp",
78 "/.Folder.directory/SubFolder/cur",
79 "/.Folder.directory/.SubFolder.directory",
80 "/.Folder.directory/.SubFolder.directory/SubSubFolder1",
81 "/.Folder.directory/.SubFolder.directory/SubSubFolder1/new",
82 "/.Folder.directory/.SubFolder.directory/SubSubFolder1/tmp",
83 "/.Folder.directory/.SubFolder.directory/SubSubFolder1/cur",
84 "/.Folder.directory/.SubFolder.directory/SubSubFolder2",
85 "/.Folder.directory/.SubFolder.directory/SubSubFolder2/new",
86 "/.Folder.directory/.SubFolder.directory/SubSubFolder2/tmp",
87 "/.Folder.directory/.SubFolder.directory/SubSubFolder2/cur",
88 "/Folder2",
89 "/Folder2/new",
90 "/Folder2/tmp",
91 "/Folder2/cur",
92 "*" // end
93 };
94
95 static const vmime::string TEST_MAILDIRFILES_KMAIL[] = // files to create and their contents
96 {
97 "/.Folder.directory/.SubFolder.directory/SubSubFolder2/cur/1043236113.351.EmqD:S", TEST_MESSAGE_1,
98 "*" // end
99 };
100
101 // Courier format
102 static const vmime::string TEST_MAILDIR_COURIER[] = // directories to create
103 {
104 "/.Folder",
105 "/.Folder/new",
106 "/.Folder/tmp",
107 "/.Folder/cur",
108 "/.Folder.SubFolder",
109 "/.Folder.SubFolder",
110 "/.Folder.SubFolder/new",
111 "/.Folder.SubFolder/tmp",
112 "/.Folder.SubFolder/cur",
113 "/.Folder.SubFolder.SubSubFolder1",
114 "/.Folder.SubFolder.SubSubFolder1/new",
115 "/.Folder.SubFolder.SubSubFolder1/tmp",
116 "/.Folder.SubFolder.SubSubFolder1/cur",
117 "/.Folder.SubFolder.SubSubFolder2",
118 "/.Folder.SubFolder.SubSubFolder2/new",
119 "/.Folder.SubFolder.SubSubFolder2/tmp",
120 "/.Folder.SubFolder.SubSubFolder2/cur",
121 "/.Folder2",
122 "/.Folder2/new",
123 "/.Folder2/tmp",
124 "/.Folder2/cur",
125 "*" // end
126 };
127
128 static const vmime::string TEST_MAILDIRFILES_COURIER[] = // files to create and their contents
129 {
130 "/.Folder/maildirfolder", "",
131 "/.Folder.SubFolder/maildirfolder", "",
132 "/.Folder.SubFolder.SubSubFolder1/maildirfolder", "",
133 "/.Folder.SubFolder.SubSubFolder2/maildirfolder", "",
134 "/.Folder.SubFolder.SubSubFolder2/cur/1043236113.351.EmqD:S", TEST_MESSAGE_1,
135 "/.Folder2/maildirfolder", "",
136 "*" // end
137 };
138
139
140
141 VMIME_TEST_SUITE_BEGIN(maildirStoreTest)
142
143 VMIME_TEST_LIST_BEGIN
144 VMIME_TEST(testDetectFormat_KMail)
145 VMIME_TEST(testDetectFormat_Courier)
146
147 VMIME_TEST(testListRootFolders_KMail)
148 VMIME_TEST(testListAllFolders_KMail)
149
150 VMIME_TEST(testListRootFolders_Courier)
151 VMIME_TEST(testListAllFolders_Courier)
152
153 VMIME_TEST(testListMessages_KMail)
154 VMIME_TEST(testListMessages_Courier)
155
156 VMIME_TEST(testRenameFolder_KMail)
157 VMIME_TEST(testRenameFolder_Courier)
158
159 VMIME_TEST(testDestroyFolder_KMail)
160 VMIME_TEST(testDestroyFolder_Courier)
161
162 VMIME_TEST(testFolderExists_KMail)
163 VMIME_TEST(testFolderExists_Courier)
164
165 VMIME_TEST(testCreateFolder_KMail)
166 VMIME_TEST(testCreateFolder_Courier)
167 VMIME_TEST_LIST_END
168
169
170 public:
171
172 maildirStoreTest()
173 {
174 // Temporary directory
175 m_tempPath = fspath() / fspathc("tmp") // Use /tmp
176 / fspathc("vmime" + vmime::utility::stringUtils::toString(std::time(NULL))
177 + vmime::utility::stringUtils::toString(std::rand()));
178 }
179
180 void tearDown()
181 {
182 // In case of an uncaught exception
183 destroyMaildir();
184 }
185
186 void testDetectFormat_KMail()
187 {
188 createMaildir(TEST_MAILDIR_KMAIL, TEST_MAILDIRFILES_KMAIL);
189
190 vmime::shared_ptr <vmime::net::maildir::maildirStore> store =
191 vmime::dynamicCast <vmime::net::maildir::maildirStore>(createAndConnectStore());
192
193 VASSERT_EQ("*", "kmail", store->getFormat()->getName());
194
195 destroyMaildir();
196 }
197
198 void testDetectFormat_Courier()
199 {
200 createMaildir(TEST_MAILDIR_COURIER, TEST_MAILDIRFILES_COURIER);
201
202 vmime::shared_ptr <vmime::net::maildir::maildirStore> store =
203 vmime::dynamicCast <vmime::net::maildir::maildirStore>(createAndConnectStore());
204
205 VASSERT_EQ("*", "courier", store->getFormat()->getName());
206
207 destroyMaildir();
208 }
209
210
211 void testListRootFolders_KMail()
212 {
213 testListRootFoldersImpl(TEST_MAILDIR_KMAIL, TEST_MAILDIRFILES_KMAIL);
214 }
215
216 void testListRootFolders_Courier()
217 {
218 testListRootFoldersImpl(TEST_MAILDIR_COURIER, TEST_MAILDIRFILES_COURIER);
219 }
220
221 void testListRootFoldersImpl(const vmime::string* const dirs, const vmime::string* const files)
222 {
223 createMaildir(dirs, files);
224
225 // Connect to store
226 vmime::shared_ptr <vmime::net::store> store = createAndConnectStore();
227 vmime::shared_ptr <vmime::net::folder> rootFolder = store->getRootFolder();
228
229 // Get root folders, not recursive
230 const std::vector <vmime::shared_ptr <vmime::net::folder> >
231 rootFolders = rootFolder->getFolders(false);
232
233 VASSERT_EQ("1", 2, rootFolders.size());
234 VASSERT("2", findFolder(rootFolders, fpath() / "Folder") != NULL);
235 VASSERT("3", findFolder(rootFolders, fpath() / "Folder2") != NULL);
236
237 destroyMaildir();
238 }
239
240
241 void testListAllFolders_KMail()
242 {
243 testListAllFoldersImpl(TEST_MAILDIR_KMAIL, TEST_MAILDIRFILES_KMAIL);
244 }
245
246 void testListAllFolders_Courier()
247 {
248 testListAllFoldersImpl(TEST_MAILDIR_COURIER, TEST_MAILDIRFILES_COURIER);
249 }
250
251 void testListAllFoldersImpl(const vmime::string* const dirs, const vmime::string* const files)
252 {
253 createMaildir(dirs, files);
254
255 // Connect to store
256 vmime::shared_ptr <vmime::net::store> store = createAndConnectStore();
257 vmime::shared_ptr <vmime::net::folder> rootFolder = store->getRootFolder();
258
259 // Get all folders, recursive
260 const std::vector <vmime::shared_ptr <vmime::net::folder> >
261 allFolders = rootFolder->getFolders(true);
262
263 VASSERT_EQ("1", 5, allFolders.size());
264 VASSERT("2", findFolder(allFolders, fpath() / "Folder") != NULL);
265 VASSERT("3", findFolder(allFolders, fpath() / "Folder" / "SubFolder") != NULL);
266 VASSERT("4", findFolder(allFolders, fpath() / "Folder" / "SubFolder" / "SubSubFolder1") != NULL);
267 VASSERT("5", findFolder(allFolders, fpath() / "Folder" / "SubFolder" / "SubSubFolder2") != NULL);
268 VASSERT("6", findFolder(allFolders, fpath() / "Folder2") != NULL);
269
270 destroyMaildir();
271 }
272
273
274 void testListMessages_KMail()
275 {
276 testListMessagesImpl(TEST_MAILDIR_KMAIL, TEST_MAILDIRFILES_KMAIL);
277 }
278
279 void testListMessages_Courier()
280 {
281 testListMessagesImpl(TEST_MAILDIR_COURIER, TEST_MAILDIRFILES_COURIER);
282 }
283
284 void testListMessagesImpl(const vmime::string* const dirs, const vmime::string* const files)
285 {
286 createMaildir(dirs, files);
287
288 vmime::shared_ptr <vmime::net::store> store = createAndConnectStore();
289 vmime::shared_ptr <vmime::net::folder> rootFolder = store->getRootFolder();
290
291 vmime::shared_ptr <vmime::net::folder> folder = store->getFolder
292 (fpath() / "Folder" / "SubFolder" / "SubSubFolder2");
293
294 vmime::size_t count, unseen;
295 folder->status(count, unseen);
296
297 VASSERT_EQ("Message count", 1, count);
298
299 folder->open(vmime::net::folder::MODE_READ_ONLY);
300
301 vmime::shared_ptr <vmime::net::message> msg = folder->getMessage(1);
302
303 folder->fetchMessage(msg, vmime::net::fetchAttributes::SIZE);
304
305 VASSERT_EQ("Message size", TEST_MESSAGE_1.length(), msg->getSize());
306
307 std::ostringstream oss;
308 vmime::utility::outputStreamAdapter os(oss);
309 msg->extract(os);
310
311 VASSERT_EQ("Message contents", TEST_MESSAGE_1, oss.str());
312
313 folder->close(false);
314
315 destroyMaildir();
316 }
317
318
319 void testRenameFolder_KMail()
320 {
321 try
322 {
323 testRenameFolderImpl(TEST_MAILDIR_KMAIL, TEST_MAILDIRFILES_KMAIL);
324 }
325 catch (vmime::exception& e)
326 {
327 std::cerr << e;
328 throw e;
329 }
330 }
331
332 void testRenameFolder_Courier()
333 {
334 try
335 {
336 testRenameFolderImpl(TEST_MAILDIR_COURIER, TEST_MAILDIRFILES_COURIER);
337 }
338 catch (vmime::exception& e)
339 {
340 std::cerr << e;
341 throw e;
342 }
343 }
344
345 void testRenameFolderImpl(const vmime::string* const dirs, const vmime::string* const files)
346 {
347 createMaildir(dirs, files);
348
349 vmime::shared_ptr <vmime::net::store> store = createAndConnectStore();
350 vmime::shared_ptr <vmime::net::folder> rootFolder = store->getRootFolder();
351
352 // Rename "Folder/SubFolder" to "Folder/foo"
353 vmime::shared_ptr <vmime::net::folder> folder = store->getFolder
354 (fpath() / "Folder" / "SubFolder");
355
356 folder->rename(fpath() / "Folder" / "foo");
357
358 // Ensure folder and its subfolders have been renamed
359 const std::vector <vmime::shared_ptr <vmime::net::folder> >
360 allFolders = rootFolder->getFolders(true);
361
362 VASSERT_EQ("1", 5, allFolders.size());
363 VASSERT("2", findFolder(allFolders, fpath() / "Folder") != NULL);
364 VASSERT("3", findFolder(allFolders, fpath() / "Folder" / "SubFolder") == NULL);
365 VASSERT("4", findFolder(allFolders, fpath() / "Folder" / "SubFolder" / "SubSubFolder1") == NULL);
366 VASSERT("5", findFolder(allFolders, fpath() / "Folder" / "SubFolder" / "SubSubFolder2") == NULL);
367 VASSERT("6", findFolder(allFolders, fpath() / "Folder2") != NULL);
368 VASSERT("7", findFolder(allFolders, fpath() / "Folder" / "foo") != NULL);
369 VASSERT("8", findFolder(allFolders, fpath() / "Folder" / "foo" / "SubSubFolder1") != NULL);
370 VASSERT("9", findFolder(allFolders, fpath() / "Folder" / "foo" / "SubSubFolder2") != NULL);
371
372 destroyMaildir();
373 }
374
375
376 void testDestroyFolder_KMail()
377 {
378 testDestroyFolderImpl(TEST_MAILDIR_KMAIL, TEST_MAILDIRFILES_KMAIL);
379 }
380
381 void testDestroyFolder_Courier()
382 {
383 testDestroyFolderImpl(TEST_MAILDIR_COURIER, TEST_MAILDIRFILES_COURIER);
384 }
385
386 void testDestroyFolderImpl(const vmime::string* const dirs, const vmime::string* const files)
387 {
388 createMaildir(dirs, files);
389
390 vmime::shared_ptr <vmime::net::store> store = createAndConnectStore();
391 vmime::shared_ptr <vmime::net::folder> rootFolder = store->getRootFolder();
392
393 // Destroy "Folder/SubFolder" (total: 3 folders)
394 vmime::shared_ptr <vmime::net::folder> folder = store->getFolder
395 (fpath() / "Folder" / "SubFolder");
396
397 folder->destroy();
398
399 // Ensure folder and its subfolders have been deleted and other folders still exist
400 const std::vector <vmime::shared_ptr <vmime::net::folder> >
401 allFolders = rootFolder->getFolders(true);
402
403 VASSERT_EQ("1", 2, allFolders.size());
404 VASSERT("2", findFolder(allFolders, fpath() / "Folder") != NULL);
405 VASSERT("3", findFolder(allFolders, fpath() / "Folder" / "SubFolder") == NULL);
406 VASSERT("4", findFolder(allFolders, fpath() / "Folder" / "SubFolder" / "SubSubFolder1") == NULL);
407 VASSERT("5", findFolder(allFolders, fpath() / "Folder" / "SubFolder" / "SubSubFolder2") == NULL);
408 VASSERT("6", findFolder(allFolders, fpath() / "Folder2") != NULL);
409
410 destroyMaildir();
411 }
412
413
414 void testFolderExists_KMail()
415 {
416 testFolderExistsImpl(TEST_MAILDIR_KMAIL, TEST_MAILDIRFILES_KMAIL);
417 }
418
419 void testFolderExists_Courier()
420 {
421 testFolderExistsImpl(TEST_MAILDIR_COURIER, TEST_MAILDIRFILES_COURIER);
422 }
423
424 void testFolderExistsImpl(const vmime::string* const dirs, const vmime::string* const files)
425 {
426 createMaildir(dirs, files);
427
428 vmime::shared_ptr <vmime::net::store> store = createAndConnectStore();
429 vmime::shared_ptr <vmime::net::folder> rootFolder = store->getRootFolder();
430
431 VASSERT("1", store->getFolder(fpath() / "Folder" / "SubFolder")->exists());
432 VASSERT("2", !store->getFolder(fpath() / "Folder" / "SubSubFolder1")->exists());
433 VASSERT("3", store->getFolder(fpath() / "Folder2")->exists());
434 VASSERT("4", store->getFolder(fpath() / "Folder" / "SubFolder" / "SubSubFolder2")->exists());
435
436 destroyMaildir();
437 }
438
439
440 void testCreateFolder_KMail()
441 {
442 testCreateFolderImpl(TEST_MAILDIR_KMAIL, TEST_MAILDIRFILES_KMAIL);
443 }
444
445 void testCreateFolder_Courier()
446 {
447 testCreateFolderImpl(TEST_MAILDIR_COURIER, TEST_MAILDIRFILES_COURIER);
448 }
449
450 void testCreateFolderImpl(const vmime::string* const dirs, const vmime::string* const files)
451 {
452 createMaildir(dirs, files);
453
454 vmime::shared_ptr <vmime::net::store> store = createAndConnectStore();
455 vmime::shared_ptr <vmime::net::folder> rootFolder = store->getRootFolder();
456
457 VASSERT("Before", !store->getFolder(fpath() / "Folder" / "NewFolder")->exists());
458
459 vmime::net::folderAttributes attribs;
460 attribs.setType(vmime::net::folderAttributes::TYPE_CONTAINS_MESSAGES);
461
462 VASSERT_NO_THROW("Creation", store->getFolder(fpath() / "Folder" / "NewFolder")->create(attribs));
463
464 VASSERT("After", store->getFolder(fpath() / "Folder" / "NewFolder")->exists());
465
466 destroyMaildir();
467 }
468
469 private:
470
471 vmime::utility::file::path m_tempPath;
472
473
474 vmime::shared_ptr <vmime::net::store> createAndConnectStore()
475 {
476 vmime::shared_ptr <vmime::net::session> session = vmime::net::session::create();
477
478 vmime::shared_ptr <vmime::net::store> store =
479 session->getStore(getStoreURL());
480
481 store->connect();
482
483 return store;
484 }
485
486 const vmime::shared_ptr <vmime::net::folder> findFolder
487 (const std::vector <vmime::shared_ptr <vmime::net::folder> >& folders,
488 const vmime::net::folder::path& path)
489 {
490 for (size_t i = 0, n = folders.size() ; i < n ; ++i)
491 {
492 if (folders[i]->getFullPath() == path)
493 return folders[i];
494 }
495
496 return vmime::null;
497 }
498
499 const vmime::utility::url getStoreURL()
500 {
501 vmime::shared_ptr <vmime::utility::fileSystemFactory> fsf =
502 vmime::platform::getHandler()->getFileSystemFactory();
503
504 vmime::utility::url url(std::string("maildir://localhost")
505 + fsf->pathToString(m_tempPath));
506
507 return url;
508 }
509
510 void createMaildir(const vmime::string* const dirs, const vmime::string* const files)
511 {
512 vmime::shared_ptr <vmime::utility::fileSystemFactory> fsf =
513 vmime::platform::getHandler()->getFileSystemFactory();
514
515 vmime::shared_ptr <vmime::utility::file> rootDir = fsf->create(m_tempPath);
516 rootDir->createDirectory(false);
517
518 for (vmime::string const* dir = dirs ; *dir != "*" ; ++dir)
519 {
520 vmime::shared_ptr <vmime::utility::file> fdir = fsf->create(m_tempPath / fsf->stringToPath(*dir));
521 fdir->createDirectory(false);
522 }
523
524 for (vmime::string const* file = files ; *file != "*" ; file += 2)
525 {
526 const vmime::string& contents = *(file + 1);
527
528 vmime::shared_ptr <vmime::utility::file> ffile = fsf->create(m_tempPath / fsf->stringToPath(*file));
529 ffile->createFile();
530
531 vmime::shared_ptr <vmime::utility::fileWriter> fileWriter = ffile->getFileWriter();
532 vmime::shared_ptr <vmime::utility::outputStream> os = fileWriter->getOutputStream();
533
534 os->write(contents.data(), contents.length());
535 os->flush();
536
537 fileWriter = vmime::null;
538 }
539
540 }
541
542 void destroyMaildir()
543 {
544 vmime::shared_ptr <vmime::utility::fileSystemFactory> fsf =
545 vmime::platform::getHandler()->getFileSystemFactory();
546
547 recursiveDelete(fsf->create(m_tempPath));
548 }
549
550 void recursiveDelete(vmime::shared_ptr <vmime::utility::file> dir)
551 {
552 if (!dir->exists() || !dir->isDirectory())
553 return;
554
555 vmime::shared_ptr <vmime::utility::fileIterator> files = dir->getFiles();
556
557 // First, delete files and subdirectories in this directory
558 while (files->hasMoreElements())
559 {
560 vmime::shared_ptr <vmime::utility::file> file = files->nextElement();
561
562 if (file->isDirectory())
563 {
564 recursiveDelete(file);
565 }
566 else
567 {
568 try
569 {
570 file->remove();
571 }
572 catch (vmime::exceptions::filesystem_exception&)
573 {
574 // Ignore
575 }
576 }
577 }
578
579 // Then, delete this (empty) directory
580 try
581 {
582 dir->remove();
583 }
584 catch (vmime::exceptions::filesystem_exception&)
585 {
586 // Ignore
587 }
588 }
589
590 VMIME_TEST_SUITE_END
591
592