1 /*
2 ** Copyright 2002-2004, Double Precision Inc.
3 **
4 ** See COPYING for distribution information.
5 */
6 #include "libmail_config.h"
7 #include "maildir/config.h"
8 #include "maildir/maildirmisc.h"
9 #include <courier-unicode.h>
10 #include "maildirfolder.H"
11 #include "maildiradd.H"
12 #include "mbox.H"
13 #include "misc.H"
14 #include <list>
15 #include <algorithm>
16 #include <errno.h>
17 #include <string.h>
18 #include <fcntl.h>
19 
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <iostream>
23 
24 #if HAVE_DIRENT_H
25 # include <dirent.h>
26 # define NAMLEN(dirent) strlen((dirent)->d_name)
27 #else
28 # define dirent direct
29 # define NAMLEN(dirent) (dirent)->d_namlen
30 # if HAVE_SYS_NDIR_H
31 #  include <sys/ndir.h>
32 # endif
33 # if HAVE_SYS_DIR_H
34 #  include <sys/dir.h>
35 # endif
36 # if HAVE_NDIR_H
37 #  include <ndir.h>
38 # endif
39 #endif
40 
41 using namespace std;
42 
folder(mail::maildir * maildirArg,string pathArg)43 mail::maildir::folder::folder(mail::maildir *maildirArg,
44 			      string pathArg)
45 	: mail::folder(maildirArg),
46 	  maildirAccount(maildirArg),
47 	  path(pathArg),
48 	  hasMessagesFlag(true),
49 	  hasSubfoldersFlag(true)
50 {
51 	name=pathArg;
52 
53 	size_t p=name.rfind('.');
54 
55 	if (p != std::string::npos)
56 		name=name.substr(p+1);
57 
58 	// Convert the name of the folder from modified UTF-7
59 	// (Courier compatibility) to the current charset.
60 
61 	char *s=unicode_convert_tobuf(name.c_str(),
62 					unicode_x_smap_modutf8,
63 					unicode_default_chset(),
64 					NULL);
65 
66 	if (s)
67 	{
68 		try {
69 			name=s;
70 			free(s);
71 		} catch (...) {
72 			free(s);
73 		}
74 	}
75 }
76 
~folder()77 mail::maildir::folder::~folder()
78 {
79 }
80 
sameServerAsHelperFunc()81 void mail::maildir::folder::sameServerAsHelperFunc() const
82 {
83 }
84 
getName()85 string mail::maildir::folder::getName() const
86 {
87 	if (path == "INBOX")
88 		return hasSubFolders() ? "Folders":"INBOX";
89 
90 	return name;
91 }
92 
getPath()93 string mail::maildir::folder::getPath() const
94 {
95 	return path;
96 }
97 
hasMessages()98 bool mail::maildir::folder::hasMessages() const
99 {
100 	return hasMessagesFlag;
101 }
102 
hasSubFolders()103 bool mail::maildir::folder::hasSubFolders() const
104 {
105 	return hasSubfoldersFlag;
106 }
107 
isParentOf(string otherPath)108 bool mail::maildir::folder::isParentOf(string otherPath) const
109 {
110 	string s=path + ".";
111 
112 	return (strncmp(otherPath.c_str(), s.c_str(), s.size()) == 0);
113 }
114 
hasMessages(bool flag)115 void mail::maildir::folder::hasMessages(bool flag)
116 {
117 	hasMessagesFlag=flag;
118 }
119 
hasSubFolders(bool flag)120 void mail::maildir::folder::hasSubFolders(bool flag)
121 {
122 	hasSubfoldersFlag=flag;
123 }
124 
125 class mail::maildir::indexSort {
126 public:
127 	indexSort();
128 	~indexSort();
129 
130 	bool operator()(const mail::maildir::maildirMessageInfo &a,
131 			const mail::maildir::maildirMessageInfo &b);
132 };
133 
indexSort()134 mail::maildir::indexSort::indexSort()
135 {
136 }
137 
~indexSort()138 mail::maildir::indexSort::~indexSort()
139 {
140 }
141 
142 // Sort messages in some reasonable order.  Rely on the timestamp component
143 // of the maildirfilename.
144 
operator()145 bool mail::maildir::indexSort::operator()
146 	(const mail::maildir::maildirMessageInfo &a,
147 	 const mail::maildir::maildirMessageInfo &b)
148 {
149 	unsigned long at=atol(a.lastKnownFilename.c_str()),
150 		bt=atol(b.lastKnownFilename.c_str());
151 
152 	if ( at != bt)
153 		return at < bt;
154 
155 	return strcmp(a.lastKnownFilename.c_str(),
156 		      b.lastKnownFilename.c_str()) < 0;
157 }
158 
159 // Scan a maildirAccount
160 
scan(string folderStr,vector<maildirMessageInfo> & index,bool scanNew)161 bool mail::maildir::scan(string folderStr, vector<maildirMessageInfo> &index,
162 			 bool scanNew)
163 {
164 	string p;
165 
166 	char *d=maildir_name2dir(path.c_str(), folderStr.c_str());
167 
168 	if (!d)
169 		return false;
170 
171 	try {
172 		p=d;
173 		free(d);
174 	} catch (...) {
175 		free(d);
176 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
177 	}
178 
179 	static const char * const subdirs[]={"/cur","/new"};
180 
181 	size_t i;
182 
183 	for (i=0; i<(scanNew ? 2:1); i++)
184 	{
185 		string n=p + subdirs[i];
186 
187 		DIR *dirp=opendir(n.c_str());
188 
189 		try {
190 			struct dirent *de;
191 
192 			while (dirp && (de=readdir(dirp)) != NULL)
193 			{
194 				if (de->d_name[0] == '.')
195 					continue;
196 
197 
198 				maildirMessageInfo newInfo;
199 
200 				newInfo.lastKnownFilename=de->d_name;
201 
202 				// Use the filename as the uid
203 
204 				newInfo.uid=de->d_name;
205 
206 				size_t p=newInfo.uid.find(MDIRSEP[0]);
207 
208 				if (p != std::string::npos)
209 					newInfo.uid=newInfo.uid.substr(0, p);
210 
211 				mail::maildir::updateFlags(de->d_name,
212 							   newInfo);
213 				newInfo.recent= i > 0;
214 				index.push_back(newInfo);
215 			}
216 
217 			if (dirp)
218 				closedir(dirp);
219 		} catch (...) {
220 			if (dirp)
221 				closedir(dirp);
222 			LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
223 		}
224 	}
225 
226 	sort(index.begin(), index.end(), indexSort());
227 	return true;
228 }
229 
getParentFolder(callback::folderList & callback1,callback & callback2)230 void mail::maildir::folder::getParentFolder(callback::folderList &callback1,
231 					    callback &callback2) const
232 {
233 	if (isDestroyed(callback2))
234 		return;
235 
236 	size_t n;
237 
238 	n=path.rfind('.');
239 
240 	if (n == std::string::npos)
241 		n=0;
242 
243 	maildirAccount->findFolder(path.substr(0, n),
244 				   callback1,
245 				   callback2);
246 }
247 
readFolderInfo(mail::callback::folderInfo & callback1,mail::callback & callback2)248 void mail::maildir::folder::readFolderInfo( mail::callback::folderInfo
249 					    &callback1,
250 					    mail::callback &callback2) const
251 {
252 	if (isDestroyed(callback2))
253 		return;
254 
255 	callback1.messageCount=0;
256 	callback1.unreadCount=0;
257 
258 	vector<maildirMessageInfo> dummyIndex;
259 
260 	if (!maildirAccount->scan(path, dummyIndex, true))
261 	{
262 		callback1.success();
263 		callback2.fail("Invalid folder");
264 		return;
265 	}
266 
267 	vector<maildirMessageInfo>::iterator b=dummyIndex.begin(),
268 		e=dummyIndex.end();
269 
270 	while (b != e)
271 	{
272 		callback1.messageCount++;
273 		if ( b->unread)
274 			callback1.unreadCount++;
275 		b++;
276 	}
277 
278 	callback1.success();
279 	callback2.success("OK");
280 }
281 
listinfo()282 mail::maildir::folder::listinfo::listinfo()
283 {
284 }
285 
~listinfo()286 mail::maildir::folder::listinfo::~listinfo()
287 {
288 }
289 
290 // Callback that lists maildirAccount folders.
291 // The callback filters only the folders under the list path
292 
maildir_list_callback(const char * folder,void * vp)293 void mail::maildir::folder::maildir_list_callback(const char *folder,
294 						  void *vp)
295 {
296 	mail::maildir::folder::listinfo *li=
297 		(mail::maildir::folder::listinfo *)vp;
298 
299 	if (strncmp(folder, li->path.c_str(), li->path.size()) ||
300 	    folder[li->path.size()] != '.')
301 		return; // Outside the hierarchy being listed.
302 
303 	folder += li->path.size();
304 	++folder;
305 
306 	// If the remaining portion of the name has another period, there's
307 	// a subdirectory there.  Otherwise, it's a file.
308 	// It's ok when we get multiple folders in the same subdirectory,
309 	// subdirs is a STL set, which gets rid of duplicates
310 
311 	const char *p=strchr(folder, '.');
312 
313 	if (p)
314 		li->subdirs.insert(string(folder, p));
315 	else
316 		li->list.insert(string(folder));
317 }
318 
readSubFolders(mail::callback::folderList & callback1,mail::callback & callback2)319 void mail::maildir::folder::readSubFolders( mail::callback::folderList
320 					    &callback1,
321 					    mail::callback &callback2) const
322 {
323 	if (isDestroyed(callback2))
324 		return;
325 
326 	if (path.size() == 0)
327 	{
328 		maildirAccount->readTopLevelFolders(callback1, callback2);
329 		return;
330 	}
331 
332 	listinfo li;
333 
334 	li.path=path;
335 
336 	maildir_list(maildirAccount->path.c_str(),
337 		     &mail::maildir::folder::maildir_list_callback,
338 		     &li);
339 
340 	list<mail::folder *> folderList;
341 	list<mail::folder *>::iterator b, e;
342 
343 	try {
344 		// Create a list of folder objects from the list of folder
345 		// names in listinfo.  Create a list in two passes.
346 
347 		// First pass - build names of folders.  If the folder is
348 		// also found in the subdirectory list, make it a dual-purpose
349 		// folder/directory.
350 
351 		buildFolderList(folderList, &li.list, &li.subdirs);
352 
353 		// Second pass - build remaining subdirs.
354 
355 		buildFolderList(folderList, NULL, &li.subdirs);
356 
357 		// Cleanup for the callback
358 		vector<const mail::folder *> myList;
359 
360 		b=folderList.begin();
361 		e=folderList.end();
362 
363 		while (b != e)
364 			myList.push_back(*b++);
365 
366 		callback1.success(myList);
367 		callback2.success("OK");
368 
369 	} catch (...) {
370 		b=folderList.begin();
371 		e=folderList.end();
372 
373 		while (b != e)
374 		{
375 			delete *b;
376 
377 			b++;
378 		}
379 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
380 	}
381 
382 	b=folderList.begin();
383 	e=folderList.end();
384 
385 	while (b != e)
386 	{
387 		delete *b;
388 
389 		b++;
390 	}
391 }
392 
buildFolderList(list<mail::folder * > & folderList,set<string> * folders,set<string> * dirs)393 void mail::maildir::folder::buildFolderList(list<mail::folder *> &folderList,
394 					    set<string> *folders,
395 					    set<string> *dirs) const
396 {
397 	set<string>::iterator b, e;
398 
399 	if (folders)
400 	{
401 		b=folders->begin();
402 		e=folders->end();
403 	}
404 	else
405 	{
406 		b=dirs->begin();
407 		e=dirs->end();
408 	}
409 
410 	while (b != e)
411 	{
412 		string name= *b++;
413 
414 		folder *p=new folder(maildirAccount, path + "." + name);
415 
416 		if (!p)
417 			LIBMAIL_THROW(strerror(errno));
418 
419 		try {
420 			if (folders)
421 			{
422 				p->hasMessages(true);
423 				p->hasSubFolders(false);
424 
425 				if (dirs->count(name) > 0)
426 					// Also a subdir
427 				{
428 					p->hasSubFolders(true);
429 					dirs->erase(name);
430 					// Don't add this folder when we do
431 					// a directory.
432 				}
433 			}
434 			else
435 			{
436 				p->hasMessages(false);
437 				p->hasSubFolders(true);
438 			}
439 
440 			folderList.push_back(p);
441 		} catch (...) {
442 			delete p;
443 			LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
444 		}
445 	}
446 }
447 
addMessage(mail::callback & callback)448 mail::addMessage *mail::maildir::folder::addMessage(mail::callback
449 							  &callback) const
450 {
451 	if (isDestroyed(callback))
452 		return NULL;
453 
454 	string folderPath;
455 
456 	char *p=maildir_name2dir(maildirAccount->path.c_str(), path.c_str());
457 
458 	if (!p)
459 	{
460 		callback.fail(strerror(errno));
461 		return NULL;
462 	}
463 
464 	try {
465 		folderPath=p;
466 		free(p);
467 	} catch (...) {
468 		free(p);
469 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
470 	}
471 
472 	mail::maildir::addmessage *m=new
473 		mail::maildir::addmessage(maildirAccount, folderPath, callback);
474 
475 	if (!m)
476 	{
477 		callback.fail(strerror(errno));
478 		return NULL;
479 	}
480 
481 	return m;
482 }
483 
moveMessagesTo(const vector<size_t> & messages,mail::folder * copyTo,mail::callback & callback)484 void mail::maildir::moveMessagesTo(const vector<size_t> &messages,
485 				   mail::folder *copyTo,
486 				   mail::callback &callback)
487 {
488 	sameServerFolderPtr=NULL;
489 	copyTo->sameServerAsHelperFunc();
490 
491 	if (sameServerFolderPtr == NULL)
492 	{
493 		mail::account::moveMessagesTo(messages, copyTo, callback);
494 		return;
495 	}
496 
497 	string destFolderPath;
498 
499 	char *p=maildir_name2dir(path.c_str(),
500 				 sameServerFolderPtr->path.c_str());
501 
502 	if (!p)
503 	{
504 		callback.fail(strerror(errno));
505 		return;
506 	}
507 
508 	try {
509 		destFolderPath=p;
510 		free(p);
511 	} catch (...) {
512 		free(p);
513 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
514 	}
515 
516 	vector<size_t>::const_iterator b=messages.begin(), e=messages.end();
517 
518 	while (b != e)
519 	{
520 		size_t n=*b++;
521 
522 		string messageFn=getfilename(n);
523 
524 		if (messageFn.size() > 0)
525 		{
526 			string destName= destFolderPath
527 				+ messageFn.substr(messageFn.rfind('/'));
528 
529 			rename(messageFn.c_str(), destName.c_str());
530 		}
531 	}
532 	updateFolderIndexInfo(&callback, false);
533 }
534 
createSubFolder(string name,bool isDirectory,mail::callback::folderList & callback1,mail::callback & callback2)535 void mail::maildir::folder::createSubFolder(string name, bool isDirectory,
536 					    mail::callback::folderList
537 					    &callback1,
538 					    mail::callback &callback2) const
539 {
540 	if (isDestroyed(callback2))
541 		return;
542 
543 	// The name of the folder is translated from the local charset
544 	// to modified UTF-7 (Courier-IMAP compatibility), with the following
545 	// blacklisted characters:
546 
547 	char *p=unicode_convert_tobuf(name.c_str(), unicode_default_chset(),
548 				      unicode_x_smap_modutf8, NULL);
549 
550 	if (!p)
551 	{
552 		callback2.fail(strerror(errno));
553 		return;
554 	}
555 
556 	std::string nameutf7;
557 
558 	errno=ENOMEM;
559 	try {
560 		nameutf7=p;
561 		free(p);
562 	} catch (...) {
563 		free(p);
564 		callback2.fail(strerror(errno));
565 		return;
566 	}
567 
568 	mail::maildir::folder newFolder(maildirAccount, path + "." + nameutf7);
569 
570 	newFolder.hasMessagesFlag= ! (newFolder.hasSubfoldersFlag=
571 				      isDirectory);
572 
573 	if (!newFolder.doCreate(isDirectory))
574 	{
575 		callback2.fail(strerror(errno));
576 		return;
577 	}
578 
579 	vector<const mail::folder *> folders;
580 
581 	folders.push_back(&newFolder);
582 	callback1.success( folders );
583 	callback2.success("Mail folder created");
584 }
585 
doCreate(bool isDirectory)586 bool mail::maildir::folder::doCreate(bool isDirectory) const
587 {
588 	if (isDirectory)
589 		return true; // Pretend
590 
591 	if (maildirAccount->ispop3maildrop)
592 	{
593 		errno=EPERM;
594 		return false; // POP3 maildrops don't have folders.
595 	}
596 
597 	string subdir;
598 
599 	char *d=maildir_name2dir(maildirAccount->path.c_str(), path.c_str());
600 	// Checks for name validity.
601 
602 	if (!d)
603 		return false;
604 
605 	try {
606 		subdir=d;
607 		free(d);
608 	} catch (...) {
609 		free(d);
610 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
611 	}
612 
613 	return mail::maildir::maildirmake(subdir, true);
614 }
615 
616 
maildirmake(string subdir,bool isFolder)617 bool mail::maildir::maildirmake(string subdir, bool isFolder)
618 {
619 	string nsubdir=subdir + "/new",
620 		csubdir=subdir + "/cur",
621 		tsubdir=subdir + "/tmp";
622 
623 	if (mkdir(subdir.c_str(), 0700) == 0)
624 	{
625 		if (mkdir(nsubdir.c_str(), 0700) == 0)
626 		{
627 			if (mkdir(tsubdir.c_str(), 0700) == 0)
628 			{
629 				if (mkdir(csubdir.c_str(), 0700) == 0)
630 				{
631 					if (!isFolder)
632 						return true;
633 
634 					string f=subdir +
635 						"/maildirfolder";
636 
637 					int fd=::open(f.c_str(),
638 						      O_CREAT |
639 						      O_RDWR, 0666);
640 
641 					if (fd >= 0)
642 					{
643 						close(fd);
644 						return true;
645 					}
646 					rmdir(csubdir.c_str());
647 				}
648 				rmdir(tsubdir.c_str());
649 			}
650 			rmdir(nsubdir.c_str());
651 		}
652 		rmdir(subdir.c_str());
653 	}
654 
655 	return false;
656 }
657 
create(bool isDirectory,mail::callback & callback)658 void mail::maildir::folder::create(bool isDirectory,
659 				   mail::callback &callback) const
660 {
661 	if (!doCreate(isDirectory))
662 	{
663 		callback.fail(strerror(errno));
664 	}
665 	else
666 	{
667 		callback.success("Mail folder created");
668 	}
669 }
670 
destroy(mail::callback & callback,bool destroyDir)671 void mail::maildir::folder::destroy(mail::callback &callback,
672 				    bool destroyDir) const
673 {
674 	if (isDestroyed(callback))
675 		return;
676 
677 	if (!destroyDir) // Folder directories are imaginary, cannot be nuked
678 	{
679 		string s;
680 		char *d=maildir_name2dir(maildirAccount->path.c_str(),
681 					 path.c_str());
682 		if (!d)
683 		{
684 			callback.fail(strerror(errno));
685 			return;
686 		}
687 
688 		try {
689 			s=d;
690 			free(d);
691 		} catch (...) {
692 			free(d);
693 			LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
694 		}
695 
696 		if (!mail::maildir::maildirdestroy(s))
697 		{
698 			callback.fail(strerror(errno));
699 			return;
700 		}
701 	}
702 
703 	callback.success("Mail folder deleted");
704 }
705 
renameFolder(const mail::folder * newParent,std::string newName,mail::callback::folderList & callback1,mail::callback & callback2)706 void mail::maildir::folder::renameFolder(const mail::folder *newParent,
707 					 std::string newName,
708 					 mail::callback::folderList &callback1,
709 					 mail::callback &callback2) const
710 {
711 	if (isDestroyed(callback2))
712 		return;
713 
714 	if (maildirAccount->folderPath.size() > 0)
715 	{
716 		size_t l=path.size();
717 
718 		if (strncmp(maildirAccount->folderPath.c_str(),
719 			    path.c_str(), l) == 0 &&
720 		    ((maildirAccount->folderPath.c_str())[l] == 0 ||
721 		     (maildirAccount->folderPath.c_str())[l] == '.'))
722 		{
723 			callback2.fail("Cannot RENAME currently open folder.");
724 			return;
725 		}
726 	}
727 
728 	// The name of the folder is translated from the local charset
729 	// to modified UTF-7 (Courier-IMAP compatibility), with the following
730 	// blacklisted characters:
731 
732 	char *s=unicode_convert_tobuf(newName.c_str(),
733 					unicode_default_chset(),
734 					unicode_x_smap_modutf8, NULL);
735 
736 	if (!s)
737 	{
738 		callback2.fail(strerror(errno));
739 		return;
740 	}
741 
742 	std::string nameutf7;
743 
744 	errno=ENOMEM;
745 	try {
746 		nameutf7=s;
747 		free(s);
748 	} catch (...) {
749 		free(s);
750 		callback2.fail(strerror(errno));
751 		return;
752 	}
753 
754 	mail::maildir::folder newFolder(maildirAccount,
755 					(newParent ?
756 					 newParent->getPath() + ".":
757 					 string("")) + nameutf7);
758 
759 	newFolder.hasMessages( hasMessages() );
760 	newFolder.hasSubFolders( hasSubFolders() );
761 
762 	vector<const mail::folder *> folders;
763 
764 	// Paths are INBOX.foo
765 
766 	string from, to;
767 
768 	char *p=maildir_name2dir(".", path.c_str());
769 
770 	if (p)
771 		try {
772 			from=p+2; // Skip ./
773 			free(p);
774 		} catch (...) {
775 			free(p);
776 			LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
777 		}
778 
779 	p=maildir_name2dir(".", newFolder.path.c_str());
780 	if (p)
781 		try {
782 			to=p+2; // Skip ./
783 			free(p);
784 		} catch (...) {
785 			free(p);
786 			LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
787 		}
788 
789 
790 	if (from.size() > 0 &&
791 	    to.size() > 0 &&
792 	    maildir_rename(maildirAccount->path.c_str(),
793 			   from.c_str(), to.c_str(),
794 			   MAILDIR_RENAME_FOLDER |
795 			   MAILDIR_RENAME_SUBFOLDERS, NULL))
796 	{
797 		callback2.fail(strerror(errno));
798 	}
799 	else
800 	{
801 		folders.push_back(&newFolder);
802 		callback1.success( folders );
803 		callback2.success("Mail folder renamed");
804 	}
805 }
806 
maildirdestroy(string d)807 bool mail::maildir::maildirdestroy(string d)
808 {
809 	list<string> contents;
810 
811 	DIR *dirp=opendir(d.c_str());
812 
813 	try {
814 		struct dirent *de;
815 
816 		while (dirp && (de=readdir(dirp)) != NULL)
817 		{
818 			if (strcmp(de->d_name, ".") == 0)
819 				continue;
820 			if (strcmp(de->d_name, "..") == 0)
821 				continue;
822 
823 			contents.push_back(de->d_name);
824 		}
825 
826 		if (dirp)
827 			closedir(dirp);
828 	} catch (...) {
829 		if (dirp)
830 			closedir(dirp);
831 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
832 	}
833 
834 	list<string>::iterator b=contents.begin(), e=contents.end();
835 
836 	while (b != e)
837 	{
838 		string s=d + "/" + *b++;
839 
840 		if (unlink(s.c_str()) < 0 && errno != ENOENT)
841 		{
842 			if (errno == EISDIR)
843 			{
844 				if (!maildirdestroy(s))
845 					return false;
846 				continue;
847 			}
848 			return false;
849 		}
850 	}
851 	rmdir(d.c_str());
852 	return true;
853 }
854 
clone()855 mail::folder *mail::maildir::folder::clone() const
856 {
857 	if (isDestroyed())
858 		return NULL;
859 
860 	mail::maildir::folder *p=new mail::maildir::folder(maildirAccount,
861 							   path);
862 
863 	if (p)
864 	{
865 		p->hasMessagesFlag=hasMessagesFlag;
866 		p->hasSubfoldersFlag=hasSubfoldersFlag;
867 		return p;
868 	}
869 	return NULL;
870 }
871 
872 
findFolder(string folder,mail::callback::folderList & callback1,mail::callback & callback2)873 void mail::maildir::findFolder(string folder,
874 			       mail::callback::folderList &callback1,
875 			       mail::callback &callback2)
876 {
877 	mail::maildir::folder tempFolder(this, folder);
878 
879 	vector<const mail::folder *> folderList;
880 
881 	folderList.push_back(&tempFolder);
882 
883 	callback1.success(folderList);
884 	callback2.success("OK");
885 }
886 
translatePath(string path)887 string mail::maildir::translatePath(string path)
888 {
889 	return mail::mbox::translatePathCommon(path, ".");
890 }
891 
encword(string s)892 static string encword(string s)
893 {
894 	string r="";
895 
896 	string::iterator b=s.begin(), e=s.end();
897 	string::iterator p=b;
898 
899 	while ( b != e )
900 	{
901 		if ( *b == ':' || *b == '\\')
902 		{
903 			r.insert(r.end(), p, b);
904 			r += "\\";
905 			p=b;
906 		}
907 		b++;
908 	}
909 
910 	r.insert(r.end(), p, b);
911 	return r;
912 }
913 
914 
getword(string & s)915 static string getword(string &s)
916 {
917 	string r="";
918 
919 	string::iterator b=s.begin(), e=s.end(), p=b;
920 
921 	while (b != e)
922 	{
923 		if (*b == ':')
924 			break;
925 
926 		if (*b == '\\')
927 		{
928 			r.insert(r.end(), p, b);
929 
930 			b++;
931 			p=b;
932 
933 			if (b == e)
934 				break;
935 		}
936 		b++;
937 	}
938 
939 	r.insert(r.end(), p, b);
940 
941 	if (b != e)
942 	{
943 		b++;
944 		s=string(b, s.end());
945 	}
946 
947 	return r;
948 }
949 
toString()950 string mail::maildir::folder::toString() const
951 {
952 	return encword(path) + ":" + encword(name) + ":" +
953 		(hasMessagesFlag ? "M":"") +
954 		(hasSubfoldersFlag ? "S":"");
955 }
956 
957 
folderFromString(string folderName)958 mail::folder *mail::maildir::folderFromString(string folderName)
959 {
960 	string path=getword(folderName);
961 	string name=getword(folderName);
962 
963 	mail::maildir::folder *f=new mail::maildir::folder(this, path);
964 
965 	if (!f)
966 		return NULL;
967 
968 	f->hasMessagesFlag= folderName.find('M') != std::string::npos;
969 	f->hasSubfoldersFlag= folderName.find('S') != std::string::npos;
970 
971 	return f;
972 }
973 
open(mail::callback & openCallback,mail::snapshot * restoreSnapshot,mail::callback::folder & folderCallback)974 void mail::maildir::folder::open(mail::callback &openCallback,
975 				 mail::snapshot *restoreSnapshot,
976 				 mail::callback::folder &folderCallback) const
977 {
978 	if (isDestroyed(openCallback))
979 		return;
980 
981 	maildirAccount->open(path, openCallback, folderCallback);
982 }
983 
984 
readTopLevelFolders(mail::callback::folderList & callback1,mail::callback & callback2)985 void mail::maildir::readTopLevelFolders(mail::callback::folderList &callback1,
986 					mail::callback &callback2)
987 {
988 	mail::maildir::folder inbox(this, INBOX);
989 	mail::maildir::folder folders(this, INBOX);
990 
991 	inbox.hasSubfoldersFlag=false;
992 	folders.hasMessagesFlag=false;
993 
994 	vector<const mail::folder *> folderList;
995 
996 	folderList.push_back(&inbox);
997 
998 	if (!ispop3maildrop)
999 		folderList.push_back(&folders);
1000 
1001 	callback1.success(folderList);
1002 	callback2.success("OK");
1003 }
1004