1 /*
2 ** Copyright 2003-2011, Double Precision Inc.
3 **
4 ** See COPYING for distribution information.
5 */
6 #include "config.h"
7 #include "myfolder.H"
8 #include "myfolderfilter.H"
9 #include "myserver.H"
10 #include "mymessage.H"
11 #include "tags.H"
12 #include "cursesmessage.H"
13 #include "myservercallback.H"
14 #include "gettext.H"
15 #include "curses/cursesstatusbar.H"
16 #include "rfc822/rfc822.h"
17 #include "rfc2045/rfc2045.h"
18 #include "libmail/rfc2047decode.H"
19 #include <courier-unicode.h>
20 #include "libmail/envelope.H"
21 #include "libmail/rfcaddr.H"
22 #include "libmail/autodecoder.H"
23 
24 #include <errno.h>
25 
26 #include <algorithm>
27 #include <sstream>
28 #include <fstream>
29 #include <iomanip>
30 #include <iostream>
31 
32 using namespace std;
33 
34 extern CursesStatusBar *statusBar;
35 extern void folderIndexScreen(void *);
36 extern void showCurrentMessage(void *);
37 extern void showAttachments(void *);
38 extern void editScreen(void *);
39 
40 #define SORT_THREAD 'T'
41 #define SORT_THREAD_S "T"
42 #define SNAPSHOTFORMAT "1" // Current format for saved indexes.
43 
44 
45 //////////////////////////////////////////////////////////////////////////////
46 //
47 // Helper class for threading.
48 
49 class myFolder::thread {
50 
51 public:
52 
53 	static void resort(myFolder &f);
54 
55 	static void resort(myFolder &f,	struct imap_refmsgtable *t);
56 
57 	static void resort_layout(myFolder &,
58 				  struct imap_refmsg *,
59 				  struct imap_refmsg *&,
60 
61 				  vector<size_t> &,
62 				  size_t);
63 
64 	static void resort_layout(myFolder &,
65 				  struct imap_refmsg *,
66 				  vector<size_t> &,
67 				  size_t, bool);
68 
69 };
70 
71 
72 
73 /////////////////////////////////////////////////////////////////////
74 //
75 // Helper class for restoring saved snapshots
76 
77 
RestoreSnapshot(myFolder * mf)78 myFolder::RestoreSnapshot::RestoreSnapshot(myFolder *mf)
79 	: snapshotId(""), nmessages(0)
80 {
81 	cachefile=mf->getServer()
82 		->getFolderConfiguration(mf->getFolder(), "SNAPSHOT");
83 
84 	if (cachefile.size() == 0) // No cache file defined.
85 		return;
86 
87 	cachefile=myServer::getConfigDir() + "/" + cachefile;
88 
89 	i.open(cachefile.c_str());
90 
91 	if (!i.is_open())
92 		return;
93 
94 	string line;
95 
96 	if (getline(i, line).fail())
97 	{
98 		i.close();
99 		return;
100 	}
101 
102 	if (line.substr(0, sizeof(SNAPSHOTFORMAT ":")-1) != SNAPSHOTFORMAT ":")
103 	{
104 		i.close();
105 		return;
106 	}
107 
108 	line=line.substr(sizeof(SNAPSHOTFORMAT ":")-1);
109 
110 	size_t n=line.find(':');
111 
112 	if (n == std::string::npos)
113 	{
114 		i.close();
115 		return;
116 	}
117 
118 	snapshotId=line.substr(n+1);
119 
120 	istringstream ii(line);
121 
122 	ii >> nmessages;
123 
124 	if (ii.fail() || ii.bad())
125 	{
126 		i.close();
127 		return;
128 	}
129 
130 }
131 
~RestoreSnapshot()132 myFolder::RestoreSnapshot::~RestoreSnapshot()
133 {
134 }
135 
getSnapshotInfo(string & snapshotIdArg,size_t & nMessagesArg)136 void myFolder::RestoreSnapshot::getSnapshotInfo(string &snapshotIdArg,
137 						size_t &nMessagesArg)
138 {
139 	if (i.is_open())
140 	{
141 		snapshotIdArg=snapshotId;
142 		nMessagesArg=nmessages;
143 	}
144 	else
145 	{
146 		snapshotIdArg="";
147 		nMessagesArg=0;
148 	}
149 }
150 
restoreSnapshot(mail::snapshot::restore & r)151 void myFolder::RestoreSnapshot::restoreSnapshot(mail::snapshot::restore &r)
152 {
153 	if (!i.is_open())
154 	{
155 		r.abortRestore();
156 		return;
157 	}
158 
159 	size_t n;
160 
161 	string l;
162 
163 	for (n=0; n<nmessages; n++)
164 	{
165 		if (getline(i, l).fail())
166 		{
167 			cerr << (string)
168 				( Gettext(_("%1%: unable to read snapshot."))
169 				  << cachefile) << endl;
170 			r.abortRestore();
171 			return;
172 		}
173 
174 		r.restoreIndex(n, mail::messageInfo(l));
175 	}
176 
177 	while (!getline(i, l).fail())
178 	{
179 		{
180 			istringstream is(l);
181 
182 			is >> n;
183 
184 			if (is.fail() || n >= nmessages)
185 			{
186 				cerr << (string)
187 					( Gettext(_("%1%: unable to read"
188 						    " snapshot."))
189 					  << cachefile) << endl;
190 				r.abortRestore();
191 				return;
192 			}
193 		}
194 
195 		set<string> kwset;
196 
197 		for (;;)
198 		{
199 			if (getline(i, l).fail())
200 			{
201 				cerr << (string)
202 					( Gettext(_("%1%: unable to read"
203 						    " snapshot."))
204 					  << cachefile) << endl;
205 				r.abortRestore();
206 				return;
207 			}
208 
209 			if (l.size() == 0)
210 				break;
211 
212 			kwset.insert(l);
213 		}
214 
215 		if (kwset.empty())
216 			continue;
217 
218 		r.restoreKeywords(n, kwset);
219 	}
220 }
221 
222 /////////////////////////////////////////////////////////////////////
223 //
224 // Helper class for reading the message index.
225 //
226 // When newMessages() notification is received, this class is started,
227 // and a request is made to download the headers from all new messages.
228 // When the request completes, the new messages are shown on the screen.
229 
230 class myFolder::FolderIndexUpdate : public mail::callback::message {
231 
232 	void reportProgress(size_t bytesCompleted,
233 			    size_t bytesEstimatedTotal,
234 
235 			    size_t messagesCompleted,
236 			    size_t messagesEstimatedTotal);
237 
238 public:
239 	mail::ptr<myFolder> folder;
240 	mail::callback &successOrFail;
241 	size_t totalCount;
242 	size_t doneCount;
243 
244 	FolderIndexUpdate(myFolder &folderArg,
245 			  mail::callback &successOrFailArg);
246 	~FolderIndexUpdate();
247 
248 	// Inherited from mail::callback::message
249 
250 	void messageEnvelopeCallback(size_t messageNumber,
251 				     const class mail::envelope
252 				     &envelope);
253 
254 	void messageReferencesCallback(size_t messageNumber,
255 				       const std::vector<std::string>
256 				       &references);
257 
258 	void messageArrivalDateCallback(size_t messageNumber,
259 					time_t datetime);
260 
261 	void messageSizeCallback(size_t messageNumber,
262 				 unsigned long size);
263 
264 
265 	// Inherited from mail::callback
266 
267 	void success(string);
268 	void fail(string);
269 };
270 
271 /////////////////////////////////////////////////////////////////////
272 //
273 // Helper class for updating the message index.
274 
275 class myFolder::NewMailUpdate : public mail::callback {
276 
277 	void reportProgress(size_t bytesCompleted,
278 			    size_t bytesEstimatedTotal,
279 
280 			    size_t messagesCompleted,
281 			    size_t messagesEstimatedTotal);
282 
283 public:
284 	myFolder::FolderIndexUpdate *update_struct;
285 
286 	NewMailUpdate();
287 	~NewMailUpdate();
288 
289 	void success(string);
290 	void fail(string);
291 };
292 
293 
myFolder(myServer * serverArg,const mail::folder * folderArg)294 myFolder::myFolder(myServer *serverArg,
295 		   const mail::folder *folderArg)
296 	: PreviousScreen(&folderIndexScreen, this),
297 	  folder(NULL), server(serverArg),
298 	  isClosing(false),
299 	  saveFirstRowShown(0),
300 	  isExpungingDrafts(false),
301 	  mustReopen(false),
302 	  currentFilter(NULL),
303 	  currentMessage(0), mymessage(0),
304 	  currentDisplay(NULL), expunge_count(0), newMailCheckCount(0),
305 	  sort_function(&myFolder::sortByArrival)
306 {
307 	expungedTimer=this;
308 	expungedTimer= &myFolder::unsolicitedExpunge;
309 	setSortFunctionNoSort("A");
310 	if ((folder=folderArg->clone()) == NULL)
311 		outofmemory();
312 }
313 
~myFolder()314 myFolder::~myFolder()
315 {
316 	if (currentDisplay)
317 		currentDisplay->f=NULL;
318 
319 	if (currentFilter)
320 		currentFilter->folder=NULL;
321 
322 	if (mymessage)
323 	{
324 		CursesMessage *m=mymessage;
325 
326 		mymessage=NULL;
327 
328 		m->myfolder=NULL;
329 		delete m;
330 	}
331 
332 	if (folder)
333 		delete folder;
334 }
335 
336 
isCheckingNewMail()337 bool myFolder::isCheckingNewMail()
338 {
339 	return newMailCheckCount > 0 || currentFilter != NULL;
340 }
341 
quiesce()342 void myFolder::quiesce()
343 {
344 	bool showingStatus=false;
345 
346 	mail::ptr<myFolder> folderPtr=this;
347 
348 	while (!folderPtr.isDestroyed() && isCheckingNewMail())
349 	{
350 		if (!showingStatus)
351 		{
352 			showingStatus=true;
353 			statusBar->clearstatus();
354 			statusBar->status(_("Closing folder..."));
355 		}
356 
357 		myServer::Callback cb;
358 
359 		checkNewMail(cb);
360 
361 		myServer::eventloop(cb);
362 	}
363 }
364 
365 // New messages arrived in folder.
366 
newMessages()367 void myFolder::newMessages()
368 {
369 	if (isClosing)
370 		return;
371 
372 	size_t currentMessageCount=
373 		server->server->getFolderIndexSize();
374 
375 	if (serverIndex.size() < currentMessageCount)
376 	{
377 		vector<size_t> newmessages;
378 
379 		size_t n;
380 
381 		for (n=serverIndex.size(); n<currentMessageCount; n++)
382 			newmessages.push_back(n);
383 
384 		serverIndex.insert(serverIndex.end(),
385 				   currentMessageCount - serverIndex.size(),
386 				   Index());
387 
388 		NewMailUpdate *new_mail_update=new NewMailUpdate();
389 
390 		if (!new_mail_update)
391 			outofmemory();
392 
393 		try {
394 			FolderIndexUpdate *update_index=
395 				new FolderIndexUpdate(*this,
396 						      *new_mail_update);
397 
398 			if (!update_index)
399 				outofmemory();
400 
401 			new_mail_update->update_struct=update_index;
402 
403 			try {
404 				++newMailCheckCount;
405 
406 				update_index->totalCount=newmessages.size();
407 
408 				server->server->
409 					readMessageAttributes(newmessages,
410 							      mail::account::ENVELOPE |
411 							      mail::account::MESSAGESIZE |
412 							      mail::account::ARRIVALDATE,
413 							       *update_index);
414 			} catch (...) {
415 				--newMailCheckCount;
416 				delete update_index;
417 				LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
418 			}
419 		} catch (...) {
420 			delete new_mail_update;
421 			LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
422 		}
423 	}
424 
425 }
426 
427 //
428 // When the folder update request completes, newMessagesReceived() is invoked
429 // to process the new messages.
430 
newMessagesReceived()431 void myFolder::newMessagesReceived()
432 {
433 	if (--newMailCheckCount)
434 		return; // More updates in progress.
435 
436 	if (currentFilter)
437 		return; // Filtering is in progress
438 
439 	if (installFilter() != NULL) // This folder has a filter
440 	{
441 		currentFilter->filterStart=sorted_index.size();
442 		currentFilter->filterEnd=serverIndex.size();
443 		currentFilter->start();
444 		return;
445 	}
446 
447 	newMessagesReceivedAndFiltered(serverIndex.size());
448 }
449 
450 // myFolderFilter completed its task.
451 //
452 // A) No new messages received in the meantime.
453 // B) New messages received, header fetching is in progress
454 // C) New messages received, all header fetching is complete
455 
messagesFiltered()456 void myFolder::messagesFiltered()
457 {
458 	if (newMailCheckCount)
459 	{
460 		// B).  Just show what's been filtered so far,
461 		// newMailReceived() will be called again, and we'll resume
462 		// at that time.
463 
464 		size_t newMessages=currentFilter->filterEnd;
465 
466 		delete currentFilter;
467 
468 		newMessagesReceivedAndFiltered(newMessages);
469 		return;
470 	}
471 
472 	if (currentFilter->filterEnd == serverIndex.size())
473 	{
474 		// A).
475 
476 		delete currentFilter;
477 		newMessagesReceivedAndFiltered(serverIndex.size());
478 		return;
479 	}
480 
481 	// C).
482 
483 	currentFilter->filterStart=currentFilter->filterEnd;
484 	currentFilter->filterEnd=serverIndex.size();
485 	currentFilter->start();
486 }
487 
newMessagesReceivedAndFiltered(size_t newMessages)488 void myFolder::newMessagesReceivedAndFiltered(size_t newMessages)
489 {
490 	size_t oldMessages=sorted_index.size();
491 
492 	if (oldMessages > newMessages)
493 		LIBMAIL_THROW("Internal error in newMessagesReceivedAndFiltered");
494 	size_t newMessageCount = newMessages - oldMessages;
495 
496 	if (sort_function_name.find(SORT_THREAD)
497 	    != std::string::npos)
498 	{
499 		while (oldMessages < newMessages)
500 		{
501 			mail::messageInfo flags=server->server->
502 				getFolderIndexInfo(oldMessages);
503 
504 			Index &n=serverIndex[oldMessages];
505 
506 			n.uid=flags.uid;
507 			n.setStatusCode(flags);
508 
509 			{
510 				set<string> keywords;
511 
512 				server->server->
513 					getFolderKeywordInfo(oldMessages,
514 							     keywords);
515 
516 				n.setTag(keywords);
517 			}
518 
519 			sorted_index.push_back(oldMessages);
520 			++oldMessages;
521 		}
522 
523 		// No clean way to do it, except another sort.
524 		setSortFunctionNoSave(sort_function_name);
525 	}
526 	else
527 	{
528 		IndexSort cmpFunc(*this);
529 
530 
531 		while (oldMessages < newMessages)
532 		{
533 			mail::messageInfo flags=server->server->
534 				getFolderIndexInfo(oldMessages);
535 
536 			Index &n=serverIndex[oldMessages];
537 
538 			n.uid=flags.uid;
539 			n.setStatusCode(flags);
540 
541 			{
542 				set<string> keywords;
543 
544 				server->server->
545 					getFolderKeywordInfo(oldMessages,
546 							     keywords);
547 
548 				n.setTag(keywords);
549 			}
550 
551 			vector<size_t>::iterator insertAt
552 				=lower_bound(sorted_index.begin(),
553 					     sorted_index.end(),
554 					     oldMessages, cmpFunc);
555 
556 			if (currentMessage >=
557 			    (size_t)(insertAt - sorted_index.begin()) &&
558 			    currentMessage < sorted_index.size())
559 				++currentMessage;
560 
561 			sorted_index.insert(insertAt, oldMessages);
562 			++oldMessages;
563 		}
564 	}
565 
566 	if (mymessage)
567 		mymessage->folderResorted();
568 
569 	if (currentDisplay)
570 		currentDisplay->draw();
571 
572 	if (newMessageCount > 0)
573 	{
574 		getServer()->saveFolderIndex( this );
575 
576 		if (!isExpungingDrafts) // Suppress message
577 		{
578 			statusBar->status(Gettext(_("%1% new message(s) in %2%"))
579 					  << newMessageCount
580 					  << folder->getName());
581 			statusBar->beepError();
582 		}
583 	}
584 }
585 
586 //
587 // After messages are removed from the folder, a number of things must
588 // be recomputed.  Here we go...
589 //
590 
messagesRemoved(vector<pair<size_t,size_t>> & removedList)591 void myFolder::messagesRemoved(vector< pair<size_t, size_t> > &removedList)
592 {
593 	vector< pair<size_t, size_t> >::iterator rb, re;
594 
595 	rb=removedList.begin();
596 	re=removedList.end();
597 
598 	while (rb != re)
599 	{
600 		--re;
601 
602 		if (re->first >= serverIndex.size())
603 			continue; /* Huh??? */
604 
605 		size_t nfrom=re->first;
606 		size_t nto=re->second;
607 
608 		if (nfrom >= serverIndex.size())
609 			continue; // Huh?
610 
611 		if (nto >= serverIndex.size())
612 			nto=serverIndex.size()-1;
613 
614 		// Erase native messages nfrom-nto
615 
616 		serverIndex.erase(serverIndex.begin()+nfrom,
617 				  serverIndex.begin()+nto+1);
618 
619 		// Now go through the index of messages in sorted order,
620 		// and make the necessary adjustments.
621 
622 		size_t i;
623 
624 		for (i=0; i<sorted_index.size(); )
625 		{
626 			if (sorted_index[i] >= nfrom &&
627 			    sorted_index[i] <= nto) // This msg is gone.
628 			{
629 				sorted_index.erase(sorted_index.begin()+i,
630 						   sorted_index.begin()+i+1);
631 
632 				if (currentMessage == i && mymessage != NULL)
633 				{
634 					statusBar->status(_("Currently opened "
635 							    "message deleted "
636 							    "on the server."),
637 							  statusBar
638 							  ->SERVERERROR);
639 					statusBar->beepError();
640 
641 					if (Curses::keepgoing)
642 					{
643 						Curses::keepgoing=false;
644 						myServer::nextScreen=
645 							&folderIndexScreen;
646 						myServer::nextScreenArg=this;
647 					}
648 
649 					delete mymessage;
650 				}
651 
652 				if (currentMessage >= i)
653 				{
654 					if (currentMessage > 0)
655 					{
656 						--currentMessage;
657 						if (mymessage)
658 							mymessage->messagesortednum
659 								= currentMessage;
660 					}
661 				}
662 				continue;
663 			}
664 
665 			if (sorted_index[i] > nto)
666 				sorted_index[i] -= nto - nfrom + 1;
667 			i++;
668 		}
669 
670 		if (mymessage && nto <= mymessage->messagenum)
671 			mymessage->messagenum -= nto - nfrom + 1;
672 
673 		size_t expunge_count_temp=nto - nfrom + 1;
674 
675 		// If there's a filter running, update the filtering range.
676 
677 		if (currentFilter)
678 		{
679 			size_t t= nto+1;
680 
681 			if (t > currentFilter->filterEnd)
682 				t=currentFilter->filterEnd;
683 
684 			if (t > currentFilter->filterStart &&
685 			    nfrom < currentFilter->filterEnd)
686 			{
687 				t -= currentFilter->filterStart;
688 				if (nfrom > currentFilter->filterStart)
689 					t -= nfrom
690 						- currentFilter->filterStart;
691 
692 				currentFilter->filterEnd -= t;
693 
694 				expunge_count_temp -= t;
695 				// DO NOT COUNT filtered messages that were
696 				// expunged!  We don't need to know about them.
697 			}
698 
699 			t=nto+1;
700 
701 			if (t > currentFilter->filterStart)
702 				t=currentFilter->filterStart;
703 
704 			if (nfrom < currentFilter->filterStart)
705 			{
706 				t -= nfrom;
707 
708 				currentFilter->filterStart -= t;
709 				currentFilter->filterEnd -= t;
710 			}
711 		}
712 
713 		expunge_count += expunge_count_temp;
714 	}
715 
716 	if (mymessage)
717 		mymessage->folderResorted();
718 
719 	if (mymessage && myMessage::FolderUpdateCallback::currentCallback)
720 		myMessage::FolderUpdateCallback::currentCallback
721 			->folderUpdated();
722 
723 	if (myServer::cmdcount == 0 // Not in command, must be unsolicited
724 	    && expunge_count)
725 		// Do not start the timer if only filtered msgs were expunged
726 	{
727 		struct timeval tv;
728 
729 		tv.tv_sec=0;
730 		tv.tv_usec=100000;
731 		expungedTimer.setTimer(tv);
732 	}
733 }
734 
735 //
736 // When a timer after an unsolicited expunge msg goes off (timer is used
737 // to combine multiple unsolicited events into one event), unsolicitedExpunge()
738 // gets invoked.
739 
unsolicitedExpunge()740 void myFolder::unsolicitedExpunge()
741 {
742 	checkExpunged();
743 }
744 
setTag(size_t n,size_t tag)745 void myFolder::setTag(size_t n, size_t tag)
746 {
747 	if (n >= size())
748 		return;
749 
750 	vector<size_t> v;
751 
752 	v.push_back(getServerIndex(n));
753 	setTag(v, tag);
754 }
755 
setTag(vector<size_t> & v,size_t tag)756 void myFolder::setTag(vector<size_t> &v, size_t tag)
757 {
758 	myServer *s=getServer();
759 
760 	if (s && s->server && tag < Tags::tags.names.size())
761 	{
762 		set<string> uids;
763 		size_t i;
764 
765 		for (i=0; i<v.size(); i++)
766 		{
767 			if (v[i] >= s->server->getFolderIndexSize())
768 				continue;
769 
770 			uids.insert(s->server->getFolderIndexInfo(v[i]).uid);
771 		}
772 
773 		set<string> kSet;
774 
775 		for (i=1; i<Tags::tags.names.size(); i++)
776 			if (i != tag)
777 				kSet.insert(Tags::tags.getTagName(i));
778 
779 		{
780 			myServer::Callback cb;
781 
782 			s->server->updateKeywords(v, kSet, true, false, cb);
783 
784 			if (!myServer::eventloop(cb) ||
785 			    !s->server)
786 				return;
787 		}
788 
789 		if (tag == 0)
790 			return;
791 
792 		kSet.clear();
793 		kSet.insert(Tags::tags.getTagName(tag));
794 		v.clear();
795 
796 		size_t n=s->server->getFolderIndexSize();
797 
798 		for (i=0; i<n; i++)
799 			if (uids.count(s->server->getFolderIndexInfo(i).uid)
800 			    > 0)
801 				v.push_back(i);
802 
803 		if (v.size() > 0)
804 		{
805 			myServer::Callback cb;
806 
807 			s->server->updateKeywords(v, kSet, true, true, cb);
808 
809 			myServer::eventloop(cb);
810 		}
811 	}
812 }
813 
markDeleted(size_t n,bool isDeleted,bool setStatusFlag)814 void myFolder::markDeleted(size_t n, bool isDeleted, bool setStatusFlag)
815 {
816 	if (n >= size())
817 		return;
818 
819 	size_t sIndex=getServerIndex(n);
820 
821 	myServer *s=getServer();
822 
823 	if (s && s->server)
824 	{
825 		mail::messageInfo msgInfo=
826 			s->server->getFolderIndexInfo(sIndex);
827 
828 		if (!isDeleted && !msgInfo.deleted)
829 			msgInfo.unread=true;
830 
831 		msgInfo.deleted=isDeleted;
832 
833 		statusBar->clearstatus();
834 		if (setStatusFlag)
835 		{
836 			statusBar->status(isDeleted
837 					  ? _("Deleting message...")
838 					  : _("Undeleting message..."));
839 		}
840 
841 		DelUndelCallback *cb= new DelUndelCallback;
842 
843 		if (!cb)
844 			outofmemory();
845 
846 		try {
847 			s->server->saveFolderIndexInfo(sIndex, msgInfo, *cb);
848 		} catch (...) {
849 			delete cb;
850 			LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
851 		}
852 	}
853 }
854 
watch(size_t n,unsigned nDays,unsigned nLevels)855 void myFolder::watch(size_t n, unsigned nDays, unsigned nLevels)
856 {
857 	if (n >= size())
858 		return;
859 
860 	setWatch(sorted_index[n], nDays, nLevels);
861 	watchUpdated();
862 	if (currentDisplay)
863 		currentDisplay->draw(n);
864 }
865 
setWatch(size_t n,unsigned nDays,unsigned nLevels)866 void myFolder::setWatch(size_t n, unsigned nDays, unsigned nLevels)
867 {
868 	Index &i=serverIndex[n];
869 
870 	i.watchLevel=nLevels;
871 	watchList(i.messageid, time(NULL) + nDays * 24 * 60 * 60, nLevels);
872 }
873 
unwatch(size_t n)874 void myFolder::unwatch(size_t n)
875 {
876 	if (n >= size())
877 		return;
878 
879 	setUnwatch(sorted_index[n]);
880 	watchUpdated();
881 	if (currentDisplay)
882 		currentDisplay->draw(n);
883 }
884 
setUnwatch(size_t n)885 void myFolder::setUnwatch(size_t n)
886 {
887 	Index &i=serverIndex[n];
888 
889 	i.watchLevel=0;
890 	watchList.unWatch(i.messageid);
891 }
892 
watchUpdated()893 void myFolder::watchUpdated()
894 {
895 	statusBar->clearstatus();
896 	statusBar->status(_("Updating watch list..."));
897 	statusBar->flush();
898 	getServer()->saveFolderIndex(this);
899 
900 	statusBar->status(_("... done"));
901 }
902 
toggleMark(size_t n)903 void myFolder::toggleMark(size_t n)
904 {
905 	if (n >= size())
906 		return;
907 
908 	size_t sIndex=getServerIndex(n);
909 
910 	myServer *s=getServer();
911 
912 	if (s && s->server)
913 	{
914 		mail::messageInfo msgInfo=
915 			s->server->getFolderIndexInfo(sIndex);
916 
917 		msgInfo.marked= !msgInfo.marked;
918 
919 		statusBar->clearstatus();
920 		statusBar->status(msgInfo.marked
921 				  ? _("Marking message...")
922 				  : _("Unmarking message..."));
923 
924 		DelUndelCallback *cb= new DelUndelCallback;
925 
926 		if (!cb)
927 			outofmemory();
928 
929 		try {
930 			s->server->saveFolderIndexInfo(sIndex, msgInfo, *cb);
931 		} catch (...) {
932 			delete cb;
933 			LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
934 		}
935 	}
936 }
937 
checkExpunged()938 void myFolder::checkExpunged()
939 {
940 	size_t save_expunge_count;
941 
942 	if ((save_expunge_count=expunge_count) == 0)
943 		return;
944 
945 	expunge_count=0;
946 
947 	if (currentDisplay == NULL || isExpungingDrafts)
948 		return;
949 
950 	if (save_expunge_count > 0 &&
951 	    sort_function_name.find(SORT_THREAD)
952 	    != std::string::npos)
953 	{
954 		// No clean way to do it, except another sort.
955 		setSortFunctionNoSave(sort_function_name);
956 	}
957 
958 	currentDisplay->draw();
959 	statusBar->status( Gettext(_("%1% message(s) deleted in %2%"))
960 			   << save_expunge_count << folder->getName(),
961 			   statusBar->EXPUNGED);
962 	statusBar->beepError();
963 }
964 
messageChanged(size_t n)965 void myFolder::messageChanged(size_t n)
966 {
967 	size_t i;
968 
969 	if (n >= serverIndex.size())
970 		return; // Huh?
971 
972 	mail::messageInfo flags=server->server->getFolderIndexInfo(n);
973 
974 	serverIndex[n].setStatusCode(flags);
975 
976 	set<string> keywords;
977 
978 	server->server->getFolderKeywordInfo(n, keywords);
979 	serverIndex[n].setTag(keywords);
980 
981 	for (i=0; i<sorted_index.size(); i++)
982 		if (sorted_index[i] == n)
983 		{
984 			if (currentDisplay)
985 				currentDisplay->draw(i);
986 			break;
987 		}
988 
989 	if (mymessage)
990 	{
991 		mymessage->folderResorted();
992 		if (myMessage::FolderUpdateCallback::currentCallback)
993 			myMessage::FolderUpdateCallback::currentCallback
994 				->folderUpdated();
995 	}
996 }
997 
saveSnapshot(string snapshotId)998 void myFolder::saveSnapshot(string snapshotId)
999 {
1000 	if (!server->server)
1001 		return; // Sanity check
1002 
1003 	string filename=server->getCachedFilename(this, "SNAPSHOT");
1004 	string tmpfilename=filename + ".tmp";
1005 
1006 	unlink(tmpfilename.c_str());
1007 
1008 	ofstream o(tmpfilename.c_str());
1009 
1010 	if (o.is_open())
1011 	{
1012 		size_t n=server->server->getFolderIndexSize();
1013 
1014 		o << SNAPSHOTFORMAT ":" << n << ":" << snapshotId << endl;
1015 
1016 		size_t i;
1017 
1018 		for (i=0; i<n; i++)
1019 			o << (string)server->server->getFolderIndexInfo(i)
1020 			  << endl;
1021 
1022 		for (i=0; i<n; i++)
1023 		{
1024 			set<string> keywSet;
1025 
1026 			server->server->getFolderKeywordInfo(i, keywSet);
1027 
1028 			set<string>::iterator b=keywSet.begin(),
1029 				e=keywSet.end();
1030 			bool hasKw=false;
1031 
1032 			while (b != e)
1033 			{
1034 				if ( b->find('\n') == b->npos )
1035 				{
1036 					if (!hasKw)
1037 						o << i << endl;
1038 					hasKw=true;
1039 
1040 					o << *b << endl;
1041 				}
1042 				++b;
1043 			}
1044 
1045 			if (hasKw)
1046 				o << endl;
1047 		}
1048 
1049 		o << flush;
1050 
1051 		o.close();
1052 
1053 		if (!o.fail() && !o.bad() && rename(tmpfilename.c_str(),
1054 						    filename.c_str()) == 0)
1055 			return;
1056 		unlink(tmpfilename.c_str());
1057 	}
1058 	statusBar->clearstatus();
1059 	statusBar->status(Gettext(_("%1%: %2%")) << tmpfilename
1060 			  << strerror(errno));
1061 	statusBar->beepError();
1062 }
1063 
1064 /////////////////////////////////////////////////////////////////////
1065 //
1066 // The folder is opened
1067 //
1068 
init()1069 bool myFolder::init()
1070 {
1071 	vector<size_t> msgindex;
1072 
1073 	size_t n=server->server->getFolderIndexSize();
1074 
1075 	size_t i;
1076 
1077 	sorted_index.resize(n);
1078 
1079 	for (i=0; i<n; i++)
1080 		sorted_index[i]=i;
1081 
1082 	statusBar->clearstatus();
1083 	statusBar->status(_("Reading headers..."));
1084 
1085 	// See what we can grab from the cache.
1086 
1087 	bool save_flag;
1088 
1089 	serverIndex.clear();
1090 	serverIndex.insert(serverIndex.end(), n, Index());
1091 
1092 	{
1093 		map<string, size_t> uid_list;
1094 
1095 		for (i=0; i<n; i++)
1096 		{
1097 			mail::messageInfo flags=server->server->
1098 				getFolderIndexInfo(i);
1099 
1100 			Index &n=serverIndex[i];
1101 
1102 			n.uid=flags.uid;
1103 			n.setStatusCode(flags);
1104 
1105 			{
1106 				set<string> keywords;
1107 
1108 				server->server->getFolderKeywordInfo(i,
1109 								     keywords);
1110 				n.setTag(keywords);
1111 			}
1112 
1113 			uid_list.insert(make_pair(server->server
1114 						  ->getFolderIndexInfo(i)
1115 						  .uid, i));
1116 		}
1117 
1118 		save_flag=loadFolderIndex(uid_list);
1119 
1120 		map<string, size_t>::iterator
1121 			b=uid_list.begin(), e=uid_list.end();
1122 
1123 		while (b != e)
1124 			msgindex.push_back( (*b++).second);
1125 	}
1126 
1127 
1128 	myServer::Callback callback;
1129 
1130 	FolderIndexUpdate index_update(*this, callback);
1131 
1132 	if (msgindex.size() > 0)
1133 	{
1134 		index_update.totalCount=msgindex.size();
1135 		server->server->readMessageAttributes(msgindex,
1136 						      mail::account::ENVELOPE |
1137 						      mail::account::MESSAGESIZE |
1138 						      mail::account::ARRIVALDATE,
1139 						      index_update);
1140 
1141 		if (!myServer::eventloop(callback))
1142 			return false;
1143 
1144 		save_flag=true;
1145 	}
1146 
1147 	statusBar->clearstatus();
1148 	statusBar->status(Gettext(_("%1%: %2% messages"))
1149 			  << folder->getName() << n);
1150 
1151 	// Remember previous sort order.
1152 
1153 	string sortBy=getServer()
1154 		->getFolderConfiguration(getFolder(), "SORT");
1155 
1156 	if (sortBy.size() > 0)
1157 		setSortFunctionNoSort(sortBy);
1158 	resort();
1159 
1160 	currentMessage=0;
1161 	for (i=0; i<n; i++)
1162 		if (server->server->getFolderIndexInfo(sorted_index[i])
1163 		    .unread)
1164 		{
1165 			currentMessage=i;
1166 			break;
1167 		}
1168 
1169 	if (save_flag)
1170 		getServer()->saveFolderIndex( this );
1171 
1172 
1173 	myFolder::FolderFilter *filter=installFilter();
1174 
1175 	if (filter)
1176 	{
1177 		statusBar->clearstatus();
1178 		statusBar->status(_("Filtering messages..."));
1179 
1180 		mail::ptr<myFolder> folderPtr=this;
1181 
1182 		filter->filterStart=0;
1183 		filter->filterEnd=getServer()->server
1184 			->getFolderIndexSize();
1185 
1186 		filter->run();
1187 		if (folderPtr.isDestroyed() ||
1188 		    getServer()->server == NULL)
1189 			return true;
1190 		// Server connection broke in mid-filtering.
1191 
1192 
1193 		// If new mail arrived while filtering took place,
1194 		// filter the remaining messages in the background.
1195 
1196 		if (currentFilter && currentFilter->filterEnd
1197 		    < getServer()->server->getFolderIndexSize()
1198 
1199 		    && newMailCheckCount == 0)
1200 			// newMailCheckCount > 0: we're still gathering
1201 			// headers of new messages, this can be done later.
1202 			// See myFolder::newMessagesReceived().
1203 		{
1204 			currentFilter->filterStart=currentFilter->filterEnd;
1205 			currentFilter->filterEnd=getServer()->server
1206 				->getFolderIndexSize();
1207 			currentFilter->start();
1208 		}
1209 		else if (currentFilter)
1210 			delete currentFilter;
1211 	}
1212 	return true;
1213 }
1214 
loadFolderIndex(map<string,size_t> & uid_list)1215 bool myFolder::loadFolderIndex(map<string, size_t> &uid_list)
1216 {
1217 	string cachefile=getServer()
1218 		->getFolderConfiguration(getFolder(), "INDEX");
1219 
1220 	if (cachefile.size() == 0)
1221 		return true;
1222 
1223 	cachefile=myServer::getConfigDir() + "/" + cachefile;
1224 
1225 	return myServer::loadFolderIndex(cachefile, uid_list, this);
1226 }
1227 
setSortFunction(string sortBy)1228 void myFolder::setSortFunction(string sortBy)
1229 {
1230 	setSortFunctionNoSave(sortBy);
1231 
1232 	myServer *s=getServer();
1233 
1234 	s->updateFolderConfiguration(getFolder(),
1235 				     "SORT", getSortFunction());
1236 	s->saveFolderConfiguration();
1237 }
1238 
setSortFunctionNoSave(string sortBy)1239 void myFolder::setSortFunctionNoSave(string sortBy)
1240 {
1241 	// Try to keep the cursor on the same message, after resorting.
1242 
1243 	size_t cm=currentMessage;
1244 
1245 	if (cm < sorted_index.size())
1246 		cm=sorted_index[cm];
1247 
1248 	setSortFunctionNoSort(sortBy);
1249 	resort();
1250 
1251 	vector<size_t>::iterator p=
1252 		find(sorted_index.begin(), sorted_index.end(), cm);
1253 
1254 	currentMessage=p != sorted_index.end()
1255 		? p - sorted_index.begin():0;
1256 
1257 	if (mymessage)
1258 		mymessage->folderResorted();
1259 
1260 	if (currentDisplay)
1261 		currentDisplay->draw();
1262 }
1263 
setSortFunctionNoSort(string sortBy)1264 void myFolder::setSortFunctionNoSort(string sortBy)
1265 {
1266 	sort_function_not=false;
1267 	sort_function_name="";
1268 
1269 	if (strncmp(sortBy.c_str(), "!", 1) == 0)
1270 	{
1271 		sort_function_not=true;
1272 		sortBy=sortBy.substr(1);
1273 		sort_function_name="!";
1274 	}
1275 
1276 	if (sortBy == "D")
1277 		sort_function= &myFolder::sortByDate;
1278 	else if (sortBy == "S")
1279 		sort_function= &myFolder::sortBySubject;
1280 	else if (sortBy == "N")
1281 		sort_function= &myFolder::sortByName;
1282 	else if (sortBy == SORT_THREAD_S)
1283 		sort_function= &myFolder::sortByArrival; // Fixed up later.
1284 	else
1285 	{
1286 		sortBy="A";
1287 
1288 		sort_function= &myFolder::sortByArrival;
1289 	}
1290 	sort_function_name += sortBy;
1291 }
1292 
getSortFunction()1293 string myFolder::getSortFunction() const
1294 {
1295 	return sort_function_name;
1296 }
1297 
getFlags(size_t n)1298 mail::messageInfo myFolder::getFlags(size_t n) const
1299 {
1300 	return server->server ?
1301 		server->server->getFolderIndexInfo(sorted_index[n])
1302 		: mail::messageInfo();
1303 }
1304 
getServer()1305 myServer *myFolder::getServer() const
1306 {
1307 	return (server);
1308 }
1309 
1310 ////////////////////////////////////////////////////////////////////////////
1311 //
1312 // Navigation, used by CursesMessageDisplay
1313 
1314 // Find next unread message
1315 
getNextUnreadMessage(size_t & messageNum)1316 bool myFolder::getNextUnreadMessage(size_t &messageNum)
1317 {
1318 	messageNum=currentMessage;
1319 
1320 	size_t n;
1321 
1322 	for (n=messageNum+1; n<sorted_index.size(); n++)
1323 		if (serverIndex[sorted_index[n]].status_code == 'N')
1324 		{
1325 			messageNum=n;
1326 			return true;
1327 		}
1328 
1329 	for (n=0; n < messageNum && n < sorted_index.size(); n++)
1330 		if (serverIndex[sorted_index[n]].status_code == 'N')
1331 		{
1332 			messageNum=n;
1333 			return true;
1334 		}
1335 
1336 	return false;
1337 }
1338 
1339 // Find next message
1340 
getNextMessage(size_t & messageNum)1341 bool myFolder::getNextMessage(size_t &messageNum)
1342 {
1343 	messageNum=currentMessage;
1344 
1345 	if (currentMessage+1 < sorted_index.size())
1346 	{
1347 		++messageNum;
1348 		return true;
1349 	}
1350 
1351 	return false;
1352 }
1353 
1354 // Find previous message
1355 
getPrevMessage(size_t & messageNum)1356 bool myFolder::getPrevMessage(size_t &messageNum)
1357 {
1358 	messageNum=currentMessage;
1359 
1360 	if (currentMessage > 0)
1361 	{
1362 		messageNum--;
1363 		return true;
1364 	}
1365 
1366 	return false;
1367 }
1368 
1369 // Shortcut to view attachment screen
1370 
viewAttachments(size_t messageNum)1371 void myFolder::viewAttachments(size_t messageNum)
1372 {
1373 	openMessage(messageNum, "", true, NULL);
1374 
1375 }
1376 
1377 //////////////////////////////////////////////////////////////////////////
1378 //
1379 // Download a draft.
1380 //
1381 // The current implementation just downloads the message, then parses it.
1382 
1383 class OpenDraftCallback : public mail::callback::message,
1384 			  public myServer::Callback {
1385 
1386 	void reportProgress(size_t bytesCompleted,
1387 			    size_t bytesEstimatedTotal,
1388 
1389 			    size_t messagesCompleted,
1390 			    size_t messagesEstimatedTotal);
1391 
1392 public:
1393 	FILE *fp;
1394 	struct rfc2045 *rfc2045p;
1395 
1396 	OpenDraftCallback();
1397 	~OpenDraftCallback();
1398 
1399 	void messageTextCallback(size_t n, string text);
1400 	void success(string message);
1401 	void fail(string message);
1402 };
1403 
OpenDraftCallback()1404 OpenDraftCallback::OpenDraftCallback()
1405 {
1406 }
1407 
~OpenDraftCallback()1408 OpenDraftCallback::~OpenDraftCallback()
1409 {
1410 }
1411 
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)1412 void OpenDraftCallback::reportProgress(size_t bytesCompleted,
1413 				       size_t bytesEstimatedTotal,
1414 
1415 				       size_t messagesCompleted,
1416 				       size_t messagesEstimatedTotal)
1417 {
1418 	myServer::reportProgress(bytesCompleted,
1419 				 bytesEstimatedTotal,
1420 				 messagesCompleted,
1421 				 messagesEstimatedTotal);
1422 }
1423 
messageTextCallback(size_t n,string text)1424 void OpenDraftCallback::messageTextCallback(size_t n, string text)
1425 {
1426 	if (fwrite(&*text.begin(), text.size(), 1, fp) != 1)
1427 		; // Ignore gcc warning
1428 	rfc2045_parse(rfc2045p, &*text.begin(), text.size());
1429 }
1430 
success(string message)1431 void OpenDraftCallback::success(string message)
1432 {
1433 	myServer::Callback::success(message);
1434 }
1435 
fail(string message)1436 void OpenDraftCallback::fail(string message)
1437 {
1438 	myServer::Callback::fail(message);
1439 }
1440 
1441 static bool prepareDraft(FILE *, struct rfc2045 *);
1442 
goDraft()1443 void myFolder::goDraft()
1444 {
1445 	size_t messageNum=getCurrentMessage();
1446 
1447 	messageNum=sorted_index[messageNum];
1448 
1449 	if (getServer()->server == NULL)
1450 		return;
1451 
1452 	mail::ptr<mail::account> server=getServer()->server;
1453 
1454 	statusBar->clearstatus();
1455 	statusBar->status(Gettext(_("Downloading message from %1%..."))
1456 			  << getFolder()->getName());
1457 
1458 	OpenDraftCallback openDraft;
1459 
1460 	if ((openDraft.rfc2045p=rfc2045_alloc()) == NULL ||
1461 	    (openDraft.fp=tmpfile()) == NULL)
1462 	{
1463 		if (openDraft.rfc2045p)
1464 			rfc2045_free(openDraft.rfc2045p);
1465 		statusBar->status(strerror(errno), statusBar->SYSERROR);
1466 		statusBar->beepError();
1467 		return;
1468 	}
1469 
1470 	mail::messageInfo oldInfo=server->getFolderIndexInfo(messageNum);
1471 
1472 	try {
1473 		vector<size_t> messageVec;
1474 
1475 		messageVec.push_back(messageNum);
1476 
1477 		server->readMessageContent(messageVec, false,
1478 					   mail::readBoth, openDraft);
1479 
1480 		if (!myServer::eventloop(openDraft))
1481 		{
1482 			fclose(openDraft.fp);
1483 			rfc2045_free(openDraft.rfc2045p);
1484 			return;
1485 		}
1486 
1487 		statusBar->clearstatus();
1488 		statusBar->status(_("Extracting MIME attachments..."));
1489 
1490 		if (fflush(openDraft.fp) < 0 || ferror(openDraft.fp) < 0 ||
1491 		    !prepareDraft(openDraft.fp, openDraft.rfc2045p))
1492 		{
1493 			return;
1494 		}
1495 
1496 	} catch (...) {
1497 		fclose(openDraft.fp);
1498 		rfc2045_free(openDraft.rfc2045p);
1499 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1500 	}
1501 	fclose(openDraft.fp);
1502 	rfc2045_free(openDraft.rfc2045p);
1503 
1504 	if (server.isDestroyed())
1505 		return;
1506 
1507 	// After we have the message, remove it from the Draft folder.
1508 
1509 	if (server->getFolderIndexSize() > messageNum &&
1510 	    server->getFolderIndexInfo(messageNum).uid == oldInfo.uid)
1511 	{
1512 		try
1513 		{
1514 			isExpungingDrafts=true;
1515 
1516 			vector<size_t> msgList;
1517 
1518 			msgList.push_back(messageNum);
1519 
1520 			myServer::Callback removeCallback;
1521 
1522 			server->removeMessages(msgList, removeCallback);
1523 
1524 			myServer::eventloop(removeCallback);
1525 
1526 			isExpungingDrafts=false;
1527 		} catch (...) {
1528 			isExpungingDrafts=false;
1529 			LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1530 		}
1531 	}
1532 
1533 	Curses::keepgoing=false;
1534         myServer::nextScreen= &editScreen;
1535         myServer::nextScreenArg=NULL;
1536 }
1537 
1538 static bool copyTo(FILE *, string, string, off_t, off_t, struct rfc2045 *,
1539 		   size_t, size_t);
1540 static bool copyTo(FILE *, ostream &, off_t, off_t, struct rfc2045 *,
1541 		   mail::autodecoder *d, size_t, size_t);
1542 
1543 // Extract the main body of the message, and its attachments.
1544 
prepareDraft(FILE * fp,struct rfc2045 * rfcp)1545 static bool prepareDraft(FILE *fp, struct rfc2045 *rfcp)
1546 {
1547 	struct rfc2045 *p;
1548 
1549 	string messagetmp=myServer::getConfigDir() + "/message.tmp";
1550 	string messagetxt=myServer::getConfigDir() + "/message.txt";
1551 
1552 	if (rfcp->firstpart == NULL)
1553 	{
1554 		off_t start_pos, end_pos, start_body, dummy;
1555 
1556 		rfc2045_mimepos(rfcp, &start_pos, &end_pos,
1557 				&start_body, &dummy, &dummy);
1558 
1559 		return copyTo(fp, messagetmp, messagetxt,
1560 			      start_pos, start_body, rfcp, 0, 1);
1561 	}
1562 
1563 	struct rfc2045 *firstSect=NULL;
1564 
1565 	size_t done=0;
1566 	size_t estTotal=0;
1567 
1568 	for (p=rfcp->firstpart; p; p=p->next)
1569 	{
1570 		if (p->isdummy)
1571 			continue;
1572 
1573 		++estTotal;
1574 	}
1575 
1576 	size_t cnt=0;
1577 
1578 	for (p=rfcp->firstpart; p; p=p->next)
1579 	{
1580 		if (p->isdummy)
1581 			continue;
1582 
1583 		if (!firstSect)
1584 		{
1585 			firstSect=p;
1586 			continue;
1587 		}
1588 
1589 		off_t start_pos, end_pos, dummy;
1590 
1591 		rfc2045_mimepos(p, &start_pos, &end_pos,
1592 				&dummy, &dummy, &dummy);
1593 
1594 		string tmpname, attname;
1595 
1596 		string buffer;
1597 
1598 		{
1599 			ostringstream o;
1600 
1601 			o << "-" << setw(4) << setfill('0') << cnt++;
1602 			buffer=o.str();
1603 		}
1604 
1605 		myMessage::createAttFilename(tmpname, attname, buffer);
1606 
1607 		if (!copyTo(fp, tmpname, attname, start_pos, end_pos, NULL,
1608 			    done, estTotal))
1609 			return false;
1610 
1611 		++done;
1612 	}
1613 
1614 	off_t start_pos, end_pos, start_body, dummy;
1615 
1616 	rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body,
1617 			&dummy, &dummy);
1618 
1619 	ofstream o(messagetmp.c_str());
1620 
1621 	if (fseek(fp, start_pos, SEEK_SET) < 0)
1622 	{
1623 		statusBar->status(strerror(errno), statusBar->SYSERROR);
1624 		statusBar->beepError();
1625 		return false;
1626 	}
1627 
1628 	char lastCh='\n';
1629 
1630 	while (start_pos < start_body)
1631 	{
1632 		int ch=getc(fp);
1633 
1634 		if (ch == EOF)
1635 			break;
1636 		++start_pos;
1637 
1638 		if (ch == '\r')
1639 			continue;
1640 
1641 		if (ch == '\n' && lastCh == '\n')
1642 			break;
1643 		o << (char)ch;
1644 
1645 		lastCh=(char)ch;
1646 	}
1647 
1648 	if (lastCh != '\n')
1649 		o << '\n';
1650 
1651 	clearerr(fp);
1652 
1653 	if (firstSect)
1654 	{
1655 		rfc2045_mimepos(firstSect, &start_pos, &end_pos, &start_body,
1656 				&dummy, &dummy);
1657 		if (!copyTo(fp, o, start_pos, start_body, firstSect, NULL,
1658 			    done, estTotal))
1659 			return false;
1660 	}
1661 
1662 	o.flush();
1663 	if (o.bad() || o.fail() ||
1664 	    (o.close(), rename(messagetmp.c_str(), messagetxt.c_str())) < 0)
1665 	{
1666 		statusBar->status(strerror(errno), statusBar->SYSERROR);
1667 		statusBar->beepError();
1668 		return false;
1669 	}
1670 	return true;
1671 }
1672 
copyTo(FILE * fp,string tmpname,string txtname,off_t from,off_t to,struct rfc2045 * contentPart,size_t messagesCompleted,size_t messagesEstimatedTotal)1673 static bool copyTo(FILE *fp, string tmpname, string txtname,
1674 		   off_t from, off_t to, struct rfc2045 *contentPart,
1675 		   size_t messagesCompleted,
1676 		   size_t messagesEstimatedTotal)
1677 {
1678 	ofstream o(tmpname.c_str());
1679 
1680 	if (!copyTo(fp, o, from, to, contentPart, NULL,
1681 		    messagesCompleted,
1682 		    messagesEstimatedTotal) ||
1683 	    (o.flush(), o.bad()) || o.fail() ||
1684 	    (o.close(), rename(tmpname.c_str(), txtname.c_str())) < 0)
1685 	{
1686 		statusBar->status(strerror(errno), statusBar->SYSERROR);
1687 		statusBar->beepError();
1688 		return false;
1689 	}
1690 	return true;
1691 }
1692 
1693 class myFolderWriteDecoded : public mail::autodecoder {
1694 
1695 	ostream &o;
1696 
1697 public:
1698 	myFolderWriteDecoded(ostream &oArg, string cte);
1699 	~myFolderWriteDecoded();
1700 
1701 	void decoded(string);
1702 };
1703 
myFolderWriteDecoded(ostream & oArg,string cte)1704 myFolderWriteDecoded::myFolderWriteDecoded(ostream &oArg, string cte)
1705 	: mail::autodecoder(cte), o(oArg)
1706 {
1707 }
1708 
~myFolderWriteDecoded()1709 myFolderWriteDecoded::~myFolderWriteDecoded()
1710 {
1711 }
1712 
decoded(string s)1713 void myFolderWriteDecoded::decoded(string s)
1714 {
1715 	o << s;
1716 }
1717 
copyTo(FILE * fp,ostream & o,off_t from,off_t to,struct rfc2045 * contentPart,mail::autodecoder * decoderArg,size_t messagesCompleted,size_t messagesEstimatedTotal)1718 static bool copyTo(FILE *fp, ostream &o, off_t from, off_t to,
1719 		   struct rfc2045 *contentPart,
1720 		   mail::autodecoder *decoderArg,
1721 		   size_t messagesCompleted,
1722 		   size_t messagesEstimatedTotal)
1723 {
1724 	if (fseek(fp, from, SEEK_SET) >= 0)
1725 	{
1726 		char buffer[BUFSIZ];
1727 		off_t startedPos=from;
1728 
1729 		int n;
1730 
1731 		while (from < to)
1732 		{
1733 			n=sizeof(buffer);
1734 
1735 			if (to - from < n)
1736 				n=to-from;
1737 
1738 			errno=EIO;
1739 			n=fread(buffer, 1, n, fp);
1740 
1741 			if (n <= 0)
1742 			{
1743 				statusBar->status(strerror(errno),
1744 						  statusBar->SYSERROR);
1745 				statusBar->beepError();
1746 				return false;
1747 			}
1748 
1749 			string ss(buffer, buffer+n);
1750 
1751 			if (decoderArg)
1752 				decoderArg->decode(ss);
1753 			else
1754 				o << ss;
1755 
1756 			from += n;
1757 			myServer::reportProgress(from - startedPos,
1758 						 to - startedPos,
1759 						 messagesCompleted,
1760 						 messagesEstimatedTotal);
1761 
1762 			if (o.bad() || o.fail())
1763 			{
1764 				statusBar->status(strerror(errno),
1765 						  statusBar->SYSERROR);
1766 				statusBar->beepError();
1767 				return false;
1768 			}
1769 		}
1770 
1771 		if (contentPart)
1772 		{
1773 			const char *content_type;
1774                         const char *content_transfer_encoding;
1775                         const char *charset;
1776 
1777                         rfc2045_mimeinfo(contentPart, &content_type,
1778                                          &content_transfer_encoding,
1779                                          &charset);
1780 
1781 			off_t start_pos, end_pos, start_body, dummy;
1782 
1783 			rfc2045_mimepos(contentPart, &start_pos, &end_pos,
1784 					&start_body, &dummy, &dummy);
1785 
1786 			myFolderWriteDecoded
1787 				decoder(o, content_transfer_encoding);
1788 
1789 			return (copyTo(fp, o, start_body, end_pos, NULL,
1790 				       &decoder,
1791 				       messagesCompleted,
1792 				       messagesEstimatedTotal));
1793 		}
1794 		return true;
1795 	}
1796 
1797 	statusBar->status(strerror(errno), statusBar->SYSERROR);
1798 	statusBar->beepError();
1799 	return false;
1800 }
1801 
1802 // Open the indicated message
1803 
goMessage(size_t messageNum,string mimeid,void (myMessage::* completedFuncArg)())1804 void myFolder::goMessage(size_t messageNum,
1805 			 string mimeid,
1806 			 void (myMessage::*completedFuncArg)())
1807 {
1808 	openMessage(messageNum, mimeid, false, completedFuncArg);
1809 }
1810 
openMessage(size_t messageNum,string mimeid,bool attachmentsOnly,void (myMessage::* completedFuncArg)())1811 void myFolder::openMessage(size_t messageNum,
1812 			   string mimeid,
1813 			   bool attachmentsOnly,
1814 			   void (myMessage::*completedFuncArg)())
1815 {
1816 	if (messageNum < size())
1817 		currentMessage=messageNum;
1818 
1819 	// If there's an error, fall back to the folder index screen
1820 
1821 	Curses::keepgoing=false;
1822 	myServer::nextScreen= &folderIndexScreen;
1823 	myServer::nextScreenArg=this;
1824 
1825 	// If we already have this message loaded, save the trouble of
1826 	// loading it again.
1827 
1828 	if (mymessage == NULL || mymessage->messagesortednum != messageNum
1829 	    || (!attachmentsOnly && !mymessage->isShownMimeId(mimeid)))
1830 	{
1831 		statusBar->clearstatus();
1832 		statusBar->status(_("Reading message..."),
1833 				  statusBar->INPROGRESS);
1834 
1835 		CursesMessage *cm=new CursesMessage(getServer()->server,
1836 						    completedFuncArg);
1837 
1838 		if (!cm)
1839 			outofmemory();
1840 
1841 		try {
1842 			mail::ptr<CursesMessage> cmptr(cm);
1843 
1844 			cm->myfolder=this;
1845 			cm->messagesortednum=messageNum;
1846 
1847 			messageNum=getServerIndex(messageNum);
1848 			cm->messagenum=messageNum;
1849 
1850 			myServer::nextScreen=NULL;
1851 
1852 			if (!cm->readAttributes() ||
1853 			    (!attachmentsOnly && !cm->init(mimeid, false)) ||
1854 			    cmptr.isDestroyed())
1855 			{
1856 				if (!cmptr.isDestroyed())
1857 				{
1858 					cm->myfolder=NULL;
1859 					delete cm;
1860 				}
1861 
1862 				Curses::keepgoing=false;
1863 
1864 				if (myServer::nextScreen == NULL)
1865 					// If we got disconnected while
1866 					// opening the message, don't mess
1867 					// with nextScreen
1868 				{
1869 					myServer::nextScreen=
1870 						&folderIndexScreen;
1871 					myServer::nextScreenArg=this;
1872 				}
1873 				return;
1874 			}
1875 
1876 			mymessage=cm;
1877 		} catch (...) {
1878 			delete cm;
1879 			LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1880 		}
1881 
1882 		if (completedFuncArg)
1883 			cm->beginReformat(80);
1884 	}
1885 	else if (completedFuncArg)
1886 		(mymessage->*completedFuncArg)();
1887 
1888 	if (completedFuncArg)
1889 		return;
1890 
1891 	Curses::keepgoing=false;
1892 	myServer::nextScreen= &folderIndexScreen;
1893 	myServer::nextScreenArg=this;
1894 
1895 	if (mymessage != NULL) // Expected
1896 	{
1897 		if (attachmentsOnly)
1898 		{
1899 			statusBar->clearstatus();
1900 			myServer::nextScreen= &showAttachments;
1901 		}
1902 		else
1903 		{
1904 			myServer::nextScreen= &showCurrentMessage;
1905 		}
1906 		myServer::nextScreenArg=mymessage;
1907 	}
1908 }
1909 
1910 ////////////////////////////////////////////////////////////////////////////
1911 
FolderIndexUpdate(myFolder & folderArg,mail::callback & callbackArg)1912 myFolder::FolderIndexUpdate::FolderIndexUpdate(myFolder &folderArg,
1913 					       mail::callback &callbackArg)
1914 	: folder(&folderArg), successOrFail(callbackArg),
1915 	  totalCount(0), doneCount(0)
1916 {
1917 }
1918 
~FolderIndexUpdate()1919 myFolder::FolderIndexUpdate::~FolderIndexUpdate()
1920 {
1921 }
1922 
success(string errmsg)1923 void myFolder::FolderIndexUpdate::success(string errmsg)
1924 {
1925 	successOrFail.success(errmsg);
1926 }
1927 
fail(string errmsg)1928 void myFolder::FolderIndexUpdate::fail(string errmsg)
1929 {
1930 	successOrFail.fail(errmsg);
1931 }
1932 
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)1933 void myFolder::FolderIndexUpdate::reportProgress(size_t bytesCompleted,
1934 						 size_t bytesEstimatedTotal,
1935 
1936 						 size_t messagesCompleted,
1937 						 size_t messagesEstimatedTotal)
1938 {
1939 	myServer::reportProgress(0, 0,
1940 				 messagesCompleted,
1941 				 messagesEstimatedTotal);
1942 }
1943 
1944 void myFolder::FolderIndexUpdate
messageEnvelopeCallback(size_t messageNumber,const class mail::envelope & envelope)1945 ::messageEnvelopeCallback(size_t messageNumber,
1946 			  const class mail::envelope
1947 			  &envelope)
1948 {
1949 	if (folder.isDestroyed())
1950 		return;
1951 
1952 	if (messageNumber < folder->serverIndex.size())
1953 	{
1954 		Index &i=folder->serverIndex[messageNumber];
1955 
1956 		// Save headers of message in the folder index.
1957 
1958 		i.messageid=messageId(folder->msgIds, envelope.messageid);
1959 		i.messageDate=envelope.date;
1960 
1961 		if (envelope.references.size() > 0)
1962 			messageReferencesCallback(messageNumber,
1963 						  envelope.references);
1964 
1965 		if (i.references.size() == 0)
1966 		{
1967 			// Try to find something in in-reply-to
1968 
1969 			string s=envelope.inreplyto;
1970 
1971 			size_t n=s.find('<');
1972 
1973 			if (n != std::string::npos)
1974 				s=s.substr(0, n-1);
1975 
1976 			n=s.find('>');
1977 
1978 			if (n != std::string::npos)
1979 				s=s.substr(n+1);
1980 
1981 			vector<string> v;
1982 
1983 			v.push_back(s);
1984 
1985 			messageReferencesCallback(messageNumber, v);
1986 		}
1987 
1988 		mail::rfc2047::decoder header_decode;
1989 
1990 		i.subject_utf8=header_decode.decode(envelope.subject,
1991 						    "utf-8");
1992 
1993 		bool err=false;
1994 
1995 		{
1996 			u32string uc;
1997 
1998 			if (!unicode::iconvert::convert(i.subject_utf8,
1999 						     "utf-8", uc))
2000 				err=true;
2001 		}
2002 
2003 		if (err)
2004 			i.subject_utf8=Gettext(_("ERROR: "))
2005 				<< strerror(errno);
2006 
2007 		string::iterator bs=i.subject_utf8.begin(),
2008 			es=i.subject_utf8.end();
2009 
2010 		while (bs != es)
2011 		{
2012 			if ( (int)(unsigned char)*bs < ' ')
2013 				*bs=' '; // Zap control chars
2014 			bs++;
2015 		}
2016 
2017 		i.name_utf8="";
2018 
2019 		vector<mail::address>::const_iterator b, e;
2020 
2021 		b=envelope.from.begin();
2022 		e=envelope.from.end();
2023 
2024 		mail::emailAddress fallback_addr;
2025 		bool has_fallback_addr=false;
2026 
2027 		string fallback_pfix;
2028 
2029 		// NetNews posts only have a From: header.  If it's my post,
2030 		// don't leave the summary blank.
2031 
2032 		while (b != e)
2033 		{
2034 			mail::emailAddress a(*b++);
2035 
2036 			if (myServer::isMyAddress(a))
2037 			{
2038 				fallback_addr=a;
2039 				has_fallback_addr=true;
2040 				continue;
2041 			}
2042 
2043 			if (i.name_utf8.size() == 0)
2044 			{
2045 				i.name_utf8=a.getDisplayName("utf-8");
2046 				if (i.name_utf8.size() == 0)
2047 					i.name_utf8=a.getDisplayAddr("utf-8");
2048 			}
2049 		}
2050 
2051 		b=envelope.to.begin();
2052 		e=envelope.to.end();
2053 
2054 		const char *addr_pfix=_("To: ");
2055 
2056 		while (b != e)
2057 		{
2058 			mail::emailAddress a(*b++);
2059 
2060 			if (myServer::isMyAddress(a))
2061 			{
2062 				fallback_addr=a;
2063 				has_fallback_addr=true;
2064 				fallback_pfix=addr_pfix;
2065 				continue;
2066 			}
2067 
2068 			if (i.name_utf8.size() == 0)
2069 			{
2070 				i.name_utf8=a.getDisplayName("utf-8");
2071 				if (i.name_utf8.size() == 0)
2072 					i.name_utf8=a.getDisplayAddr("utf-8");
2073 
2074 				if (i.name_utf8.size() > 0)
2075 					i.name_utf8=addr_pfix + i.name_utf8;
2076 			}
2077 		}
2078 
2079 		b=envelope.cc.begin();
2080 		e=envelope.cc.end();
2081 
2082 		addr_pfix=_("Cc: ");
2083 
2084 		while (b != e)
2085 		{
2086 			mail::emailAddress a(*b++);
2087 
2088 			if (myServer::isMyAddress(a))
2089 			{
2090 				fallback_addr=a;
2091 				has_fallback_addr=true;
2092 				fallback_pfix=addr_pfix;
2093 				continue;
2094 			}
2095 
2096 			if (i.name_utf8.size() == 0)
2097 			{
2098 				i.name_utf8=a.getDisplayName("utf-8");
2099 				if (i.name_utf8.size() == 0)
2100 					i.name_utf8=a.getDisplayAddr("utf-8");
2101 				if (i.name_utf8.size() > 0)
2102 					i.name_utf8=addr_pfix + i.name_utf8;
2103 			}
2104 		}
2105 
2106 		if (i.name_utf8.size() == 0 && has_fallback_addr)
2107 		{
2108 			i.name_utf8= fallback_addr.getDisplayName("utf-8");
2109 			if (i.name_utf8.size() == 0)
2110 				i.name_utf8=fallback_addr.getDisplayAddr("utf-8");
2111 
2112 			if (i.name_utf8.size() > 0)
2113 				i.name_utf8=fallback_pfix + i.name_utf8;
2114 		}
2115 
2116 		bs=i.name_utf8.begin();
2117 		es=i.name_utf8.end();
2118 
2119 		while (bs != es)
2120 		{
2121 			if ( (int)(unsigned char)*bs < ' ')
2122 				*bs=' '; // Zap control chars
2123 			bs++;
2124 		}
2125 
2126 		i.toupper();
2127 		i.checkwatch(*folder);
2128 	}
2129 
2130 	if (++doneCount > totalCount*3)
2131 		totalCount=doneCount / 3;
2132 	reportProgress(0, 0, doneCount / 3, totalCount);
2133 }
2134 
2135 void myFolder::FolderIndexUpdate
messageReferencesCallback(size_t messageNumber,const vector<string> & references)2136 ::messageReferencesCallback(size_t messageNumber,
2137 			    const vector<string> &references)
2138 {
2139 	if (folder.isDestroyed())
2140 		return;
2141 
2142 	if (messageNumber >= folder->serverIndex.size())
2143 		return;
2144 
2145 	Index &i=folder->serverIndex[messageNumber];
2146 
2147 	i.references.clear();
2148 	i.references.reserve(references.size());
2149 	i.watchLevel=0;
2150 	i.expires=0;
2151 
2152 	if (references.size() > 0)
2153 	{
2154 		vector<string>::const_iterator rb=references.begin(),
2155 			re=references.end();
2156 
2157 		while (rb != re)
2158 		{
2159 			string s=*rb;
2160 
2161 			if (s.size() > 0 && s[0] == '<')
2162 				s=s.substr(1);
2163 
2164 			if (s.size() > 0 && s[s.size()-1] == '>')
2165 				s=s.substr(0, s.size()-1);
2166 
2167 			messageId mid(folder->msgIds, s);
2168 			i.references.push_back(mid);
2169 
2170 			if (!folder->watchList.watching(messageId(folder
2171 								  ->msgIds,
2172 								  *rb),
2173 							i.expires,
2174 							i.watchLevel))
2175 				if (i.watchLevel)
2176 				{
2177 					if (--i.watchLevel == 0)
2178 						i.expires=0;
2179 				}
2180 			++rb;
2181 		}
2182 	}
2183 
2184 	if (((string)i.messageid).size() > 0) // Already seen env
2185 	{
2186 		if (i.watchLevel)
2187 		{
2188 			--i.watchLevel;
2189 			folder->watchList(i.messageid, i.expires,
2190 					  i.watchLevel);
2191 		}
2192 		i.checkwatch(*folder);
2193 	}
2194 }
2195 
2196 void myFolder::FolderIndexUpdate
messageArrivalDateCallback(size_t messageNumber,time_t datetime)2197 ::messageArrivalDateCallback(size_t messageNumber,
2198 			     time_t datetime)
2199 {
2200 	if (folder.isDestroyed())
2201 		return;
2202 
2203 	if (messageNumber < folder->serverIndex.size())
2204 		folder->serverIndex[messageNumber].arrivalDate=datetime;
2205 
2206 	if (++doneCount > totalCount*3)
2207 		totalCount=doneCount / 3;
2208 	reportProgress(0, 0, doneCount / 3, totalCount);
2209 }
2210 
2211 void myFolder::FolderIndexUpdate
messageSizeCallback(size_t messageNumber,unsigned long size)2212 ::messageSizeCallback(size_t messageNumber,
2213 		      unsigned long size)
2214 {
2215 	if (folder.isDestroyed())
2216 		return;
2217 
2218 	if (messageNumber < folder->serverIndex.size())
2219 		folder->serverIndex[messageNumber].messageSize=size;
2220 
2221 	if (++doneCount > totalCount*3)
2222 		totalCount=doneCount / 3;
2223 	reportProgress(0, 0, doneCount / 3, totalCount);
2224 }
2225 
2226 ////////////////////////////////////////////////////////////////////////////
2227 
Index()2228 myFolder::Index::Index() : arrivalDate(0), messageDate(0), messageSize(0),
2229 			   status_code(' '),
2230 			   tag(0), threadLevel(0), watchLevel(0),
2231 			   expires(0)
2232 {
2233 }
2234 
~Index()2235 myFolder::Index::~Index()
2236 {
2237 }
2238 
2239 // Pre-convert sort fields to uppercase
2240 
toupper()2241 void myFolder::Index::toupper()
2242 {
2243 	upperSubject_utf8=unicode::iconvert::convert_tocase(subject_utf8,
2244 							 "utf-8",
2245 							 unicode_uc);
2246 	upperName_utf8=unicode::iconvert::convert_tocase(name_utf8,
2247 						      "utf-8",
2248 						      unicode_uc);
2249 }
2250 
checkwatch(myFolder & f)2251 void myFolder::Index::checkwatch(myFolder &f)
2252 {
2253 	if (watchLevel == 0)
2254 		f.watchList.watching(messageid, expires, watchLevel);
2255 }
2256 
setStatusCode(const class mail::messageInfo & flags)2257 void myFolder::Index::setStatusCode(const class mail::messageInfo &flags)
2258 {
2259 	status_code=flags.deleted ? 'D':
2260 		flags.unread ? 'N':
2261 		flags.replied ? 'R':' ';
2262 }
2263 
setTag(set<string> & keywords)2264 void myFolder::Index::setTag(set<string> &keywords)
2265 {
2266 	set<string>::iterator b=keywords.begin(), e=keywords.end();
2267 
2268 	tag=0;
2269 
2270 	while (b != e)
2271 	{
2272 		size_t n;
2273 
2274 		if (Tags::tags.isTagName(*b, n) && n > tag)
2275 			tag=n;
2276 		++b;
2277 	}
2278 }
2279 
2280 ////////////////////////////////////////////////////////////////////////////
2281 
NewMailUpdate()2282 myFolder::NewMailUpdate::NewMailUpdate() : update_struct(NULL)
2283 {
2284 }
2285 
~NewMailUpdate()2286 myFolder::NewMailUpdate::~NewMailUpdate()
2287 {
2288 }
2289 
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)2290 void myFolder::NewMailUpdate::reportProgress(size_t bytesCompleted,
2291 					     size_t bytesEstimatedTotal,
2292 
2293 					     size_t messagesCompleted,
2294 					     size_t messagesEstimatedTotal)
2295 {
2296 	myServer::reportProgress(bytesCompleted,
2297 				 bytesEstimatedTotal,
2298 				 messagesCompleted,
2299 				 messagesEstimatedTotal);
2300 }
2301 
success(string message)2302 void myFolder::NewMailUpdate::success(string message)
2303 {
2304 	if (update_struct)
2305 	{
2306 		if (!update_struct->folder.isDestroyed())
2307 			update_struct->folder->newMessagesReceived();
2308 		delete update_struct;
2309 	}
2310 	delete this;
2311 }
2312 
fail(string message)2313 void myFolder::NewMailUpdate::fail(string message)
2314 {
2315 	if (update_struct)
2316 	{
2317 		if (!update_struct->folder.isDestroyed())
2318 			update_struct->folder->newMessagesReceived();
2319 		delete update_struct;
2320 	}
2321 	delete this;
2322 }
2323 
checkNewMail(mail::callback & cb)2324 void myFolder::checkNewMail(mail::callback &cb)
2325 {
2326 	if (server->server == NULL)
2327 	{
2328 		cb.success("OK");
2329 		return;
2330 	}
2331 
2332 	server->server->checkNewMail(cb);
2333 }
2334 
operator()2335 bool myFolder::IndexSort::operator()(size_t a, size_t b)
2336 {
2337 	if (f.sort_function_not)
2338 	{
2339 		size_t c=a;
2340 
2341 		a=b;
2342 		b=c;
2343 	}
2344 
2345 	return (f.*(f.sort_function))(a, b);
2346 }
2347 
sortByArrival(size_t a,size_t b)2348 bool myFolder::sortByArrival(size_t a, size_t b)
2349 {
2350 	return a < b;
2351 }
2352 
sortByDate(size_t a,size_t b)2353 bool myFolder::sortByDate(size_t a, size_t b)
2354 {
2355 	time_t ad=serverIndex[a].messageDate;
2356 	time_t bd=serverIndex[b].messageDate;
2357 
2358 	if (ad == 0)
2359 		ad=serverIndex[a].arrivalDate;
2360 	if (bd == 0)
2361 		bd=serverIndex[b].arrivalDate;
2362 
2363 	if (ad == 0 || bd == 0)
2364 		return a < b;
2365 
2366 	return ad < bd;
2367 }
2368 
sortBySubject(size_t a,size_t b)2369 bool myFolder::sortBySubject(size_t a, size_t b)
2370 {
2371 	string sa=serverIndex[a].upperSubject_utf8;
2372 	string sb=serverIndex[b].upperSubject_utf8;
2373 
2374 	int arefwd, brefwd;
2375 
2376 	char *as=rfc822_coresubj_nouc(sa.c_str(), &arefwd);
2377 
2378 	if (!as)
2379 		outofmemory();
2380 
2381 	try {
2382 		sa=as;
2383 		free(as);
2384 	} catch (...) {
2385 		free(as);
2386 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
2387 	}
2388 
2389 	char *bs=rfc822_coresubj_nouc(sb.c_str(), &brefwd);
2390 
2391 	if (!bs)
2392 		outofmemory();
2393 
2394 	try {
2395 		sb=bs;
2396 		free(bs);
2397 	} catch (...) {
2398 		free(bs);
2399 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
2400 	}
2401 
2402 	if (sa == sb)
2403 	{
2404 		if (arefwd == brefwd)
2405 			return sortByDate(a, b);
2406 
2407 		return (arefwd < brefwd);
2408 	}
2409 
2410 	return sa < sb;
2411 }
2412 
sortByName(size_t a,size_t b)2413 bool myFolder::sortByName(size_t a, size_t b)
2414 {
2415 	string sa=serverIndex[a].upperName_utf8;
2416 	string sb=serverIndex[b].upperName_utf8;
2417 
2418 	if (sa == sb)
2419 		return sortByDate(a, b);
2420 
2421 	return sa < sb;
2422 }
2423 
2424 //////////////////////////////////////////////////////////////////////////
2425 //
2426 // Interface for displaying the folder index on the screen
2427 //
2428 
IndexDisplay(myFolder * fArg)2429 myFolder::IndexDisplay::IndexDisplay(myFolder *fArg)
2430 	: f(fArg)
2431 {
2432 	f->currentDisplay=this;
2433 }
2434 
~IndexDisplay()2435 myFolder::IndexDisplay::~IndexDisplay()
2436 {
2437 	if (f)
2438 		f->currentDisplay=NULL;
2439 }
2440 
2441 //////////////////////////////////////////////////////////////////////////////
2442 
DelUndelCallback()2443 myFolder::DelUndelCallback::DelUndelCallback()
2444 {
2445 }
2446 
~DelUndelCallback()2447 myFolder::DelUndelCallback::~DelUndelCallback()
2448 {
2449 }
2450 
success(string message)2451 void myFolder::DelUndelCallback::success(string message)
2452 {
2453 	statusBar->status(message, statusBar->NORMAL);
2454 	delete this;
2455 }
2456 
fail(string message)2457 void myFolder::DelUndelCallback::fail(string message)
2458 {
2459 	statusBar->status(message, statusBar->SERVERERROR);
2460 	statusBar->beepError();
2461 	delete this;
2462 }
2463 
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)2464 void myFolder::DelUndelCallback::reportProgress(size_t bytesCompleted,
2465 						size_t bytesEstimatedTotal,
2466 
2467 						size_t messagesCompleted,
2468 						size_t messagesEstimatedTotal)
2469 {
2470 	myServer::reportProgress(bytesCompleted,
2471 				 bytesEstimatedTotal,
2472 				 messagesCompleted,
2473 				 messagesEstimatedTotal);
2474 }
2475 
2476 //
2477 // Re-sort sorted_index according to the current sort order
2478 //
2479 
resort()2480 void myFolder::resort()
2481 {
2482 	if (sort_function_name.find(SORT_THREAD) != std::string::npos)
2483 	{
2484 		thread::resort(*this);
2485 		return;
2486 	}
2487 
2488 	vector<myFolder::Index>::iterator b=serverIndex.begin(),
2489 		e=serverIndex.end();
2490 
2491 	while (b != e)
2492 	{
2493 		b->threadLevel=0;
2494 		b->active_threads.clear();
2495 		b++;
2496 	}
2497 
2498 	sort(sorted_index.begin(), sorted_index.end(), IndexSort(*this));
2499 }
2500 
2501 /////////////////////////////////////////////////////////////////////////////
2502 //
2503 // Threading implementation
2504 
2505 #include "rfc822/imaprefs.h"
2506 
resort(myFolder & f)2507 void myFolder::thread::resort(myFolder &f)
2508 {
2509 	struct imap_refmsgtable *t=rfc822_threadalloc();
2510 
2511 	if (!t)
2512 		LIBMAIL_THROW((strerror(errno)));
2513 
2514 	try {
2515 		resort(f, t);
2516 		rfc822_threadfree(t);
2517 	} catch (...) {
2518 		rfc822_threadfree(t);
2519 		throw;
2520 	}
2521 }
2522 
resort(myFolder & f,struct imap_refmsgtable * t)2523 void myFolder::thread::resort(myFolder &f, struct imap_refmsgtable *t)
2524 {
2525 	vector<myFolder::Index>::iterator ib, ie;
2526 
2527 	size_t n=0;
2528 
2529 	size_t cnt=f.sorted_index.size();
2530 
2531 	for (ib=f.serverIndex.begin(), ie=ib+cnt; ib != ie; ib++)
2532 	{
2533 		vector<const char *> refArray;
2534 
2535 		refArray.reserve(ib->references.size()+1);
2536 
2537 		vector<messageId>::iterator b=ib->references.begin(),
2538 			e=ib->references.end();
2539 
2540 		while (b != e)
2541 		{
2542 			refArray.push_back( b->c_str() );
2543 			++b;
2544 		}
2545 
2546 		refArray.push_back(0);
2547 
2548 		if (!rfc822_threadmsgrefs(t, ib->messageid.c_str(),
2549 					  &refArray[0],
2550 					  ib->subject_utf8.c_str(),
2551 					  NULL, ib->messageDate, n++))
2552 			LIBMAIL_THROW((strerror(errno)));
2553 	}
2554 
2555 	struct imap_refmsg *root=rfc822_thread(t);
2556 
2557 	if (!root)
2558 		LIBMAIL_THROW((strerror(errno)));
2559 
2560 	vector<size_t> active_threads;
2561 	size_t threadLevel=0;
2562 
2563 	struct imap_refmsg *lastMsg=NULL;
2564 
2565 	f.sorted_index.clear();
2566 	f.sorted_index.reserve(cnt);
2567 
2568 	resort_layout(f, root, lastMsg, active_threads, threadLevel);
2569 
2570 	if (lastMsg)
2571 		resort_layout(f, lastMsg, active_threads, threadLevel, false);
2572 }
2573 
2574 // Unwrap thread tree into flat layout
2575 
2576 
2577 // Visit all messages at the same thread level.  The thread tree may have
2578 // dummy entries, we can't just visit each node just like that.
2579 // If this function receives a dummy node, it recursively invokes itself
2580 // for all of its children nodes.  Otherwise:
2581 
2582 //  We may need to do some special processing for the last visited message
2583 // (namely, pop off the last active thread).  Therefore, do not visit each
2584 // node right away.  Instead, keep a "last seen" message pointer, which is
2585 // initialized to NULL.  When this function visits this node, it sets the
2586 // last visited ptr to nodePtr.  If last visited ptr was not null, the
2587 // last visited ptr is officially visited.  On exited, last visited ptr
2588 // contains a ptr to the last message at the same thread level, so it can
2589 // be properly serviced.
2590 
2591 
2592 #define MAXTHREADLEVEL 10
2593 
2594 	// Beyond the 10th thread level, there's not much point in
2595 	// keeping the score.
2596 
2597 
resort_layout(myFolder & f,struct imap_refmsg * nodePtr,struct imap_refmsg * & lastPtr,vector<size_t> & active_threads,size_t threadLevel)2598 void myFolder::thread::resort_layout(myFolder &f,
2599 				     struct imap_refmsg *nodePtr,
2600 				     struct imap_refmsg *&lastPtr,
2601 
2602 				     vector<size_t> &active_threads,
2603 				     size_t threadLevel)
2604 {
2605 	if (!nodePtr->isdummy)
2606 	{
2607 		if (lastPtr)
2608 			resort_layout(f, lastPtr, active_threads, threadLevel,
2609 				      false);
2610 			// Visit the last seen node, officially.
2611 		lastPtr=nodePtr;
2612 
2613 		if (threadLevel < MAXTHREADLEVEL)
2614 			return;
2615 	}
2616 
2617 	for (nodePtr=nodePtr->firstchild; nodePtr;
2618 	     nodePtr=nodePtr->nextsib)
2619 		resort_layout(f, nodePtr, lastPtr, active_threads,
2620 			      threadLevel);
2621 }
2622 
resort_layout(myFolder & f,struct imap_refmsg * node,vector<size_t> & active_threads,size_t threadLevel,bool popLast)2623 void myFolder::thread::resort_layout(myFolder &f,
2624 				     struct imap_refmsg *node,
2625 				     vector<size_t> &active_threads,
2626 				     size_t threadLevel,
2627 				     bool popLast)
2628 {
2629 	myFolder::Index &i=f.serverIndex[node->seqnum];
2630 
2631 #if 0
2632 	cerr << "Thread: msg " << f.sorted_index.size() // << node->seqnum
2633 	     << "(" << i.subject_utf8 << "), level " << threadLevel << ":";
2634 
2635 	{
2636 		vector<size_t>::iterator a=active_threads.begin(),
2637 			b=active_threads.end();
2638 
2639 		while (a != b)
2640 			cerr << " " << *a++;
2641 		cerr << endl << flush;
2642 	}
2643 #endif
2644 
2645 	i.threadLevel=threadLevel;
2646 	i.active_threads=active_threads;
2647 
2648 	f.sorted_index.push_back(node->seqnum);
2649 
2650 	if (popLast)
2651 		active_threads.pop_back();
2652 
2653 	active_threads.push_back(threadLevel);
2654 
2655 	struct imap_refmsg *last=NULL;
2656 
2657 	if (threadLevel < MAXTHREADLEVEL)
2658 		for (node=node->firstchild; node; node=node->nextsib)
2659 			resort_layout(f, node, last, active_threads,
2660 				      threadLevel+1);
2661 
2662 	if (last)
2663 		resort_layout(f, last, active_threads, threadLevel+1, true);
2664 	else
2665 		active_threads.pop_back();
2666 }
2667