Objects.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /*
  2. * Copyright (c) 2021, Kyle Pereira <hey@xylepereira.me>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/CharacterTypes.h>
  7. #include <LibIMAP/Objects.h>
  8. namespace IMAP {
  9. String Sequence::serialize() const
  10. {
  11. if (start == end) {
  12. return AK::String::formatted("{}", start);
  13. } else {
  14. auto start_char = start != -1 ? String::formatted("{}", start) : "*";
  15. auto end_char = end != -1 ? String::formatted("{}", end) : "*";
  16. return String::formatted("{}:{}", start_char, end_char);
  17. }
  18. }
  19. String FetchCommand::DataItem::Section::serialize() const
  20. {
  21. StringBuilder headers_builder;
  22. switch (type) {
  23. case SectionType::Header:
  24. return "HEADER";
  25. case SectionType::HeaderFields:
  26. case SectionType::HeaderFieldsNot: {
  27. if (type == SectionType::HeaderFields)
  28. headers_builder.append("HEADER.FIELDS (");
  29. else
  30. headers_builder.append("HEADERS.FIELDS.NOT (");
  31. bool first = true;
  32. for (auto& field : headers.value()) {
  33. if (!first)
  34. headers_builder.append(" ");
  35. headers_builder.append(field);
  36. first = false;
  37. }
  38. headers_builder.append(")");
  39. return headers_builder.build();
  40. }
  41. case SectionType::Text:
  42. return "TEXT";
  43. case SectionType::Parts: {
  44. StringBuilder sb;
  45. bool first = true;
  46. for (int part : parts.value()) {
  47. if (!first)
  48. sb.append(".");
  49. sb.appendff("{}", part);
  50. first = false;
  51. }
  52. if (ends_with_mime) {
  53. sb.append(".MIME");
  54. }
  55. return sb.build();
  56. }
  57. }
  58. VERIFY_NOT_REACHED();
  59. }
  60. String FetchCommand::DataItem::serialize() const
  61. {
  62. switch (type) {
  63. case DataItemType::Envelope:
  64. return "ENVELOPE";
  65. case DataItemType::Flags:
  66. return "FLAGS";
  67. case DataItemType::InternalDate:
  68. return "INTERNALDATE";
  69. case DataItemType::UID:
  70. return "UID";
  71. case DataItemType::PeekBody:
  72. TODO();
  73. case DataItemType::BodySection: {
  74. StringBuilder sb;
  75. sb.appendff("BODY[{}]", section.value().serialize());
  76. if (partial_fetch) {
  77. sb.appendff("<{}.{}>", start, octets);
  78. }
  79. return sb.build();
  80. }
  81. case DataItemType::BodyStructure:
  82. return "BODYSTRUCTURE";
  83. }
  84. VERIFY_NOT_REACHED();
  85. }
  86. String FetchCommand::serialize()
  87. {
  88. StringBuilder sequence_builder;
  89. bool first = true;
  90. for (auto& sequence : sequence_set) {
  91. if (!first) {
  92. sequence_builder.append(",");
  93. }
  94. sequence_builder.append(sequence.serialize());
  95. first = false;
  96. }
  97. StringBuilder data_items_builder;
  98. first = true;
  99. for (auto& data_item : data_items) {
  100. if (!first) {
  101. data_items_builder.append(" ");
  102. }
  103. data_items_builder.append(data_item.serialize());
  104. first = false;
  105. }
  106. return AK::String::formatted("{} ({})", sequence_builder.build(), data_items_builder.build());
  107. }
  108. String serialize_astring(StringView string)
  109. {
  110. // Try to send an atom
  111. auto is_non_atom_char = [](char x) {
  112. auto non_atom_chars = { '(', ')', '{', ' ', '%', '*', '"', '\\', ']' };
  113. return AK::find(non_atom_chars.begin(), non_atom_chars.end(), x) != non_atom_chars.end();
  114. };
  115. auto is_atom = all_of(string, [&](auto ch) { return is_ascii_control(ch) && !is_non_atom_char(ch); });
  116. if (is_atom) {
  117. return string;
  118. }
  119. // Try to quote
  120. auto can_be_quoted = !(string.contains('\n') || string.contains('\r'));
  121. if (can_be_quoted) {
  122. auto escaped_str = string.replace("\\", "\\\\", ReplaceMode::FirstOnly).replace("\"", "\\\"", ReplaceMode::FirstOnly);
  123. return String::formatted("\"{}\"", escaped_str);
  124. }
  125. // Just send a literal
  126. return String::formatted("{{{}}}\r\n{}", string.length(), string);
  127. }
  128. String SearchKey::serialize() const
  129. {
  130. return data.visit(
  131. [&](All const&) { return String("ALL"); },
  132. [&](Answered const&) { return String("ANSWERED"); },
  133. [&](Bcc const& x) { return String::formatted("BCC {}", serialize_astring(x.bcc)); },
  134. [&](Cc const& x) { return String::formatted("CC {}", serialize_astring(x.cc)); },
  135. [&](Deleted const&) { return String("DELETED"); },
  136. [&](Draft const&) { return String("DRAFT"); },
  137. [&](From const& x) { return String::formatted("FROM {}", serialize_astring(x.from)); },
  138. [&](Header const& x) { return String::formatted("HEADER {} {}", serialize_astring(x.header), serialize_astring(x.value)); },
  139. [&](Keyword const& x) { return String::formatted("KEYWORD {}", x.keyword); },
  140. [&](Larger const& x) { return String::formatted("LARGER {}", x.number); },
  141. [&](New const&) { return String("NEW"); },
  142. [&](Not const& x) { return String::formatted("NOT {}", x.operand->serialize()); },
  143. [&](Old const&) { return String("OLD"); },
  144. [&](On const& x) { return String::formatted("ON {}", x.date.to_string("%d-%b-%Y")); },
  145. [&](Or const& x) { return String::formatted("OR {} {}", x.lhs->serialize(), x.rhs->serialize()); },
  146. [&](Recent const&) { return String("RECENT"); },
  147. [&](SearchKeys const& x) {
  148. StringBuilder sb;
  149. sb.append("(");
  150. bool first = true;
  151. for (const auto& item : x.keys) {
  152. if (!first)
  153. sb.append(", ");
  154. sb.append(item->serialize());
  155. first = false;
  156. }
  157. return sb.build();
  158. },
  159. [&](Seen const&) { return String("SEEN"); },
  160. [&](SentBefore const& x) { return String::formatted("SENTBEFORE {}", x.date.to_string("%d-%b-%Y")); },
  161. [&](SentOn const& x) { return String::formatted("SENTON {}", x.date.to_string("%d-%b-%Y")); },
  162. [&](SentSince const& x) { return String::formatted("SENTSINCE {}", x.date.to_string("%d-%b-%Y")); },
  163. [&](SequenceSet const& x) { return x.sequence.serialize(); },
  164. [&](Since const& x) { return String::formatted("SINCE {}", x.date.to_string("%d-%b-%Y")); },
  165. [&](Smaller const& x) { return String::formatted("SMALLER {}", x.number); },
  166. [&](Subject const& x) { return String::formatted("SUBJECT {}", serialize_astring(x.subject)); },
  167. [&](Text const& x) { return String::formatted("TEXT {}", serialize_astring(x.text)); },
  168. [&](To const& x) { return String::formatted("TO {}", serialize_astring(x.to)); },
  169. [&](UID const& x) { return String::formatted("UID {}", x.uid); },
  170. [&](Unanswered const&) { return String("UNANSWERED"); },
  171. [&](Undeleted const&) { return String("UNDELETED"); },
  172. [&](Undraft const&) { return String("UNDRAFT"); },
  173. [&](Unkeyword const& x) { return String::formatted("UNKEYWORD {}", serialize_astring(x.flag_keyword)); },
  174. [&](Unseen const&) { return String("UNSEEN"); });
  175. }
  176. }