1use core::{
8 any::Any,
9 fmt,
10 future::Future,
11 num::{NonZeroU32, NonZeroU64},
12 sync::atomic::{AtomicBool, Ordering},
13 time::Duration,
14};
15
16use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, string::String, vec, vec::Vec};
17
18use std::{
19 io::{self, Read, Write},
20 sync::{
21 Arc,
22 mpsc::{self, Receiver, RecvTimeoutError, TryRecvError},
23 },
24 thread::{self, JoinHandle},
25};
26
27use imap_codec::{
28 fragmentizer::Fragmentizer,
29 imap_types::{
30 command::SelectParameter,
31 core::{IString, NString, Vec1},
32 extensions::{
33 enable::CapabilityEnable,
34 sort::SortCriterion,
35 thread::{Thread, ThreadingAlgorithm},
36 },
37 fetch::{MacroOrMessageDataItemNames, MessageDataItem},
38 flag::{Flag, StoreType},
39 mailbox::{ListMailbox, Mailbox},
40 response::Capability,
41 search::SearchKey,
42 sequence::SequenceSet,
43 status::{StatusDataItem, StatusDataItemName},
44 },
45};
46#[cfg(feature = "scram")]
47use io_sasl::rfc5802::SaslScramCreds;
48use thiserror::Error;
49
50#[cfg(feature = "scram")]
51use crate::rfc7677::auth_scram_sha_256::*;
52use crate::{
53 coroutine::*,
54 rfc2971::id::*,
55 rfc3501::{
56 append::*, append_stream::*, capability::*, check::*, close::*, copy::*, create::*,
57 delete::*, examine::*, expunge::*, fetch::*, fetch_stream::*, fetch_stream_batch::*,
58 greeting::*, list::*, login::*, logout::*, lsub::*, noop::*, raw::*, rename::*, search::*,
59 select::*, starttls::*, status::*, store::*, subscribe::*, unsubscribe::*,
60 },
61 rfc3691::unselect::*,
62 rfc4315::expunge_uid::*,
63 rfc5161::enable::*,
64 rfc5256::{sort::*, thread::*},
65 rfc6851::r#move::*,
66 rfc7628::auth_oauthbearer::*,
67 sasl::{auth_anonymous::*, auth_login::*, auth_plain::*, auth_xoauth2::*},
68 session::*,
69 watch::*,
70};
71
72#[cfg(any(
73 feature = "rustls-aws",
74 feature = "rustls-ring",
75 feature = "native-tls"
76))]
77mod connect;
78
79#[derive(Debug, Error)]
81pub enum ImapClientError {
82 #[error(transparent)]
84 Greeting(#[from] ImapGreetingGetError),
85 #[error(transparent)]
87 Login(#[from] ImapLoginError),
88 #[error(transparent)]
90 AuthLogin(#[from] ImapAuthLoginError),
91 #[error(transparent)]
93 AuthPlain(#[from] ImapAuthPlainError),
94 #[error(transparent)]
96 AuthAnonymous(#[from] ImapAuthAnonymousError),
97 #[error(transparent)]
99 AuthOAuthBearer(#[from] ImapAuthOauthbearerError),
100 #[error(transparent)]
102 AuthXOAuth2(#[from] ImapAuthXoauth2Error),
103 #[cfg(feature = "scram")]
105 #[error(transparent)]
106 AuthScramSha256(#[from] ImapAuthScramSha256Error),
107 #[error(transparent)]
109 SessionOpen(#[from] ImapSessionOpenError),
110 #[error(transparent)]
112 Logout(#[from] ImapLogoutError),
113 #[error(transparent)]
115 Capability(#[from] ImapCapabilityGetError),
116 #[error(transparent)]
118 Noop(#[from] ImapNoopError),
119 #[error(transparent)]
121 Raw(#[from] ImapRawError),
122 #[error(transparent)]
124 ServerId(#[from] ImapServerIdError),
125 #[error(transparent)]
127 ExtensionEnable(#[from] ImapExtensionEnableError),
128 #[error(transparent)]
130 MailboxList(#[from] ImapMailboxListError),
131 #[error(transparent)]
133 MailboxLsub(#[from] ImapMailboxLsubError),
134 #[error(transparent)]
136 MailboxStatus(#[from] ImapMailboxStatusError),
137 #[error(transparent)]
139 MailboxCreate(#[from] ImapMailboxCreateError),
140 #[error(transparent)]
142 MailboxDelete(#[from] ImapMailboxDeleteError),
143 #[error(transparent)]
145 MailboxRename(#[from] ImapMailboxRenameError),
146 #[error(transparent)]
148 MailboxSubscribe(#[from] ImapMailboxSubscribeError),
149 #[error(transparent)]
151 MailboxUnsubscribe(#[from] ImapMailboxUnsubscribeError),
152 #[error(transparent)]
154 MailboxSelect(#[from] ImapMailboxSelectError),
155 #[error(transparent)]
157 MailboxExamine(#[from] ImapMailboxExamineError),
158 #[error(transparent)]
160 MailboxWatch(#[from] ImapMailboxWatchError),
161 #[error(transparent)]
163 MailboxClose(#[from] ImapMailboxCloseError),
164 #[error(transparent)]
166 MailboxUnselect(#[from] ImapMailboxUnselectError),
167 #[error(transparent)]
169 MailboxCheck(#[from] ImapMailboxCheckError),
170 #[error(transparent)]
172 MailboxExpunge(#[from] ImapMailboxExpungeError),
173 #[error(transparent)]
175 MessageExpungeUid(#[from] ImapMessageExpungeUidError),
176 #[error(transparent)]
178 MessageSort(#[from] ImapMessageSortError),
179 #[error(transparent)]
181 MessageFetch(#[from] ImapMessageFetchError),
182 #[error(transparent)]
184 MessageFetchStream(#[from] ImapMessageFetchStreamError),
185 #[error(transparent)]
187 MessageFetchStreamBatch(#[from] ImapMessageFetchStreamBatchError),
188 #[error(transparent)]
190 MessageSearch(#[from] ImapMessageSearchError),
191 #[error(transparent)]
193 MessageStore(#[from] ImapMessageStoreError),
194 #[error(transparent)]
196 MessageCopy(#[from] ImapMessageCopyError),
197 #[error(transparent)]
199 MessageMove(#[from] ImapMessageMoveError),
200 #[error(transparent)]
202 MessageAppend(#[from] ImapMessageAppendError),
203 #[error(transparent)]
205 MessageAppendStream(#[from] ImapMessageAppendStreamError),
206 #[error(transparent)]
208 MessageThread(#[from] ImapMessageThreadError),
209 #[error(transparent)]
211 Io(#[from] io::Error),
212 #[error(transparent)]
214 StartTls(#[from] ImapStartTlsError),
215 #[cfg(any(
217 feature = "rustls-aws",
218 feature = "rustls-ring",
219 feature = "native-tls"
220 ))]
221 #[error(transparent)]
222 Tls(#[from] anyhow::Error),
223 #[error("Invalid IMAP LOGIN credentials")]
225 InvalidLoginCredentials(#[from] imap_codec::imap_types::error::ValidationError),
226 #[error("IMAP server does not advertise QRESYNC capability")]
228 QresyncNotSupported,
229 #[error("Invalid mod-sequence value: 0")]
231 InvalidModSeq,
232 #[error(transparent)]
238 Transport(Box<dyn core::error::Error + Send + Sync>),
239}
240
241macro_rules! imap_client_commands {
250 (
251 $(
252 $(#[$meta:meta])*
253 fn $name:ident($($arg:ident: $ty:ty),* $(,)?) -> $out:ty {
254 $coroutine:expr
255 }
256 )*
257 ) => {
258 pub trait ImapClient {
281 fn run<C, T, E>(&mut self, coroutine: C) -> Result<T, ImapClientError>
284 where
285 C: ImapCoroutine<Yield = ImapYield, Return = Result<T, E>>,
286 ImapClientError: From<E>;
287
288 $(
289 $(#[$meta])*
290 fn $name(&mut self, $($arg: $ty),*) -> Result<$out, ImapClientError> {
291 self.run($coroutine)
292 }
293 )*
294
295 fn login(
297 &mut self,
298 user: &str,
299 password: &str,
300 opts: ImapLoginOptions,
301 ) -> Result<Vec<Capability<'static>>, ImapClientError> {
302 self.run(ImapLogin::new(user, password, opts)?)
303 }
304
305 fn raw(&mut self, command: &[u8]) -> Result<String, ImapClientError> {
314 self.run(ImapRaw::new(command)?)
315 }
316
317 fn select_qresync(
322 &mut self,
323 mailbox: Mailbox<'static>,
324 uid_validity: NonZeroU32,
325 highest_mod_seq: u64,
326 capability: &[Capability<'static>],
327 ) -> Result<ImapMailboxSelectData, ImapClientError> {
328 let parameters = qresync_parameters(uid_validity, highest_mod_seq, capability)?;
329 self.select(mailbox, ImapMailboxSelectOptions { parameters })
330 }
331 }
332
333 pub trait ImapClientAsync: Send {
351 fn run<C, T, E>(
354 &mut self,
355 coroutine: C,
356 ) -> impl Future<Output = Result<T, ImapClientError>> + Send
357 where
358 C: ImapCoroutine<Yield = ImapYield, Return = Result<T, E>> + Send,
359 T: Send,
360 E: Send,
361 ImapClientError: From<E>;
362
363 $(
364 $(#[$meta])*
365 fn $name(
366 &mut self,
367 $($arg: $ty),*
368 ) -> impl Future<Output = Result<$out, ImapClientError>> + Send {
369 self.run($coroutine)
370 }
371 )*
372
373 fn login(
375 &mut self,
376 user: &str,
377 password: &str,
378 opts: ImapLoginOptions,
379 ) -> impl Future<Output = Result<Vec<Capability<'static>>, ImapClientError>> + Send
380 {
381 async move { self.run(ImapLogin::new(user, password, opts)?).await }
382 }
383
384 fn raw(
393 &mut self,
394 command: &[u8],
395 ) -> impl Future<Output = Result<String, ImapClientError>> + Send {
396 async move { self.run(ImapRaw::new(command)?).await }
397 }
398
399 fn select_qresync(
404 &mut self,
405 mailbox: Mailbox<'static>,
406 uid_validity: NonZeroU32,
407 highest_mod_seq: u64,
408 capability: &[Capability<'static>],
409 ) -> impl Future<Output = Result<ImapMailboxSelectData, ImapClientError>> + Send
410 {
411 async move {
412 let parameters =
413 qresync_parameters(uid_validity, highest_mod_seq, capability)?;
414 self.select(mailbox, ImapMailboxSelectOptions { parameters })
415 .await
416 }
417 }
418 }
419 };
420}
421
422imap_client_commands! {
423 fn greeting() -> ImapGreetingOk {
428 ImapGreetingGet::new(ImapGreetingGetOptions { ensure_capabilities: true })
429 }
430
431 fn starttls() -> Vec<u8> {
438 ImapStartTls::new()
439 }
440
441 fn auth_anonymous(
443 message: Option<&str>,
444 opts: ImapAuthAnonymousOptions,
445 ) -> Vec<Capability<'static>> {
446 ImapAuthAnonymous::new(message, opts)
447 }
448
449 fn auth_login(
452 user: &str,
453 password: &str,
454 opts: ImapAuthLoginOptions,
455 ) -> Vec<Capability<'static>> {
456 ImapAuthLogin::new(user, password, opts)
457 }
458
459 fn auth_plain(
461 authzid: Option<&str>,
462 authcid: &str,
463 password: &str,
464 opts: ImapAuthPlainOptions,
465 ) -> Vec<Capability<'static>> {
466 ImapAuthPlain::new(authzid, authcid, password, opts)
467 }
468
469 fn auth_oauthbearer(
471 user: &str,
472 host: &str,
473 port: u16,
474 token: &str,
475 opts: ImapAuthOauthbearerOptions,
476 ) -> Vec<Capability<'static>> {
477 ImapAuthOauthbearer::new(user, host, port, token, opts)
478 }
479
480 fn auth_xoauth2(
483 user: &str,
484 token: &str,
485 opts: ImapAuthXoauth2Options,
486 ) -> Vec<Capability<'static>> {
487 ImapAuthXoauth2::new(user, token, opts)
488 }
489
490 #[cfg(feature = "scram")]
497 fn auth_scram_sha256(
498 creds: SaslScramCreds,
499 opts: ImapAuthScramSha256Options,
500 ) -> Vec<Capability<'static>> {
501 ImapAuthScramSha256::new(creds, opts)
502 }
503
504 fn logout() -> () {
506 ImapLogout::new()
507 }
508
509 fn capability() -> Vec<Capability<'static>> {
511 ImapCapabilityGet::new()
512 }
513
514 fn noop() -> () {
517 ImapNoop::new()
518 }
519
520 fn id(
522 opts: ImapServerIdOptions,
523 ) -> Option<Vec<(IString<'static>, NString<'static>)>> {
524 ImapServerId::new(opts)
525 }
526
527 fn enable(
529 capabilities: Vec1<CapabilityEnable<'static>>,
530 ) -> Option<Vec<CapabilityEnable<'static>>> {
531 ImapExtensionEnable::new(capabilities)
532 }
533
534 fn list(
536 reference: Mailbox<'static>,
537 pattern: ListMailbox<'static>,
538 ) -> ImapMailboxListing {
539 ImapMailboxList::new(reference, pattern)
540 }
541
542 fn lsub(
545 reference: Mailbox<'static>,
546 pattern: ListMailbox<'static>,
547 ) -> ImapMailboxListing {
548 ImapMailboxLsub::new(reference, pattern)
549 }
550
551 fn status(
553 mailbox: Mailbox<'static>,
554 item_names: Cow<'static, [StatusDataItemName]>,
555 ) -> Vec<StatusDataItem> {
556 ImapMailboxStatus::new(mailbox, item_names)
557 }
558
559 fn create(mailbox: Mailbox<'static>) -> () {
561 ImapMailboxCreate::new(mailbox)
562 }
563
564 fn delete(mailbox: Mailbox<'static>) -> () {
566 ImapMailboxDelete::new(mailbox)
567 }
568
569 fn rename(from: Mailbox<'static>, to: Mailbox<'static>) -> () {
571 ImapMailboxRename::new(from, to)
572 }
573
574 fn subscribe(mailbox: Mailbox<'static>) -> () {
576 ImapMailboxSubscribe::new(mailbox)
577 }
578
579 fn unsubscribe(mailbox: Mailbox<'static>) -> () {
581 ImapMailboxUnsubscribe::new(mailbox)
582 }
583
584 fn select(
586 mailbox: Mailbox<'static>,
587 opts: ImapMailboxSelectOptions,
588 ) -> ImapMailboxSelectData {
589 ImapMailboxSelect::new(mailbox, opts)
590 }
591
592 fn examine(
594 mailbox: Mailbox<'static>,
595 opts: ImapMailboxExamineOptions,
596 ) -> ImapMailboxSelectData {
597 ImapMailboxExamine::new(mailbox, opts)
598 }
599
600 fn close() -> () {
602 ImapMailboxClose::new()
603 }
604
605 fn unselect() -> () {
607 ImapMailboxUnselect::new()
608 }
609
610 fn check() -> () {
612 ImapMailboxCheck::new()
613 }
614
615 fn expunge() -> Vec<NonZeroU32> {
617 ImapMailboxExpunge::new()
618 }
619
620 fn uid_expunge(sequence_set: SequenceSet) -> Vec<NonZeroU32> {
627 ImapMessageExpungeUid::new(sequence_set)
628 }
629
630 fn fetch(
632 sequence_set: SequenceSet,
633 items: MacroOrMessageDataItemNames<'static>,
634 opts: ImapMessageFetchOptions,
635 ) -> BTreeMap<NonZeroU32, Vec1<MessageDataItem<'static>>> {
636 ImapMessageFetch::new(sequence_set, items, opts)
637 }
638
639 fn search(
641 criteria: Vec1<SearchKey<'static>>,
642 opts: ImapMessageSearchOptions,
643 ) -> Vec<NonZeroU32> {
644 ImapMessageSearch::new(criteria, opts)
645 }
646
647 fn store(
649 sequence_set: SequenceSet,
650 kind: StoreType,
651 flags: Vec<Flag<'static>>,
652 opts: ImapMessageStoreOptions,
653 ) -> BTreeMap<NonZeroU32, Vec1<MessageDataItem<'static>>> {
654 ImapMessageStore::new(sequence_set, kind, flags, opts)
655 }
656
657 fn copy(
660 sequence_set: SequenceSet,
661 mailbox: Mailbox<'static>,
662 opts: ImapMessageCopyOptions,
663 ) -> ImapCopyUid {
664 ImapMessageCopy::new(sequence_set, mailbox, opts)
665 }
666
667 fn r#move(
670 sequence_set: SequenceSet,
671 mailbox: Mailbox<'static>,
672 opts: ImapMessageMoveOptions,
673 ) -> ImapCopyUid {
674 ImapMessageMove::new(sequence_set, mailbox, opts)
675 }
676
677 fn append(
682 mailbox: Mailbox<'static>,
683 message: &[u8],
684 opts: ImapMessageAppendOptions,
685 ) -> ImapMessageAppendOutput {
686 ImapMessageAppend::new(mailbox, message.to_vec(), opts)
687 }
688
689 fn sort(
696 sort_criteria: Vec1<SortCriterion>,
697 search_criteria: Vec1<SearchKey<'static>>,
698 opts: ImapMessageSortOptions,
699 ) -> Vec<NonZeroU32> {
700 ImapMessageSort::new(sort_criteria, search_criteria, opts)
701 }
702
703 fn thread(
705 algorithm: ThreadingAlgorithm<'static>,
706 search_criteria: Vec1<SearchKey<'static>>,
707 opts: ImapMessageThreadOptions,
708 ) -> Vec<Thread> {
709 ImapMessageThread::new(algorithm, search_criteria, opts)
710 }
711}
712
713fn qresync_parameters(
716 uid_validity: NonZeroU32,
717 highest_mod_seq: u64,
718 capability: &[Capability<'static>],
719) -> Result<Vec<SelectParameter>, ImapClientError> {
720 if !capability.contains(&Capability::QResync) {
721 return Err(ImapClientError::QresyncNotSupported);
722 }
723
724 let Some(mod_sequence_value) = NonZeroU64::new(highest_mod_seq) else {
725 return Err(ImapClientError::InvalidModSeq);
726 };
727
728 Ok(vec![SelectParameter::QResync {
729 uid_validity,
730 mod_sequence_value,
731 known_uids: None,
732 seq_match_data: None,
733 }])
734}
735
736const READ_BUFFER_SIZE: usize = 16 * 1024;
737const BODY_COPY_BUFFER_SIZE: usize = 128 * 1024;
743const FRAGMENTIZER_MAX_MESSAGE_SIZE: u32 = 100 * 1024 * 1024;
744
745pub use crate::session::{default_alpn, default_port};
750
751pub struct ImapClientStd {
754 pub stream: Box<dyn ImapStream>,
756 pub fragmentizer: Fragmentizer,
759 pub auto_id: Option<Vec<(IString<'static>, NString<'static>)>>,
765 pub pre_authenticated: bool,
770}
771
772impl ImapClient for ImapClientStd {
773 fn run<C, T, E>(&mut self, mut coroutine: C) -> Result<T, ImapClientError>
774 where
775 C: ImapCoroutine<Yield = ImapYield, Return = Result<T, E>>,
776 ImapClientError: From<E>,
777 {
778 let mut buf = [0u8; READ_BUFFER_SIZE];
779 let mut arg: Option<&[u8]> = None;
780
781 loop {
782 match coroutine.resume(&mut self.fragmentizer, arg.take()) {
783 ImapCoroutineState::Complete(Ok(out)) => return Ok(out),
784 ImapCoroutineState::Complete(Err(err)) => return Err(err.into()),
785 ImapCoroutineState::Yielded(ImapYield::WantsRead) => {
786 let n = self.read_response(&mut buf)?;
787 arg = Some(&buf[..n]);
788 }
789 ImapCoroutineState::Yielded(ImapYield::WantsWrite(bytes)) => {
790 self.stream.write_all(&bytes)?;
791 arg = None;
792 }
793 }
794 }
795 }
796}
797
798impl ImapClientStd {
799 pub fn new<S: ImapStream + 'static>(stream: S) -> Self {
802 Self {
803 stream: Box::new(stream),
804 fragmentizer: Fragmentizer::new(FRAGMENTIZER_MAX_MESSAGE_SIZE),
805 auto_id: None,
806 pre_authenticated: false,
807 }
808 }
809
810 fn read_response(&mut self, buf: &mut [u8]) -> Result<usize, ImapClientError> {
818 match self.stream.read(buf)? {
819 0 => {
820 let kind = io::ErrorKind::UnexpectedEof;
821 let err = io::Error::new(kind, "IMAP server closed the connection");
822 Err(err.into())
823 }
824 n => Ok(n),
825 }
826 }
827
828 pub fn set_stream<S: ImapStream + 'static>(&mut self, stream: S) {
830 self.stream = Box::new(stream);
831 }
832
833 pub fn watch_mailbox(
840 self,
841 mailbox: Mailbox<'static>,
842 capability: &[Capability<'static>],
843 opts: ImapMailboxWatchStreamOptions,
844 ) -> Result<ImapMailboxWatchStream, ImapClientError> {
845 let shutdown = Arc::new(AtomicBool::new(false));
846 let watch_opts = ImapMailboxWatchOptions {
847 idle_timeout: opts.idle_timeout,
848 poll: opts.poll.is_some(),
849 };
850 let mut watcher = ImapMailboxWatch::new(capability, mailbox, shutdown.clone(), watch_opts);
851 let mut fragmentizer = self.fragmentizer;
852 let mut stream = self.stream;
853
854 stream.set_read_timeout(Some(opts.shutdown_poll))?;
855 stream.stop_retrying();
856
857 let (tx, rx) = mpsc::sync_channel::<Result<ImapMailboxWatchEvent, ImapClientError>>(256);
858 let shutdown_handle = shutdown.clone();
859 let handle = thread::spawn(move || {
860 let mut buf = [0u8; READ_BUFFER_SIZE];
861 let mut arg: Option<Vec<u8>> = None;
862
863 loop {
864 match watcher.resume(&mut fragmentizer, arg.as_deref()) {
865 ImapCoroutineState::Yielded(ImapMailboxWatchYield::Event(e)) => {
866 arg = None;
867 if tx.send(Ok(e)).is_err() {
868 return;
869 }
870 }
871 ImapCoroutineState::Complete(Ok(())) => return,
872 ImapCoroutineState::Yielded(ImapMailboxWatchYield::WantsRead) => {
873 match stream.read(&mut buf) {
874 Ok(0) => {
875 let eof = io::ErrorKind::UnexpectedEof;
876 let err = "IMAP server closed the connection during watch";
877 tx.send(Err(io::Error::new(eof, err).into())).ok();
878 return;
879 }
880 Ok(n) => arg = Some(buf[..n].to_vec()),
881 Err(err)
889 if matches!(
890 err.kind(),
891 io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut
892 ) =>
893 {
894 if shutdown.load(Ordering::SeqCst) {
895 return;
896 }
897 arg = None;
898 }
899 Err(err) => {
900 tx.send(Err(err.into())).ok();
901 return;
902 }
903 }
904 }
905 ImapCoroutineState::Yielded(ImapMailboxWatchYield::WantsWrite(bytes)) => {
906 if let Err(err) = stream.write_all(&bytes) {
907 tx.send(Err(err.into())).ok();
908 return;
909 }
910 arg = None;
911 }
912 ImapCoroutineState::Yielded(ImapMailboxWatchYield::WantsWait) => {
917 let mut left = opts.poll.unwrap_or_default();
918
919 while left > Duration::ZERO {
920 if shutdown.load(Ordering::SeqCst) {
921 return;
922 }
923
924 let step = left.min(opts.shutdown_poll);
925 thread::sleep(step);
926 left -= step;
927 }
928
929 arg = None;
930 }
931 ImapCoroutineState::Complete(Err(err)) => {
932 tx.send(Err(err.into())).ok();
933 return;
934 }
935 }
936 }
937 });
938
939 Ok(ImapMailboxWatchStream {
940 rx,
941 handle: Some(handle),
942 shutdown: shutdown_handle,
943 })
944 }
945
946 pub fn fetch_body_stream(
952 &mut self,
953 id: NonZeroU32,
954 uid: bool,
955 mut sink: impl Write,
956 ) -> Result<(), ImapClientError> {
957 let mut coroutine = ImapMessageFetchStream::new(id, uid);
958 let mut buf = [0u8; READ_BUFFER_SIZE];
959 let mut body_buf = vec![0u8; BODY_COPY_BUFFER_SIZE];
960 let mut arg: Option<&[u8]> = None;
961
962 loop {
963 match coroutine.resume(&mut self.fragmentizer, arg.take()) {
964 ImapCoroutineState::Complete(Ok(())) => return Ok(()),
965 ImapCoroutineState::Complete(Err(err)) => return Err(err.into()),
966 ImapCoroutineState::Yielded(ImapMessageFetchStreamYield::WantsRead) => {
967 let n = self.read_response(&mut buf)?;
968 arg = Some(&buf[..n]);
969 }
970 ImapCoroutineState::Yielded(ImapMessageFetchStreamYield::WantsWrite(bytes)) => {
971 self.stream.write_all(&bytes)?;
972 arg = None;
973 }
974 ImapCoroutineState::Yielded(ImapMessageFetchStreamYield::BodyChunk(bytes)) => {
975 sink.write_all(&bytes)?;
976 arg = None;
977 }
978 ImapCoroutineState::Yielded(ImapMessageFetchStreamYield::WantsStream { len }) => {
979 let mut remaining = len as u64;
980 let mut short = false;
981
982 while remaining > 0 {
983 let want = remaining.min(body_buf.len() as u64) as usize;
984 let n = self.stream.read(&mut body_buf[..want])?;
985 if n == 0 {
986 short = true;
987 break;
988 }
989 sink.write_all(&body_buf[..n])?;
990 remaining -= n as u64;
991 }
992
993 arg = short.then_some(&[]);
994 }
995 }
996 }
997 }
998
999 pub fn fetch_bodies_stream<S: Write>(
1006 &mut self,
1007 sequence_set: SequenceSet,
1008 uid: bool,
1009 mut open: impl FnMut(u32) -> io::Result<S>,
1010 mut done: impl FnMut(u32, S) -> io::Result<()>,
1011 ) -> Result<(), ImapClientError> {
1012 let mut coroutine = ImapMessageFetchStreamBatch::new(sequence_set, uid);
1013 let mut buf = [0u8; READ_BUFFER_SIZE];
1014 let mut body_buf = vec![0u8; BODY_COPY_BUFFER_SIZE];
1015 let mut current: Option<(u32, S)> = None;
1018 let mut arg: Option<&[u8]> = None;
1019
1020 loop {
1021 match coroutine.resume(&mut self.fragmentizer, arg.take()) {
1022 ImapCoroutineState::Complete(Ok(())) => return Ok(()),
1023 ImapCoroutineState::Complete(Err(err)) => return Err(err.into()),
1024 ImapCoroutineState::Yielded(ImapMessageFetchStreamBatchYield::WantsRead) => {
1025 let n = self.read_response(&mut buf)?;
1026 arg = Some(&buf[..n]);
1027 }
1028 ImapCoroutineState::Yielded(ImapMessageFetchStreamBatchYield::WantsWrite(
1029 bytes,
1030 )) => {
1031 self.stream.write_all(&bytes)?;
1032 arg = None;
1033 }
1034 ImapCoroutineState::Yielded(ImapMessageFetchStreamBatchYield::MessageStart {
1035 uid,
1036 }) => {
1037 current = Some((uid, open(uid)?));
1038 arg = None;
1039 }
1040 ImapCoroutineState::Yielded(ImapMessageFetchStreamBatchYield::BodyChunk(bytes)) => {
1041 let (_, sink) = current.as_mut().expect("body chunk within a message");
1042 sink.write_all(&bytes)?;
1043 arg = None;
1044 }
1045 ImapCoroutineState::Yielded(ImapMessageFetchStreamBatchYield::WantsStream {
1046 len,
1047 }) => {
1048 let (_, sink) = current.as_mut().expect("stream within a message");
1049 let mut remaining = len as u64;
1050 let mut short = false;
1051 while remaining > 0 {
1052 let want = remaining.min(body_buf.len() as u64) as usize;
1053 let n = self.stream.read(&mut body_buf[..want])?;
1054 if n == 0 {
1055 short = true;
1056 break;
1057 }
1058 sink.write_all(&body_buf[..n])?;
1059 remaining -= n as u64;
1060 }
1061 arg = short.then_some(&[]);
1062 }
1063 ImapCoroutineState::Yielded(ImapMessageFetchStreamBatchYield::MessageEnd) => {
1064 let (uid, sink) = current.take().expect("message end within a message");
1065 done(uid, sink)?;
1066 arg = None;
1067 }
1068 }
1069 }
1070 }
1071
1072 pub fn append_stream(
1080 &mut self,
1081 mailbox: Mailbox<'static>,
1082 mut source: impl Read,
1083 len: usize,
1084 opts: ImapMessageAppendOptions,
1085 ) -> Result<ImapMessageAppendOutput, ImapClientError> {
1086 let mut coroutine = ImapMessageAppendStream::new(mailbox, len as u32, opts);
1087 let mut buf = [0u8; READ_BUFFER_SIZE];
1088 let mut arg: Option<&[u8]> = None;
1089
1090 loop {
1091 match coroutine.resume(&mut self.fragmentizer, arg.take()) {
1092 ImapCoroutineState::Complete(Ok(out)) => return Ok(out),
1093 ImapCoroutineState::Complete(Err(err)) => return Err(err.into()),
1094 ImapCoroutineState::Yielded(ImapMessageAppendStreamYield::WantsRead) => {
1095 let n = self.read_response(&mut buf)?;
1096 arg = Some(&buf[..n]);
1097 }
1098 ImapCoroutineState::Yielded(ImapMessageAppendStreamYield::WantsWrite(bytes)) => {
1099 self.stream.write_all(&bytes)?;
1100 arg = None;
1101 }
1102 ImapCoroutineState::Yielded(ImapMessageAppendStreamYield::WantsStream) => {
1103 let len = len as u64;
1104 let mut sink = source.by_ref().take(len);
1105 let n = io::copy(&mut sink, &mut self.stream)?;
1106 arg = (n != len).then_some(&[]);
1107 }
1108 }
1109 }
1110 }
1111}
1112
1113impl fmt::Debug for ImapClientStd {
1114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1115 f.debug_struct("ImapClientStd")
1116 .field("fragmentizer", &self.fragmentizer)
1117 .finish_non_exhaustive()
1118 }
1119}
1120
1121#[derive(Clone, Copy, Debug)]
1123pub struct ImapMailboxWatchStreamOptions {
1124 pub shutdown_poll: Duration,
1134 pub idle_timeout: Option<Duration>,
1138 pub poll: Option<Duration>,
1145}
1146
1147impl Default for ImapMailboxWatchStreamOptions {
1148 fn default() -> Self {
1149 Self {
1150 shutdown_poll: Duration::from_secs(5),
1151 idle_timeout: None,
1152 poll: None,
1153 }
1154 }
1155}
1156
1157pub struct ImapMailboxWatchStream {
1159 rx: Receiver<Result<ImapMailboxWatchEvent, ImapClientError>>,
1160 handle: Option<JoinHandle<()>>,
1161 shutdown: Arc<AtomicBool>,
1162}
1163
1164impl ImapMailboxWatchStream {
1165 pub fn try_recv(&self) -> Result<Result<ImapMailboxWatchEvent, ImapClientError>, TryRecvError> {
1167 self.rx.try_recv()
1168 }
1169
1170 pub fn recv_timeout(
1172 &self,
1173 timeout: Duration,
1174 ) -> Result<Result<ImapMailboxWatchEvent, ImapClientError>, RecvTimeoutError> {
1175 self.rx.recv_timeout(timeout)
1176 }
1177
1178 pub fn close(mut self) -> Result<(), ImapClientError> {
1180 self.shutdown.store(true, Ordering::SeqCst);
1181 if let Some(handle) = self.handle.take() {
1182 handle
1183 .join()
1184 .map_err(|_| io::Error::other("IMAP watch worker panicked"))?;
1185 }
1186 Ok(())
1187 }
1188}
1189
1190impl Iterator for ImapMailboxWatchStream {
1191 type Item = Result<ImapMailboxWatchEvent, ImapClientError>;
1192
1193 fn next(&mut self) -> Option<Self::Item> {
1194 self.rx.recv().ok()
1195 }
1196}
1197
1198impl Drop for ImapMailboxWatchStream {
1199 fn drop(&mut self) {
1200 self.shutdown.store(true, Ordering::SeqCst);
1201
1202 if let Some(handle) = self.handle.take() {
1203 handle.join().ok();
1204 }
1205 }
1206}
1207
1208pub trait ImapStream: Read + Write + Send + Any {
1215 fn as_any_mut(&mut self) -> &mut dyn Any;
1217
1218 fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()>;
1222
1223 fn stop_retrying(&mut self) {}
1232}