/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 | | }; |