Coverage Report

Created: 2026-02-14 08:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibIMAP/Objects.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2021, Kyle Pereira <hey@xylepereira.me>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <AK/Function.h>
10
#include <AK/Tuple.h>
11
#include <AK/Variant.h>
12
#include <LibCore/DateTime.h>
13
#include <LibCore/EventReceiver.h>
14
15
namespace IMAP {
16
enum class CommandType {
17
    Append,
18
    Authenticate,
19
    Capability,
20
    Copy,
21
    Check,
22
    Close,
23
    Create,
24
    Delete,
25
    Examine,
26
    Expunge,
27
    Fetch,
28
    Idle,
29
    List,
30
    ListSub,
31
    Login,
32
    Logout,
33
    Noop,
34
    Rename,
35
    Search,
36
    Select,
37
    Status,
38
    Store,
39
    Subscribe,
40
    UIDCopy,
41
    UIDFetch,
42
    UIDSearch,
43
    UIDStore,
44
    Unsubscribe,
45
};
46
47
enum class MailboxFlag : unsigned {
48
    All = 1u << 0,
49
    Drafts = 1u << 1,
50
    Flagged = 1u << 2,
51
    HasChildren = 1u << 3,
52
    HasNoChildren = 1u << 4,
53
    Important = 1u << 5,
54
    Junk = 1u << 6,
55
    Marked = 1u << 7,
56
    NoInferiors = 1u << 8,
57
    NoSelect = 1u << 9,
58
    Sent = 1u << 10,
59
    Trash = 1u << 11,
60
    Unmarked = 1u << 12,
61
    Unknown = 1u << 13,
62
};
63
64
enum class ResponseType : unsigned {
65
    Capability = 1u << 0,
66
    List = 1u << 1,
67
    Exists = 1u << 2,
68
    Recent = 1u << 3,
69
    Flags = 1u << 4,
70
    UIDNext = 1u << 5,
71
    UIDValidity = 1u << 6,
72
    Unseen = 1u << 7,
73
    PermanentFlags = 1u << 8,
74
    Fetch = 1u << 9,
75
    Search = 1u << 10,
76
    ListSub = 1u << 11,
77
    Expunged = 1u << 12,
78
    Bye = 1u << 13,
79
    Status = 1u << 14
80
};
81
82
enum class FetchResponseType : unsigned {
83
    Body = 1u << 1,
84
    UID = 1u << 2,
85
    InternalDate = 1u << 3,
86
    Envelope = 1u << 4,
87
    Flags = 1u << 5,
88
    BodyStructure = 1u << 6,
89
};
90
91
enum class StatusItemType : unsigned {
92
    Recent = 1u << 1,
93
    UIDNext = 1u << 2,
94
    UIDValidity = 1u << 3,
95
    Unseen = 1u << 4,
96
    Messages = 1u << 5,
97
};
98
99
class Parser;
100
101
class StatusItem {
102
public:
103
    [[nodiscard]] unsigned status_items() const
104
0
    {
105
0
        return m_status_items;
106
0
    }
107
108
    [[nodiscard]] bool contains_status_item_type(StatusItemType type) const
109
0
    {
110
0
        return (static_cast<unsigned>(type) & m_status_items) != 0;
111
0
    }
112
113
    void add_status_item_type(StatusItemType type)
114
0
    {
115
0
        m_status_items |= static_cast<unsigned>(type);
116
0
    }
117
118
0
    void set_mailbox(ByteString&& mailbox) { m_mailbox = move(mailbox); }
119
0
    ByteString& mailbox() { return m_mailbox; }
120
121
    unsigned get(StatusItemType type) const
122
0
    {
123
0
        VERIFY(contains_status_item_type(type));
124
0
        switch (type) {
125
0
        case StatusItemType::Recent:
126
0
            return m_recent;
127
0
        case StatusItemType::UIDNext:
128
0
            return m_uid_next;
129
0
        case StatusItemType::UIDValidity:
130
0
            return m_uid_validity;
131
0
        case StatusItemType::Unseen:
132
0
            return m_unseen;
133
0
        case StatusItemType::Messages:
134
0
            return m_messages;
135
0
        }
136
0
        VERIFY_NOT_REACHED();
137
0
    }
138
139
    void set(StatusItemType type, unsigned value)
140
0
    {
141
0
        add_status_item_type(type);
142
0
        switch (type) {
143
0
        case StatusItemType::Recent:
144
0
            m_recent = value;
145
0
            break;
146
0
        case StatusItemType::UIDNext:
147
0
            m_uid_next = value;
148
0
            break;
149
0
        case StatusItemType::UIDValidity:
150
0
            m_uid_validity = value;
151
0
            break;
152
0
        case StatusItemType::Unseen:
153
0
            m_unseen = value;
154
0
            break;
155
0
        case StatusItemType::Messages:
156
0
            m_uid_next = value;
157
0
            break;
158
0
        }
159
0
    }
160
161
private:
162
    unsigned m_status_items { 0 };
163
    unsigned m_messages { 0 };
164
    unsigned m_recent { 0 };
165
    unsigned m_uid_next { 0 };
166
    unsigned m_uid_validity { 0 };
167
    unsigned m_unseen { 0 };
168
    ByteString m_mailbox;
169
};
170
171
struct Address {
172
    ByteString name;
173
    ByteString source_route;
174
    ByteString mailbox;
175
    ByteString host;
176
};
177
178
struct Envelope {
179
    ByteString date; // Format of date not specified.
180
    ByteString subject;
181
    Vector<Address> from;
182
    Vector<Address> sender;
183
    Vector<Address> reply_to;
184
    Vector<Address> to;
185
    Vector<Address> cc;
186
    Vector<Address> bcc;
187
    ByteString in_reply_to;
188
    ByteString message_id;
189
};
190
191
class BodyStructure;
192
193
struct BodyExtension {
194
    AK::Variant<Optional<ByteString>, unsigned, Vector<OwnPtr<BodyExtension>>> data;
195
};
196
197
struct MultiPartBodyStructureData {
198
    Optional<Tuple<ByteString, HashMap<ByteString, ByteString>>> disposition;
199
    Vector<OwnPtr<BodyStructure>> bodies;
200
    Vector<ByteString> langs;
201
    ByteString multipart_subtype;
202
    HashMap<ByteString, ByteString> params;
203
    ByteString location;
204
    Vector<BodyExtension> extensions;
205
};
206
207
struct BodyStructureData {
208
    ByteString type;
209
    ByteString subtype;
210
    ByteString id {};
211
    ByteString desc {};
212
    ByteString encoding;
213
    HashMap<ByteString, ByteString> fields;
214
    unsigned bytes { 0 };
215
    unsigned lines { 0 };
216
    Optional<Tuple<Envelope, NonnullOwnPtr<BodyStructure>>> contanied_message;
217
218
    ByteString md5 {};
219
    Optional<Tuple<ByteString, HashMap<ByteString, ByteString>>> disposition {};
220
    Optional<Vector<ByteString>> langs {};
221
    ByteString location {};
222
223
    Vector<BodyExtension> extensions {};
224
};
225
226
class BodyStructure {
227
    friend Parser;
228
229
public:
230
    explicit BodyStructure(BodyStructureData&& data)
231
222k
        : m_data(move(data))
232
222k
    {
233
222k
    }
234
235
    explicit BodyStructure(MultiPartBodyStructureData&& data)
236
3.31k
        : m_data(move(data))
237
3.31k
    {
238
3.31k
    }
239
240
0
    AK::Variant<BodyStructureData, MultiPartBodyStructureData> const& data() const { return m_data; }
241
242
private:
243
    AK::Variant<BodyStructureData, MultiPartBodyStructureData> m_data;
244
};
245
246
// Set -1 for '*' i.e highest possible value.
247
struct Sequence {
248
    int start;
249
    int end;
250
251
    [[nodiscard]] ByteString serialize() const;
252
};
253
254
struct FetchCommand {
255
    enum class DataItemType {
256
        BodyStructure,
257
        Envelope,
258
        Flags,
259
        InternalDate,
260
        UID,
261
        PeekBody,
262
        BodySection
263
    };
264
265
    struct DataItem {
266
        enum class SectionType {
267
            Header,
268
            HeaderFields,
269
            HeaderFieldsNot,
270
            Text,
271
            Parts
272
        };
273
        struct Section {
274
            SectionType type;
275
276
            Optional<Vector<unsigned>> parts {};
277
            bool ends_with_mime {};
278
279
            Optional<Vector<ByteString>> headers {};
280
281
            [[nodiscard]] ByteString serialize() const;
282
        };
283
284
        DataItemType type;
285
286
        Optional<Section> section {};
287
        bool partial_fetch { false };
288
        int start { 0 };
289
        int octets { 0 };
290
291
        [[nodiscard]] ByteString serialize() const;
292
    };
293
294
    Vector<Sequence> sequence_set;
295
    Vector<DataItem> data_items;
296
297
    ByteString serialize();
298
};
299
struct Command {
300
public:
301
    CommandType type;
302
    int tag;
303
    Vector<ByteString> args;
304
};
305
306
enum class ResponseStatus {
307
    Bad,
308
    No,
309
    OK,
310
};
311
312
struct ListItem {
313
    unsigned flags;
314
    ByteString reference;
315
    ByteString name;
316
};
317
318
class FetchResponseData {
319
public:
320
    [[nodiscard]] unsigned response_type() const
321
0
    {
322
0
        return m_response_type;
323
0
    }
324
325
    [[nodiscard]] bool contains_response_type(FetchResponseType response_type) const
326
0
    {
327
0
        return (static_cast<unsigned>(response_type) & m_response_type) != 0;
328
0
    }
329
330
    void add_response_type(FetchResponseType type)
331
242k
    {
332
242k
        m_response_type |= static_cast<unsigned>(type);
333
242k
    }
334
335
    void add_body_data(FetchCommand::DataItem&& data_item, ByteString&& body)
336
11.2k
    {
337
11.2k
        add_response_type(FetchResponseType::Body);
338
11.2k
        m_bodies.append({ move(data_item), move(body) });
339
11.2k
    }
340
341
    Vector<Tuple<FetchCommand::DataItem, ByteString>>& body_data()
342
0
    {
343
0
        VERIFY(contains_response_type(FetchResponseType::Body));
344
0
        return m_bodies;
345
0
    }
346
347
    void set_uid(unsigned uid)
348
80.9k
    {
349
80.9k
        add_response_type(FetchResponseType::UID);
350
80.9k
        m_uid = uid;
351
80.9k
    }
352
353
    [[nodiscard]] unsigned uid() const
354
0
    {
355
0
        VERIFY(contains_response_type(FetchResponseType::UID));
356
0
        return m_uid;
357
0
    }
358
359
    void set_internal_date(Core::DateTime time)
360
0
    {
361
0
        add_response_type(FetchResponseType::InternalDate);
362
0
        m_internal_date = time;
363
0
    }
364
365
    Core::DateTime& internal_date()
366
0
    {
367
0
        VERIFY(contains_response_type(FetchResponseType::InternalDate));
368
0
        return m_internal_date;
369
0
    }
370
371
    void set_envelope(Envelope&& envelope)
372
0
    {
373
0
        add_response_type(FetchResponseType::Envelope);
374
0
        m_envelope = move(envelope);
375
0
    }
376
377
    Envelope& envelope()
378
0
    {
379
0
        VERIFY(contains_response_type(FetchResponseType::Envelope));
380
0
        return m_envelope;
381
0
    }
382
383
    void set_flags(Vector<ByteString>&& flags)
384
124k
    {
385
124k
        add_response_type(FetchResponseType::Flags);
386
124k
        m_flags = move(flags);
387
124k
    }
388
389
    Vector<ByteString>& flags()
390
0
    {
391
0
        VERIFY(contains_response_type(FetchResponseType::Flags));
392
0
        return m_flags;
393
0
    }
394
395
    void set_body_structure(BodyStructure&& structure)
396
26.2k
    {
397
26.2k
        add_response_type(FetchResponseType::BodyStructure);
398
26.2k
        m_body_structure = move(structure);
399
26.2k
    }
400
401
    BodyStructure& body_structure()
402
0
    {
403
0
        VERIFY(contains_response_type(FetchResponseType::BodyStructure));
404
0
        return m_body_structure;
405
0
    }
406
407
    FetchResponseData()
408
190k
        : m_body_structure(BodyStructureData {})
409
190k
    {
410
190k
    }
411
412
private:
413
    Vector<ByteString> m_flags;
414
    Vector<Tuple<FetchCommand::DataItem, ByteString>> m_bodies;
415
    Core::DateTime m_internal_date;
416
    Envelope m_envelope;
417
    unsigned m_uid { 0 };
418
    unsigned m_response_type { 0 };
419
    BodyStructure m_body_structure;
420
};
421
422
ByteString serialize_astring(StringView string);
423
424
struct SearchKey {
425
public:
426
    // clang-format off
427
    struct All { };
428
    struct Answered { };
429
    struct Bcc { ByteString bcc; };
430
    struct Cc { ByteString cc; };
431
    struct Deleted { };
432
    struct Draft { };
433
    struct From { ByteString from; };
434
    struct Header { ByteString header; ByteString value; };
435
    struct Keyword { ByteString keyword; };
436
    struct Larger { unsigned number; };
437
    struct New { };
438
    struct Not { OwnPtr<SearchKey> operand; };
439
    struct Old { };
440
    struct On { Core::DateTime date; };
441
    struct Or { OwnPtr<SearchKey> lhs; OwnPtr<SearchKey> rhs; };
442
    struct Recent { };
443
    struct SearchKeys { Vector<OwnPtr<SearchKey>> keys; };
444
    struct Seen { };
445
    struct SentBefore { Core::DateTime date; };
446
    struct SentOn { Core::DateTime date; };
447
    struct SentSince { Core::DateTime date; };
448
    struct SequenceSet { Sequence sequence; };
449
    struct Since { Core::DateTime date; };
450
    struct Smaller { unsigned number; };
451
    struct Subject { ByteString subject; };
452
    struct Text { ByteString text; };
453
    struct To { ByteString to; };
454
    struct UID { unsigned  uid; };
455
    struct Unanswered { };
456
    struct Undeleted { };
457
    struct Undraft { };
458
    struct Unkeyword { ByteString flag_keyword; };
459
    struct Unseen { };
460
    // clang-format on
461
462
    Variant<All, Answered, Bcc, Cc, Deleted, Draft, From, Header, Keyword,
463
        Larger, New, Not, Old, On, Or, Recent, SearchKeys, Seen, SentBefore, SentOn,
464
        SentSince, SequenceSet, Since, Smaller, Subject, Text, To, UID, Unanswered,
465
        Undeleted, Undraft, Unkeyword, Unseen>
466
        data;
467
468
    SearchKey(SearchKey&& other) noexcept
469
        : data(move(other.data))
470
0
    {
471
0
    }
472
473
    template<typename T>
474
    explicit SearchKey(T&& t)
475
        : data(forward<T>(t))
476
    {
477
    }
478
479
    SearchKey& operator=(SearchKey&& other) noexcept
480
0
    {
481
0
        if (this == &other) {
482
0
            return *this;
483
0
        }
484
0
485
0
        this->data = move(other.data);
486
0
487
0
        return *this;
488
0
    }
489
490
    [[nodiscard]] ByteString serialize() const;
491
};
492
493
class ResponseData {
494
public:
495
    [[nodiscard]] unsigned response_type() const
496
0
    {
497
0
        return m_response_type;
498
0
    }
499
500
    ResponseData()
501
17.6k
        : m_response_type(0)
502
17.6k
    {
503
17.6k
    }
504
505
    ResponseData(ResponseData&) = delete;
506
11.5k
    ResponseData(ResponseData&&) = default;
507
    ResponseData& operator=(ResponseData const&) = delete;
508
11.1k
    ResponseData& operator=(ResponseData&&) = default;
509
510
    [[nodiscard]] bool contains_response_type(ResponseType response_type) const
511
0
    {
512
0
        return (static_cast<unsigned>(response_type) & m_response_type) != 0;
513
0
    }
514
515
    void add_response_type(ResponseType response_type)
516
1.03M
    {
517
1.03M
        m_response_type = m_response_type | static_cast<unsigned>(response_type);
518
1.03M
    }
519
520
    void add_capabilities(Vector<ByteString>&& capabilities)
521
527k
    {
522
527k
        m_capabilities = move(capabilities);
523
527k
        add_response_type(ResponseType::Capability);
524
527k
    }
525
526
    Vector<ByteString>& capabilities()
527
0
    {
528
0
        VERIFY(contains_response_type(ResponseType::Capability));
529
0
        return m_capabilities;
530
0
    }
531
532
    void add_list_item(ListItem&& item)
533
68.6k
    {
534
68.6k
        add_response_type(ResponseType::List);
535
68.6k
        m_list_items.append(move(item));
536
68.6k
    }
537
538
    Vector<ListItem>& list_items()
539
0
    {
540
0
        VERIFY(contains_response_type(ResponseType::List));
541
0
        return m_list_items;
542
0
    }
543
544
    void add_lsub_item(ListItem&& item)
545
83.1k
    {
546
83.1k
        add_response_type(ResponseType::List);
547
83.1k
        m_lsub_items.append(move(item));
548
83.1k
    }
549
550
    Vector<ListItem>& lsub_items()
551
0
    {
552
0
        VERIFY(contains_response_type(ResponseType::ListSub));
553
0
        return m_lsub_items;
554
0
    }
555
556
    void set_exists(unsigned exists)
557
192
    {
558
192
        add_response_type(ResponseType::Exists);
559
192
        m_exists = exists;
560
192
    }
561
562
    [[nodiscard]] unsigned exists() const
563
0
    {
564
0
        VERIFY(contains_response_type(ResponseType::Exists));
565
0
        return m_exists;
566
0
    }
567
568
    void set_recent(unsigned recent)
569
200
    {
570
200
        add_response_type(ResponseType::Recent);
571
200
        m_recent = recent;
572
200
    }
573
574
    [[nodiscard]] unsigned recent() const
575
0
    {
576
0
        VERIFY(contains_response_type(ResponseType::Recent));
577
0
        return m_recent;
578
0
    }
579
580
    void set_uid_next(unsigned uid_next)
581
204
    {
582
204
        add_response_type(ResponseType::UIDNext);
583
204
        m_uid_next = uid_next;
584
204
    }
585
586
    [[nodiscard]] unsigned uid_next() const
587
0
    {
588
0
        VERIFY(contains_response_type(ResponseType::UIDNext));
589
0
        return m_uid_next;
590
0
    }
591
592
    void set_uid_validity(unsigned uid_validity)
593
192
    {
594
192
        add_response_type(ResponseType::UIDValidity);
595
192
        m_uid_validity = uid_validity;
596
192
    }
597
598
    [[nodiscard]] unsigned uid_validity() const
599
0
    {
600
0
        VERIFY(contains_response_type(ResponseType::UIDValidity));
601
0
        return m_uid_validity;
602
0
    }
603
604
    void set_unseen(unsigned unseen)
605
198
    {
606
198
        add_response_type(ResponseType::Unseen);
607
198
        m_unseen = unseen;
608
198
    }
609
610
    [[nodiscard]] unsigned unseen() const
611
0
    {
612
0
        VERIFY(contains_response_type(ResponseType::Unseen));
613
0
        return m_unseen;
614
0
    }
615
616
    void set_flags(Vector<ByteString>&& flags)
617
9.91k
    {
618
9.91k
        m_response_type |= static_cast<unsigned>(ResponseType::Flags);
619
9.91k
        m_flags = move(flags);
620
9.91k
    }
621
622
    Vector<ByteString>& flags()
623
0
    {
624
0
        VERIFY(contains_response_type(ResponseType::Flags));
625
0
        return m_flags;
626
0
    }
627
628
    void set_permanent_flags(Vector<ByteString>&& flags)
629
250
    {
630
250
        add_response_type(ResponseType::PermanentFlags);
631
250
        m_permanent_flags = move(flags);
632
250
    }
633
634
    Vector<ByteString>& permanent_flags()
635
0
    {
636
0
        VERIFY(contains_response_type(ResponseType::PermanentFlags));
637
0
        return m_permanent_flags;
638
0
    }
639
640
    void add_fetch_response(unsigned message, FetchResponseData&& data)
641
186k
    {
642
186k
        add_response_type(ResponseType::Fetch);
643
186k
        m_fetch_responses.append(Tuple<unsigned, FetchResponseData> { move(message), move(data) });
644
186k
    }
645
646
    Vector<Tuple<unsigned, FetchResponseData>>& fetch_data()
647
0
    {
648
0
        VERIFY(contains_response_type(ResponseType::Fetch));
649
0
        return m_fetch_responses;
650
0
    }
651
652
    void set_search_results(Vector<unsigned>&& results)
653
0
    {
654
0
        add_response_type(ResponseType::Search);
655
0
        m_search_results = move(results);
656
0
    }
657
658
    Vector<unsigned>& search_results()
659
0
    {
660
0
        VERIFY(contains_response_type(ResponseType::Search));
661
0
        return m_search_results;
662
0
    }
663
664
    void add_expunged(unsigned message)
665
162k
    {
666
162k
        add_response_type(ResponseType::Expunged);
667
162k
        m_expunged.append(message);
668
162k
    }
669
670
    Vector<unsigned>& expunged()
671
0
    {
672
0
        VERIFY(contains_response_type(ResponseType::Expunged));
673
0
        return m_expunged;
674
0
    }
675
676
    void set_bye(Optional<ByteString> message)
677
6.06k
    {
678
6.06k
        add_response_type(ResponseType::Bye);
679
6.06k
        m_bye_message = move(message);
680
6.06k
    }
681
682
    Optional<ByteString>& bye_message()
683
0
    {
684
0
        VERIFY(contains_response_type(ResponseType::Bye));
685
0
        return m_bye_message;
686
0
    }
687
688
    void add_status_item(StatusItem&& item)
689
0
    {
690
0
        add_response_type(ResponseType::Status);
691
0
        m_status_items.append(move(item));
692
0
    }
693
694
    Vector<StatusItem>& status_items()
695
0
    {
696
0
        VERIFY(contains_response_type(ResponseType::Status));
697
0
        return m_status_items;
698
0
    }
699
700
private:
701
    unsigned m_response_type;
702
703
    Vector<ByteString> m_capabilities;
704
    Vector<ListItem> m_list_items;
705
    Vector<ListItem> m_lsub_items;
706
    Vector<StatusItem> m_status_items;
707
    Vector<unsigned> m_expunged;
708
709
    unsigned m_recent {};
710
    unsigned m_exists {};
711
712
    unsigned m_uid_next {};
713
    unsigned m_uid_validity {};
714
    unsigned m_unseen {};
715
    Vector<ByteString> m_permanent_flags;
716
    Vector<ByteString> m_flags;
717
    Vector<Tuple<unsigned, FetchResponseData>> m_fetch_responses;
718
    Vector<unsigned> m_search_results;
719
    Optional<ByteString> m_bye_message;
720
};
721
722
enum class StoreMethod {
723
    Replace,
724
    Add,
725
    Remove
726
};
727
728
class SolidResponse {
729
    // Parser is allowed to set up fields
730
    friend class Parser;
731
732
public:
733
0
    ResponseStatus status() { return m_status; }
734
735
0
    int tag() const { return m_tag; }
736
737
1.04M
    ResponseData& data() { return m_data; }
738
739
0
    ByteString response_text() { return m_response_text; }
740
741
    SolidResponse()
742
17.6k
        : SolidResponse(ResponseStatus::Bad, -1)
743
17.6k
    {
744
17.6k
    }
745
746
    SolidResponse(ResponseStatus status, int tag)
747
17.6k
        : m_status(status)
748
17.6k
        , m_tag(tag)
749
17.6k
        , m_data(ResponseData())
750
17.6k
    {
751
17.6k
    }
752
753
private:
754
    ResponseStatus m_status;
755
    ByteString m_response_text;
756
    unsigned m_tag;
757
758
    ResponseData m_data;
759
};
760
761
struct ContinueRequest {
762
    ByteString data;
763
};
764
765
using Response = Variant<SolidResponse, ContinueRequest>;
766
}
767
768
// An RFC 2822 message
769
// https://datatracker.ietf.org/doc/html/rfc2822
770
struct Message {
771
    ByteString data;
772
};