Client.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. /*
  2. * Copyright (c) 2021, Kyle Pereira <hey@xylepereira.me>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/OwnPtr.h>
  7. #include <LibCore/Stream.h>
  8. #include <LibIMAP/Client.h>
  9. namespace IMAP {
  10. Client::Client(StringView host, u16 port, NonnullOwnPtr<Core::Stream::Socket> socket)
  11. : m_host(host)
  12. , m_port(port)
  13. , m_socket(move(socket))
  14. , m_connect_pending(Promise<Empty>::construct())
  15. {
  16. setup_callbacks();
  17. }
  18. Client::Client(Client&& other)
  19. : m_host(other.m_host)
  20. , m_port(other.m_port)
  21. , m_socket(move(other.m_socket))
  22. , m_connect_pending(move(other.m_connect_pending))
  23. {
  24. setup_callbacks();
  25. }
  26. void Client::setup_callbacks()
  27. {
  28. m_socket->on_ready_to_read = [&] {
  29. auto maybe_error = on_ready_to_receive();
  30. if (maybe_error.is_error()) {
  31. dbgln("Error receiving from the socket: {}", maybe_error.error());
  32. close();
  33. }
  34. };
  35. }
  36. ErrorOr<NonnullOwnPtr<Client>> Client::connect_tls(StringView host, u16 port)
  37. {
  38. auto tls_socket = TRY(TLS::TLSv12::connect(host, port));
  39. dbgln("connecting to {}:{}", host, port);
  40. return adopt_nonnull_own_or_enomem(new (nothrow) Client(host, port, move(tls_socket)));
  41. }
  42. ErrorOr<NonnullOwnPtr<Client>> Client::connect_plaintext(StringView host, u16 port)
  43. {
  44. auto socket = TRY(Core::Stream::TCPSocket::connect(host, port));
  45. dbgln("Connected to {}:{}", host, port);
  46. return adopt_nonnull_own_or_enomem(new (nothrow) Client(host, port, move(socket)));
  47. }
  48. ErrorOr<void> Client::on_ready_to_receive()
  49. {
  50. if (!TRY(m_socket->can_read_without_blocking()))
  51. return {};
  52. auto pending_bytes = TRY(m_socket->pending_bytes());
  53. auto receive_buffer = TRY(m_buffer.get_bytes_for_writing(pending_bytes));
  54. TRY(m_socket->read(receive_buffer));
  55. // Once we get server hello we can start sending.
  56. if (m_connect_pending) {
  57. m_connect_pending->resolve({});
  58. m_connect_pending.clear();
  59. m_buffer.clear();
  60. return {};
  61. }
  62. if (m_buffer[m_buffer.size() - 1] == '\n') {
  63. // Don't try parsing until we have a complete line.
  64. auto response = m_parser.parse(move(m_buffer), m_expecting_response);
  65. TRY(handle_parsed_response(move(response)));
  66. m_buffer.clear();
  67. }
  68. return {};
  69. }
  70. static ReadonlyBytes command_byte_buffer(CommandType command)
  71. {
  72. switch (command) {
  73. case CommandType::Noop:
  74. return "NOOP"sv.bytes();
  75. case CommandType::Capability:
  76. return "CAPABILITY"sv.bytes();
  77. case CommandType::Logout:
  78. return "LOGOUT"sv.bytes();
  79. case CommandType ::Idle:
  80. return "IDLE"sv.bytes();
  81. case CommandType::Login:
  82. return "LOGIN"sv.bytes();
  83. case CommandType::List:
  84. return "LIST"sv.bytes();
  85. case CommandType::Select:
  86. return "SELECT"sv.bytes();
  87. case CommandType::Fetch:
  88. return "FETCH"sv.bytes();
  89. case CommandType::Store:
  90. return "STORE"sv.bytes();
  91. case CommandType::Copy:
  92. return "COPY"sv.bytes();
  93. case CommandType::Create:
  94. return "CREATE"sv.bytes();
  95. case CommandType::Delete:
  96. return "DELETE"sv.bytes();
  97. case CommandType::Search:
  98. return "SEARCH"sv.bytes();
  99. case CommandType::UIDFetch:
  100. return "UID FETCH"sv.bytes();
  101. case CommandType::UIDStore:
  102. return "UID STORE"sv.bytes();
  103. case CommandType::UIDCopy:
  104. return "UID COPY"sv.bytes();
  105. case CommandType::UIDSearch:
  106. return "UID SEARCH"sv.bytes();
  107. case CommandType::Append:
  108. return "APPEND"sv.bytes();
  109. case CommandType::Examine:
  110. return "EXAMINE"sv.bytes();
  111. case CommandType::ListSub:
  112. return "LSUB"sv.bytes();
  113. case CommandType::Expunge:
  114. return "EXPUNGE"sv.bytes();
  115. case CommandType::Subscribe:
  116. return "SUBSCRIBE"sv.bytes();
  117. case CommandType::Unsubscribe:
  118. return "UNSUBSCRIBE"sv.bytes();
  119. case CommandType::Authenticate:
  120. return "AUTHENTICATE"sv.bytes();
  121. case CommandType::Check:
  122. return "CHECK"sv.bytes();
  123. case CommandType::Close:
  124. return "CLOSE"sv.bytes();
  125. case CommandType::Rename:
  126. return "RENAME"sv.bytes();
  127. case CommandType::Status:
  128. return "STATUS"sv.bytes();
  129. }
  130. VERIFY_NOT_REACHED();
  131. }
  132. ErrorOr<void> Client::send_raw(StringView data)
  133. {
  134. TRY(m_socket->write(data.bytes()));
  135. TRY(m_socket->write("\r\n"sv.bytes()));
  136. return {};
  137. }
  138. RefPtr<Promise<Optional<Response>>> Client::send_command(Command&& command)
  139. {
  140. m_command_queue.append(move(command));
  141. m_current_command++;
  142. auto promise = Promise<Optional<Response>>::construct();
  143. m_pending_promises.append(promise);
  144. if (m_pending_promises.size() == 1)
  145. MUST(send_next_command());
  146. return promise;
  147. }
  148. template<typename T>
  149. RefPtr<Promise<Optional<T>>> cast_promise(RefPtr<Promise<Optional<Response>>> promise_variant)
  150. {
  151. auto new_promise = promise_variant->map<Optional<T>>(
  152. [](Optional<Response>& variant) {
  153. return variant.has_value() ? move(variant->get<T>()) : Optional<T>();
  154. });
  155. return new_promise;
  156. }
  157. RefPtr<Promise<Optional<SolidResponse>>> Client::login(StringView username, StringView password)
  158. {
  159. auto command = Command { CommandType::Login, m_current_command, { serialize_astring(username), serialize_astring(password) } };
  160. return cast_promise<SolidResponse>(send_command(move(command)));
  161. }
  162. RefPtr<Promise<Optional<SolidResponse>>> Client::list(StringView reference_name, StringView mailbox)
  163. {
  164. auto command = Command { CommandType::List, m_current_command,
  165. { DeprecatedString::formatted("\"{}\"", reference_name),
  166. DeprecatedString::formatted("\"{}\"", mailbox) } };
  167. return cast_promise<SolidResponse>(send_command(move(command)));
  168. }
  169. RefPtr<Promise<Optional<SolidResponse>>> Client::lsub(StringView reference_name, StringView mailbox)
  170. {
  171. auto command = Command { CommandType::ListSub, m_current_command,
  172. { DeprecatedString::formatted("\"{}\"", reference_name),
  173. DeprecatedString::formatted("\"{}\"", mailbox) } };
  174. return cast_promise<SolidResponse>(send_command(move(command)));
  175. }
  176. RefPtr<Promise<Optional<SolidResponse>>> Client::fetch(FetchCommand request, bool uid)
  177. {
  178. auto command = Command { uid ? CommandType::UIDFetch : CommandType::Fetch, m_current_command, { request.serialize() } };
  179. return cast_promise<SolidResponse>(send_command(move(command)));
  180. }
  181. RefPtr<Promise<Optional<Response>>> Client::send_simple_command(CommandType type)
  182. {
  183. auto command = Command { type, m_current_command, {} };
  184. return send_command(move(command));
  185. }
  186. RefPtr<Promise<Optional<SolidResponse>>> Client::select(StringView string)
  187. {
  188. auto command = Command { CommandType::Select, m_current_command, { serialize_astring(string) } };
  189. return cast_promise<SolidResponse>(send_command(move(command)));
  190. }
  191. ErrorOr<void> Client::handle_parsed_response(ParseStatus&& parse_status)
  192. {
  193. if (!m_expecting_response) {
  194. if (!parse_status.successful) {
  195. dbgln("Parsing failed on unrequested data!");
  196. } else if (parse_status.response.has_value()) {
  197. unrequested_response_callback(move(parse_status.response.value().get<SolidResponse>().data()));
  198. }
  199. } else {
  200. bool should_send_next = false;
  201. if (!parse_status.successful) {
  202. m_expecting_response = false;
  203. m_pending_promises.first()->resolve({});
  204. m_pending_promises.remove(0);
  205. }
  206. if (parse_status.response.has_value()) {
  207. m_expecting_response = false;
  208. should_send_next = parse_status.response->has<SolidResponse>();
  209. m_pending_promises.first()->resolve(move(parse_status.response));
  210. m_pending_promises.remove(0);
  211. }
  212. if (should_send_next && !m_command_queue.is_empty()) {
  213. TRY(send_next_command());
  214. }
  215. }
  216. return {};
  217. }
  218. ErrorOr<void> Client::send_next_command()
  219. {
  220. auto command = m_command_queue.take_first();
  221. ByteBuffer buffer;
  222. auto tag = AK::DeprecatedString::formatted("A{} ", m_current_command);
  223. buffer += tag.to_byte_buffer();
  224. auto command_type = command_byte_buffer(command.type);
  225. buffer.append(command_type.data(), command_type.size());
  226. for (auto& arg : command.args) {
  227. buffer.append(" ", 1);
  228. buffer.append(arg.bytes().data(), arg.length());
  229. }
  230. TRY(send_raw(buffer));
  231. m_expecting_response = true;
  232. return {};
  233. }
  234. RefPtr<Promise<Optional<SolidResponse>>> Client::examine(StringView string)
  235. {
  236. auto command = Command { CommandType::Examine, m_current_command, { serialize_astring(string) } };
  237. return cast_promise<SolidResponse>(send_command(move(command)));
  238. }
  239. RefPtr<Promise<Optional<SolidResponse>>> Client::create_mailbox(StringView name)
  240. {
  241. auto command = Command { CommandType::Create, m_current_command, { serialize_astring(name) } };
  242. return cast_promise<SolidResponse>(send_command(move(command)));
  243. }
  244. RefPtr<Promise<Optional<SolidResponse>>> Client::delete_mailbox(StringView name)
  245. {
  246. auto command = Command { CommandType::Delete, m_current_command, { serialize_astring(name) } };
  247. return cast_promise<SolidResponse>(send_command(move(command)));
  248. }
  249. RefPtr<Promise<Optional<SolidResponse>>> Client::store(StoreMethod method, Sequence sequence_set, bool silent, Vector<DeprecatedString> const& flags, bool uid)
  250. {
  251. StringBuilder data_item_name;
  252. switch (method) {
  253. case StoreMethod::Replace:
  254. data_item_name.append("FLAGS"sv);
  255. break;
  256. case StoreMethod::Add:
  257. data_item_name.append("+FLAGS"sv);
  258. break;
  259. case StoreMethod::Remove:
  260. data_item_name.append("-FLAGS"sv);
  261. break;
  262. }
  263. if (silent) {
  264. data_item_name.append(".SILENT"sv);
  265. }
  266. StringBuilder flags_builder;
  267. flags_builder.append('(');
  268. flags_builder.join(' ', flags);
  269. flags_builder.append(')');
  270. auto command = Command { uid ? CommandType::UIDStore : CommandType::Store, m_current_command, { sequence_set.serialize(), data_item_name.build(), flags_builder.build() } };
  271. return cast_promise<SolidResponse>(send_command(move(command)));
  272. }
  273. RefPtr<Promise<Optional<SolidResponse>>> Client::search(Optional<DeprecatedString> charset, Vector<SearchKey>&& keys, bool uid)
  274. {
  275. Vector<DeprecatedString> args;
  276. if (charset.has_value()) {
  277. args.append("CHARSET "sv);
  278. args.append(charset.value());
  279. }
  280. for (auto const& item : keys) {
  281. args.append(item.serialize());
  282. }
  283. auto command = Command { uid ? CommandType::UIDSearch : CommandType::Search, m_current_command, args };
  284. return cast_promise<SolidResponse>(send_command(move(command)));
  285. }
  286. RefPtr<Promise<Optional<ContinueRequest>>> Client::idle()
  287. {
  288. auto promise = send_simple_command(CommandType::Idle);
  289. return cast_promise<ContinueRequest>(promise);
  290. }
  291. RefPtr<Promise<Optional<SolidResponse>>> Client::finish_idle()
  292. {
  293. auto promise = Promise<Optional<Response>>::construct();
  294. m_pending_promises.append(promise);
  295. MUST(send_raw("DONE"sv));
  296. m_expecting_response = true;
  297. return cast_promise<SolidResponse>(promise);
  298. }
  299. RefPtr<Promise<Optional<SolidResponse>>> Client::status(StringView mailbox, Vector<StatusItemType> const& types)
  300. {
  301. Vector<DeprecatedString> args;
  302. for (auto type : types) {
  303. switch (type) {
  304. case StatusItemType::Recent:
  305. args.append("RECENT"sv);
  306. break;
  307. case StatusItemType::UIDNext:
  308. args.append("UIDNEXT"sv);
  309. break;
  310. case StatusItemType::UIDValidity:
  311. args.append("UIDVALIDITY"sv);
  312. break;
  313. case StatusItemType::Unseen:
  314. args.append("UNSEEN"sv);
  315. break;
  316. case StatusItemType::Messages:
  317. args.append("MESSAGES"sv);
  318. break;
  319. }
  320. }
  321. StringBuilder types_list;
  322. types_list.append('(');
  323. types_list.join(' ', args);
  324. types_list.append(')');
  325. auto command = Command { CommandType::Status, m_current_command, { mailbox, types_list.build() } };
  326. return cast_promise<SolidResponse>(send_command(move(command)));
  327. }
  328. RefPtr<Promise<Optional<SolidResponse>>> Client::append(StringView mailbox, Message&& message, Optional<Vector<DeprecatedString>> flags, Optional<Core::DateTime> date_time)
  329. {
  330. Vector<DeprecatedString> args = { mailbox };
  331. if (flags.has_value()) {
  332. StringBuilder flags_sb;
  333. flags_sb.append('(');
  334. flags_sb.join(' ', flags.value());
  335. flags_sb.append(')');
  336. args.append(flags_sb.build());
  337. }
  338. if (date_time.has_value())
  339. args.append(date_time.value().to_deprecated_string("\"%d-%b-%Y %H:%M:%S +0000\""sv));
  340. args.append(DeprecatedString::formatted("{{{}}}", message.data.length()));
  341. auto continue_req = send_command(Command { CommandType::Append, m_current_command, args });
  342. auto response_promise = Promise<Optional<Response>>::construct();
  343. m_pending_promises.append(response_promise);
  344. continue_req->on_resolved = [this, message2 { move(message) }](auto& data) {
  345. if (!data.has_value()) {
  346. MUST(handle_parsed_response({ .successful = false, .response = {} }));
  347. } else {
  348. MUST(send_raw(message2.data));
  349. m_expecting_response = true;
  350. }
  351. };
  352. return cast_promise<SolidResponse>(response_promise);
  353. }
  354. RefPtr<Promise<Optional<SolidResponse>>> Client::subscribe(StringView mailbox)
  355. {
  356. auto command = Command { CommandType::Subscribe, m_current_command, { serialize_astring(mailbox) } };
  357. return cast_promise<SolidResponse>(send_command(move(command)));
  358. }
  359. RefPtr<Promise<Optional<SolidResponse>>> Client::unsubscribe(StringView mailbox)
  360. {
  361. auto command = Command { CommandType::Unsubscribe, m_current_command, { serialize_astring(mailbox) } };
  362. return cast_promise<SolidResponse>(send_command(move(command)));
  363. }
  364. RefPtr<Promise<Optional<Response>>> Client::authenticate(StringView method)
  365. {
  366. auto command = Command { CommandType::Authenticate, m_current_command, { method } };
  367. return send_command(move(command));
  368. }
  369. RefPtr<Promise<Optional<SolidResponse>>> Client::rename(StringView from, StringView to)
  370. {
  371. auto command = Command { CommandType::Rename, m_current_command, { serialize_astring(from), serialize_astring(to) } };
  372. return cast_promise<SolidResponse>(send_command(move(command)));
  373. }
  374. RefPtr<Promise<Optional<SolidResponse>>> Client::copy(Sequence sequence_set, StringView name, bool uid)
  375. {
  376. auto command = Command {
  377. uid ? CommandType::UIDCopy : CommandType::Copy, m_current_command, { sequence_set.serialize(), serialize_astring(name) }
  378. };
  379. return cast_promise<SolidResponse>(send_command(move(command)));
  380. }
  381. void Client::close()
  382. {
  383. m_socket->close();
  384. }
  385. bool Client::is_open()
  386. {
  387. return m_socket->is_open();
  388. }
  389. }