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