Objects.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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.begin(), string.end(), [&](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.to_string();
  123. escaped_str.replace("\\", "\\\\");
  124. escaped_str.replace("\"", "\\\"");
  125. return String::formatted("\"{}\"", escaped_str);
  126. }
  127. // Just send a literal
  128. return String::formatted("{{{}}}\r\n{}", string.length(), string);
  129. }
  130. String SearchKey::serialize() const
  131. {
  132. return data.visit(
  133. [&](Empty const&) { VERIFY_NOT_REACHED(); return String("The compiler complains if you remove this."); },
  134. [&](All const&) { return String("ALL"); },
  135. [&](Answered const&) { return String("ANSWERED"); },
  136. [&](Bcc const& x) { return String::formatted("BCC {}", serialize_astring(x.bcc)); },
  137. [&](Cc const& x) { return String::formatted("CC {}", serialize_astring(x.cc)); },
  138. [&](Deleted const&) { return String("DELETED"); },
  139. [&](Draft const&) { return String("DRAFT"); },
  140. [&](From const& x) { return String::formatted("FROM {}", serialize_astring(x.from)); },
  141. [&](Header const& x) { return String::formatted("HEADER {} {}", serialize_astring(x.header), serialize_astring(x.value)); },
  142. [&](Keyword const& x) { return String::formatted("KEYWORD {}", x.keyword); },
  143. [&](Larger const& x) { return String::formatted("LARGER {}", x.number); },
  144. [&](New const&) { return String("NEW"); },
  145. [&](Not const& x) { return String::formatted("NOT {}", x.operand->serialize()); },
  146. [&](Old const&) { return String("OLD"); },
  147. [&](On const& x) { return String::formatted("ON {}", x.date.to_string("%d-%b-%Y")); },
  148. [&](Or const& x) { return String::formatted("OR {} {}", x.lhs->serialize(), x.rhs->serialize()); },
  149. [&](Recent const&) { return String("RECENT"); },
  150. [&](SearchKeys const& x) {
  151. StringBuilder sb;
  152. sb.append("(");
  153. bool first = true;
  154. for (const auto& item : x.keys) {
  155. if (!first)
  156. sb.append(", ");
  157. sb.append(item->serialize());
  158. first = false;
  159. }
  160. return sb.build();
  161. },
  162. [&](Seen const&) { return String("SEEN"); },
  163. [&](SentBefore const& x) { return String::formatted("SENTBEFORE {}", x.date.to_string("%d-%b-%Y")); },
  164. [&](SentOn const& x) { return String::formatted("SENTON {}", x.date.to_string("%d-%b-%Y")); },
  165. [&](SentSince const& x) { return String::formatted("SENTSINCE {}", x.date.to_string("%d-%b-%Y")); },
  166. [&](SequenceSet const& x) { return x.sequence.serialize(); },
  167. [&](Since const& x) { return String::formatted("SINCE {}", x.date.to_string("%d-%b-%Y")); },
  168. [&](Smaller const& x) { return String::formatted("SMALLER {}", x.number); },
  169. [&](Subject const& x) { return String::formatted("SUBJECT {}", serialize_astring(x.subject)); },
  170. [&](Text const& x) { return String::formatted("TEXT {}", serialize_astring(x.text)); },
  171. [&](To const& x) { return String::formatted("TO {}", serialize_astring(x.to)); },
  172. [&](UID const& x) { return String::formatted("UID {}", x.uid); },
  173. [&](Unanswered const&) { return String("UNANSWERED"); },
  174. [&](Undeleted const&) { return String("UNDELETED"); },
  175. [&](Undraft const&) { return String("UNDRAFT"); },
  176. [&](Unkeyword const& x) { return String::formatted("UNKEYWORD {}", serialize_astring(x.flag_keyword)); },
  177. [&](Unseen const&) { return String("UNSEEN"); });
  178. }
  179. }