1 /*
2     This file is part of Akonadi.
3     SPDX-FileCopyrightText: 2003 Andreas Gungl <a.gungl@gmx.de>
4     SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5     SPDX-FileCopyrightText: 2010 Leo Franchi <lfranchi@kde.org>
6 
7     SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #include "messagestatus.h"
11 
12 #include "messageflags.h"
13 
14 #include <QString>
15 
16 /** The message status format. These can be or'd together.
17     Note, that the StatusIgnored implies the
18     status to be Read.
19     This is done in isRead() and related getters.
20     So we can preserve the state when switching a
21     thread to Ignored and back. */
22 enum Status {
23     StatusUnknown = 0x00000000,
24     StatusUnread = 0x00000002, // deprecated
25     StatusRead = 0x00000004,
26     StatusDeleted = 0x00000010,
27     StatusReplied = 0x00000020,
28     StatusForwarded = 0x00000040,
29     StatusQueued = 0x00000080,
30     StatusSent = 0x00000100,
31     StatusFlag = 0x00000200, // flag means important
32     StatusWatched = 0x00000400,
33     StatusIgnored = 0x00000800, // forces isRead()
34     StatusToAct = 0x00001000,
35     StatusSpam = 0x00002000,
36     StatusHam = 0x00004000,
37     StatusHasAttachment = 0x00008000,
38     StatusHasInvitation = 0x00010000,
39     StatusSigned = 0x00020000,
40     StatusEncrypted = 0x00040000,
41     StatusHasError = 0x00080000
42 };
43 
MessageStatus()44 Akonadi::MessageStatus::MessageStatus()
45 {
46     mStatus = StatusUnknown;
47 }
48 
operator ==(Akonadi::MessageStatus other) const49 bool Akonadi::MessageStatus::operator==(Akonadi::MessageStatus other) const
50 {
51     return mStatus == other.mStatus;
52 }
53 
operator !=(Akonadi::MessageStatus other) const54 bool Akonadi::MessageStatus::operator!=(Akonadi::MessageStatus other) const
55 {
56     return mStatus != other.mStatus;
57 }
58 
operator &(Akonadi::MessageStatus other) const59 bool Akonadi::MessageStatus::operator&(Akonadi::MessageStatus other) const
60 {
61     if (mStatus == StatusUnread) {
62         return !(other.mStatus & StatusRead);
63     }
64 
65     if (other.mStatus == StatusUnread) {
66         return !(mStatus & StatusRead);
67     }
68 
69     return mStatus & other.mStatus;
70 }
71 
clear()72 void Akonadi::MessageStatus::clear()
73 {
74     mStatus = StatusUnknown;
75 }
76 
set(Akonadi::MessageStatus other)77 void Akonadi::MessageStatus::set(Akonadi::MessageStatus other)
78 {
79     Q_ASSERT(!(other.mStatus & StatusUnread));
80 
81     // Those stati are exclusive, but we have to lock at the
82     // internal representation because Ignored can manipulate
83     // the result of the getter methods.
84     if (other.mStatus & StatusRead) {
85         setRead();
86     }
87     if (other.isDeleted()) {
88         setDeleted();
89     }
90     if (other.isReplied()) {
91         setReplied();
92     }
93     if (other.isForwarded()) {
94         setForwarded();
95     }
96     if (other.isQueued()) {
97         setQueued();
98     }
99     if (other.isSent()) {
100         setSent();
101     }
102     if (other.isImportant()) {
103         setImportant();
104     }
105 
106     if (other.isWatched()) {
107         setWatched();
108     }
109     if (other.isIgnored()) {
110         setIgnored();
111     }
112     if (other.isToAct()) {
113         setToAct();
114     }
115     if (other.isSpam()) {
116         setSpam();
117     }
118     if (other.isHam()) {
119         setHam();
120     }
121     if (other.hasAttachment()) {
122         setHasAttachment();
123     }
124     if (other.hasInvitation()) {
125         setHasInvitation();
126     }
127     if (other.isSigned()) {
128         setSigned();
129     }
130     if (other.isEncrypted()) {
131         setEncrypted();
132     }
133     if (other.hasError()) {
134         setHasError();
135     }
136 }
137 
toggle(Akonadi::MessageStatus other)138 void Akonadi::MessageStatus::toggle(Akonadi::MessageStatus other)
139 {
140     Q_ASSERT(!(other.mStatus & StatusUnread));
141 
142     if (other.isDeleted()) {
143         setDeleted(!(mStatus & StatusDeleted));
144     }
145     if (other.isReplied()) {
146         setReplied(!(mStatus & StatusReplied));
147     }
148     if (other.isForwarded()) {
149         setForwarded(!(mStatus & StatusForwarded));
150     }
151     if (other.isQueued()) {
152         setQueued(!(mStatus & StatusQueued));
153     }
154     if (other.isSent()) {
155         setSent(!(mStatus & StatusSent));
156     }
157     if (other.isImportant()) {
158         setImportant(!(mStatus & StatusFlag));
159     }
160 
161     if (other.isWatched()) {
162         setWatched(!(mStatus & StatusWatched));
163     }
164     if (other.isIgnored()) {
165         setIgnored(!(mStatus & StatusIgnored));
166     }
167     if (other.isToAct()) {
168         setToAct(!(mStatus & StatusToAct));
169     }
170     if (other.isSpam()) {
171         setSpam(!(mStatus & StatusSpam));
172     }
173     if (other.isHam()) {
174         setHam(!(mStatus & StatusHam));
175     }
176     if (other.hasAttachment()) {
177         setHasAttachment(!(mStatus & StatusHasAttachment));
178     }
179     if (other.hasInvitation()) {
180         setHasInvitation(!(mStatus & StatusHasInvitation));
181     }
182     if (other.isSigned()) {
183         setSigned(!(mStatus & StatusSigned));
184     }
185     if (other.isEncrypted()) {
186         setEncrypted(!(mStatus & StatusEncrypted));
187     }
188     if (other.hasError()) {
189         setHasError(!(mStatus & StatusHasError));
190     }
191 }
192 
isOfUnknownStatus() const193 bool Akonadi::MessageStatus::isOfUnknownStatus() const
194 {
195     return mStatus == StatusUnknown;
196 }
197 
isRead() const198 bool Akonadi::MessageStatus::isRead() const
199 {
200     return (mStatus & StatusRead) || (mStatus & StatusIgnored);
201 }
202 
isDeleted() const203 bool Akonadi::MessageStatus::isDeleted() const
204 {
205     return mStatus & StatusDeleted;
206 }
207 
isReplied() const208 bool Akonadi::MessageStatus::isReplied() const
209 {
210     return mStatus & StatusReplied;
211 }
212 
isForwarded() const213 bool Akonadi::MessageStatus::isForwarded() const
214 {
215     return mStatus & StatusForwarded;
216 }
217 
isQueued() const218 bool Akonadi::MessageStatus::isQueued() const
219 {
220     return mStatus & StatusQueued;
221 }
222 
isSent() const223 bool Akonadi::MessageStatus::isSent() const
224 {
225     return mStatus & StatusSent;
226 }
227 
isImportant() const228 bool Akonadi::MessageStatus::isImportant() const
229 {
230     return mStatus & StatusFlag;
231 }
232 
isWatched() const233 bool Akonadi::MessageStatus::isWatched() const
234 {
235     return mStatus & StatusWatched;
236 }
237 
isIgnored() const238 bool Akonadi::MessageStatus::isIgnored() const
239 {
240     return mStatus & StatusIgnored;
241 }
242 
isToAct() const243 bool Akonadi::MessageStatus::isToAct() const
244 {
245     return mStatus & StatusToAct;
246 }
247 
isSpam() const248 bool Akonadi::MessageStatus::isSpam() const
249 {
250     return mStatus & StatusSpam;
251 }
252 
isHam() const253 bool Akonadi::MessageStatus::isHam() const
254 {
255     return mStatus & StatusHam;
256 }
257 
hasAttachment() const258 bool Akonadi::MessageStatus::hasAttachment() const
259 {
260     return mStatus & StatusHasAttachment;
261 }
262 
hasInvitation() const263 bool Akonadi::MessageStatus::hasInvitation() const
264 {
265     return mStatus & StatusHasInvitation;
266 }
267 
isSigned() const268 bool Akonadi::MessageStatus::isSigned() const
269 {
270     return mStatus & StatusSigned;
271 }
272 
isEncrypted() const273 bool Akonadi::MessageStatus::isEncrypted() const
274 {
275     return mStatus & StatusEncrypted;
276 }
277 
hasError() const278 bool Akonadi::MessageStatus::hasError() const
279 {
280     return mStatus & StatusHasError;
281 }
282 
setRead(bool read)283 void Akonadi::MessageStatus::setRead(bool read)
284 {
285     if (read) {
286         mStatus |= StatusRead;
287     } else {
288         mStatus &= ~StatusRead;
289     }
290 }
291 
setDeleted(bool deleted)292 void Akonadi::MessageStatus::setDeleted(bool deleted)
293 {
294     if (deleted) {
295         mStatus |= StatusDeleted;
296     } else {
297         mStatus &= ~StatusDeleted;
298     }
299 }
300 
setReplied(bool replied)301 void Akonadi::MessageStatus::setReplied(bool replied)
302 {
303     if (replied) {
304         mStatus |= StatusReplied;
305     } else {
306         mStatus &= ~StatusReplied;
307     }
308 }
309 
setForwarded(bool forwarded)310 void Akonadi::MessageStatus::setForwarded(bool forwarded)
311 {
312     if (forwarded) {
313         mStatus |= StatusForwarded;
314     } else {
315         mStatus &= ~StatusForwarded;
316     }
317 }
318 
setQueued(bool queued)319 void Akonadi::MessageStatus::setQueued(bool queued)
320 {
321     if (queued) {
322         mStatus |= StatusQueued;
323     } else {
324         mStatus &= ~StatusQueued;
325     }
326 }
327 
setSent(bool sent)328 void Akonadi::MessageStatus::setSent(bool sent)
329 {
330     if (sent) {
331         mStatus &= ~StatusQueued;
332         mStatus |= StatusSent;
333     } else {
334         mStatus &= ~StatusSent;
335     }
336 }
337 
setImportant(bool important)338 void Akonadi::MessageStatus::setImportant(bool important)
339 {
340     if (important) {
341         mStatus |= StatusFlag;
342     } else {
343         mStatus &= ~StatusFlag;
344     }
345 }
346 
347 // Watched and ignored are mutually exclusive
setWatched(bool watched)348 void Akonadi::MessageStatus::setWatched(bool watched)
349 {
350     if (watched) {
351         mStatus &= ~StatusIgnored;
352         mStatus |= StatusWatched;
353     } else {
354         mStatus &= ~StatusWatched;
355     }
356 }
357 
setIgnored(bool ignored)358 void Akonadi::MessageStatus::setIgnored(bool ignored)
359 {
360     if (ignored) {
361         mStatus &= ~StatusWatched;
362         mStatus |= StatusIgnored;
363     } else {
364         mStatus &= ~StatusIgnored;
365     }
366 }
367 
setToAct(bool toAct)368 void Akonadi::MessageStatus::setToAct(bool toAct)
369 {
370     if (toAct) {
371         mStatus |= StatusToAct;
372     } else {
373         mStatus &= ~StatusToAct;
374     }
375 }
376 
377 // Ham and Spam are mutually exclusive
setSpam(bool spam)378 void Akonadi::MessageStatus::setSpam(bool spam)
379 {
380     if (spam) {
381         mStatus &= ~StatusHam;
382         mStatus |= StatusSpam;
383     } else {
384         mStatus &= ~StatusSpam;
385     }
386 }
387 
setHam(bool ham)388 void Akonadi::MessageStatus::setHam(bool ham)
389 {
390     if (ham) {
391         mStatus &= ~StatusSpam;
392         mStatus |= StatusHam;
393     } else {
394         mStatus &= ~StatusHam;
395     }
396 }
397 
setHasAttachment(bool withAttachment)398 void Akonadi::MessageStatus::setHasAttachment(bool withAttachment)
399 {
400     if (withAttachment) {
401         mStatus |= StatusHasAttachment;
402     } else {
403         mStatus &= ~StatusHasAttachment;
404     }
405 }
406 
setHasInvitation(bool withInvitation)407 void Akonadi::MessageStatus::setHasInvitation(bool withInvitation)
408 {
409     if (withInvitation) {
410         mStatus |= StatusHasInvitation;
411     } else {
412         mStatus &= ~StatusHasInvitation;
413     }
414 }
415 
setSigned(bool value)416 void Akonadi::MessageStatus::setSigned(bool value)
417 {
418     if (value) {
419         mStatus |= StatusSigned;
420     } else {
421         mStatus &= ~StatusSigned;
422     }
423 }
424 
setEncrypted(bool value)425 void Akonadi::MessageStatus::setEncrypted(bool value)
426 {
427     if (value) {
428         mStatus |= StatusEncrypted;
429     } else {
430         mStatus &= ~StatusEncrypted;
431     }
432 }
433 
setHasError(bool hasError)434 void Akonadi::MessageStatus::setHasError(bool hasError)
435 {
436     if (hasError) {
437         mStatus |= StatusHasError;
438     } else {
439         mStatus &= ~StatusHasError;
440     }
441 }
442 
toQInt32() const443 qint32 Akonadi::MessageStatus::toQInt32() const
444 {
445     return mStatus;
446 }
447 
fromQInt32(qint32 status)448 void Akonadi::MessageStatus::fromQInt32(qint32 status)
449 {
450     mStatus = status;
451 }
452 
statusStr() const453 QString Akonadi::MessageStatus::statusStr() const
454 {
455     QByteArray sstr;
456     if (mStatus & StatusRead) {
457         sstr += 'R';
458     } else {
459         sstr += 'U';
460     }
461     if (mStatus & StatusDeleted) {
462         sstr += 'D';
463     }
464     if (mStatus & StatusReplied) {
465         sstr += 'A';
466     }
467     if (mStatus & StatusForwarded) {
468         sstr += 'F';
469     }
470     if (mStatus & StatusQueued) {
471         sstr += 'Q';
472     }
473     if (mStatus & StatusToAct) {
474         sstr += 'K';
475     }
476     if (mStatus & StatusSent) {
477         sstr += 'S';
478     }
479     if (mStatus & StatusFlag) {
480         sstr += 'G';
481     }
482     if (mStatus & StatusWatched) {
483         sstr += 'W';
484     }
485     if (mStatus & StatusIgnored) {
486         sstr += 'I';
487     }
488     if (mStatus & StatusSpam) {
489         sstr += 'P';
490     }
491     if (mStatus & StatusHam) {
492         sstr += 'H';
493     }
494     if (mStatus & StatusHasAttachment) {
495         sstr += 'T';
496     }
497 
498     return QLatin1String(sstr);
499 }
500 
setStatusFromStr(const QString & aStr)501 void Akonadi::MessageStatus::setStatusFromStr(const QString &aStr)
502 {
503     mStatus = StatusUnknown;
504 
505     if (aStr.contains(QLatin1Char('U'))) {
506         setRead(false);
507     }
508     if (aStr.contains(QLatin1Char('R'))) {
509         setRead();
510     }
511     if (aStr.contains(QLatin1Char('D'))) {
512         setDeleted();
513     }
514     if (aStr.contains(QLatin1Char('A'))) {
515         setReplied();
516     }
517     if (aStr.contains(QLatin1Char('F'))) {
518         setForwarded();
519     }
520     if (aStr.contains(QLatin1Char('Q'))) {
521         setQueued();
522     }
523     if (aStr.contains(QLatin1Char('K'))) {
524         setToAct();
525     }
526     if (aStr.contains(QLatin1Char('S'))) {
527         setSent();
528     }
529     if (aStr.contains(QLatin1Char('G'))) {
530         setImportant();
531     }
532     if (aStr.contains(QLatin1Char('W'))) {
533         setWatched();
534     }
535     if (aStr.contains(QLatin1Char('I'))) {
536         setIgnored();
537     }
538     if (aStr.contains(QLatin1Char('P'))) {
539         setSpam();
540     }
541     if (aStr.contains(QLatin1Char('H'))) {
542         setHam();
543     }
544     if (aStr.contains(QLatin1Char('T'))) {
545         setHasAttachment();
546     }
547     if (aStr.contains(QLatin1Char('C'))) {
548         setHasAttachment(false);
549     }
550 }
551 
statusFlags() const552 QSet<QByteArray> Akonadi::MessageStatus::statusFlags() const
553 {
554     QSet<QByteArray> flags;
555 
556     if (mStatus & StatusDeleted) {
557         flags += Akonadi::MessageFlags::Deleted;
558     } else {
559         if (mStatus & StatusRead) {
560             flags += Akonadi::MessageFlags::Seen;
561         }
562         if (mStatus & StatusReplied) {
563             flags += Akonadi::MessageFlags::Answered;
564         }
565         if (mStatus & StatusFlag) {
566             flags += Akonadi::MessageFlags::Flagged;
567         }
568 
569         // non standard flags
570         if (mStatus & StatusSent) {
571             flags += Akonadi::MessageFlags::Sent;
572         }
573         if (mStatus & StatusQueued) {
574             flags += Akonadi::MessageFlags::Queued;
575         }
576         if (mStatus & StatusReplied) {
577             flags += Akonadi::MessageFlags::Replied;
578         }
579         if (mStatus & StatusForwarded) {
580             flags += Akonadi::MessageFlags::Forwarded;
581         }
582         if (mStatus & StatusToAct) {
583             flags += Akonadi::MessageFlags::ToAct;
584         }
585         if (mStatus & StatusWatched) {
586             flags += Akonadi::MessageFlags::Watched;
587         }
588         if (mStatus & StatusIgnored) {
589             flags += Akonadi::MessageFlags::Ignored;
590         }
591         if (mStatus & StatusHasAttachment) {
592             flags += Akonadi::MessageFlags::HasAttachment;
593         }
594         if (mStatus & StatusHasInvitation) {
595             flags += Akonadi::MessageFlags::HasInvitation;
596         }
597         if (mStatus & StatusSigned) {
598             flags += Akonadi::MessageFlags::Signed;
599         }
600         if (mStatus & StatusEncrypted) {
601             flags += Akonadi::MessageFlags::Encrypted;
602         }
603         if (mStatus & StatusSpam) {
604             flags += Akonadi::MessageFlags::Spam;
605         }
606         if (mStatus & StatusHam) {
607             flags += Akonadi::MessageFlags::Ham;
608         }
609         if (mStatus & StatusHasError) {
610             flags += Akonadi::MessageFlags::HasError;
611         }
612     }
613 
614     return flags;
615 }
616 
setStatusFromFlags(const QSet<QByteArray> & flags)617 void Akonadi::MessageStatus::setStatusFromFlags(const QSet<QByteArray> &flags)
618 {
619     mStatus = StatusUnknown;
620 
621     for (const QByteArray &flag : flags) {
622         const QByteArray &upperedFlag = flag.toUpper();
623         if (upperedFlag == Akonadi::MessageFlags::Deleted) {
624             setDeleted();
625         } else if (upperedFlag == Akonadi::MessageFlags::Seen) {
626             setRead();
627         } else if (upperedFlag == Akonadi::MessageFlags::Answered) {
628             setReplied();
629         } else if (upperedFlag == Akonadi::MessageFlags::Flagged) {
630             setImportant();
631 
632             // non standard flags
633         } else if (upperedFlag == Akonadi::MessageFlags::Sent) {
634             setSent();
635         } else if (upperedFlag == Akonadi::MessageFlags::Queued) {
636             setQueued();
637         } else if (upperedFlag == Akonadi::MessageFlags::Replied) {
638             setReplied();
639         } else if (upperedFlag == Akonadi::MessageFlags::Forwarded) {
640             setForwarded();
641         } else if (upperedFlag == Akonadi::MessageFlags::ToAct) {
642             setToAct();
643         } else if (upperedFlag == Akonadi::MessageFlags::Watched) {
644             setWatched();
645         } else if (upperedFlag == Akonadi::MessageFlags::Ignored) {
646             setIgnored();
647         } else if (upperedFlag == Akonadi::MessageFlags::HasAttachment) {
648             setHasAttachment();
649         } else if (upperedFlag == Akonadi::MessageFlags::HasInvitation) {
650             setHasInvitation();
651         } else if (upperedFlag == Akonadi::MessageFlags::Signed) {
652             setSigned();
653         } else if (upperedFlag == Akonadi::MessageFlags::Encrypted) {
654             setEncrypted();
655         } else if (upperedFlag == Akonadi::MessageFlags::Spam) {
656             setSpam();
657         } else if (upperedFlag == Akonadi::MessageFlags::Ham) {
658             setHam();
659         } else if (upperedFlag == Akonadi::MessageFlags::HasError) {
660             setHasError();
661         }
662     }
663 }
664 
statusUnread()665 const Akonadi::MessageStatus Akonadi::MessageStatus::statusUnread()
666 {
667     Akonadi::MessageStatus st;
668     st.mStatus = StatusUnread;
669     return st;
670 }
671 
statusRead()672 const Akonadi::MessageStatus Akonadi::MessageStatus::statusRead()
673 {
674     Akonadi::MessageStatus st;
675     st.setRead();
676     return st;
677 }
678 
statusDeleted()679 const Akonadi::MessageStatus Akonadi::MessageStatus::statusDeleted()
680 {
681     Akonadi::MessageStatus st;
682     st.setDeleted();
683     return st;
684 }
685 
statusReplied()686 const Akonadi::MessageStatus Akonadi::MessageStatus::statusReplied()
687 {
688     Akonadi::MessageStatus st;
689     st.setReplied();
690     return st;
691 }
692 
statusForwarded()693 const Akonadi::MessageStatus Akonadi::MessageStatus::statusForwarded()
694 {
695     Akonadi::MessageStatus st;
696     st.setForwarded();
697     return st;
698 }
699 
statusQueued()700 const Akonadi::MessageStatus Akonadi::MessageStatus::statusQueued()
701 {
702     Akonadi::MessageStatus st;
703     st.setQueued();
704     return st;
705 }
706 
statusSent()707 const Akonadi::MessageStatus Akonadi::MessageStatus::statusSent()
708 {
709     Akonadi::MessageStatus st;
710     st.setSent();
711     return st;
712 }
713 
statusImportant()714 const Akonadi::MessageStatus Akonadi::MessageStatus::statusImportant()
715 {
716     Akonadi::MessageStatus st;
717     st.setImportant();
718     return st;
719 }
720 
statusWatched()721 const Akonadi::MessageStatus Akonadi::MessageStatus::statusWatched()
722 {
723     Akonadi::MessageStatus st;
724     st.setWatched();
725     return st;
726 }
727 
statusIgnored()728 const Akonadi::MessageStatus Akonadi::MessageStatus::statusIgnored()
729 {
730     Akonadi::MessageStatus st;
731     st.setIgnored();
732     return st;
733 }
734 
statusToAct()735 const Akonadi::MessageStatus Akonadi::MessageStatus::statusToAct()
736 {
737     Akonadi::MessageStatus st;
738     st.setToAct();
739     return st;
740 }
741 
statusSpam()742 const Akonadi::MessageStatus Akonadi::MessageStatus::statusSpam()
743 {
744     Akonadi::MessageStatus st;
745     st.setSpam();
746     return st;
747 }
748 
statusHam()749 const Akonadi::MessageStatus Akonadi::MessageStatus::statusHam()
750 {
751     Akonadi::MessageStatus st;
752     st.setHam();
753     return st;
754 }
755 
statusHasAttachment()756 const Akonadi::MessageStatus Akonadi::MessageStatus::statusHasAttachment()
757 {
758     Akonadi::MessageStatus st;
759     st.setHasAttachment();
760     return st;
761 }
762 
statusHasInvitation()763 const Akonadi::MessageStatus Akonadi::MessageStatus::statusHasInvitation()
764 {
765     MessageStatus st;
766     st.setHasInvitation();
767     return st;
768 }
769 
statusSigned()770 const Akonadi::MessageStatus Akonadi::MessageStatus::statusSigned()
771 {
772     MessageStatus st;
773     st.setSigned();
774     return st;
775 }
776 
statusEncrypted()777 const Akonadi::MessageStatus Akonadi::MessageStatus::statusEncrypted()
778 {
779     MessageStatus st;
780     st.setEncrypted();
781     return st;
782 }
783 
statusHasError()784 const Akonadi::MessageStatus Akonadi::MessageStatus::statusHasError()
785 {
786     MessageStatus st;
787     st.setHasError();
788     return st;
789 }
790 
operator <<(QDebug d,const Akonadi::MessageStatus & t)791 QDebug operator<<(QDebug d, const Akonadi::MessageStatus &t)
792 {
793     d << "status " << t.statusStr();
794     return d;
795 }
796