1use std::num::{ParseIntError, TryFromIntError};
13
14#[cfg(feature = "bounded-static")]
15use bounded_static::{IntoBoundedStatic, ToStatic};
16use imap_types::{
17 auth::AuthenticateData,
18 command::Command,
19 core::{LiteralMode, Tag},
20 extensions::idle::IdleDone,
21 response::{Greeting, Response},
22};
23use nom::error::{ErrorKind, FromExternalError, ParseError};
24
25use crate::{
26 auth::authenticate_data,
27 command::command,
28 extensions::idle::idle_done,
29 response::{greeting, response},
30 AuthenticateDataCodec, CommandCodec, GreetingCodec, IdleDoneCodec, ResponseCodec,
31};
32
33pub(crate) type IMAPResult<'a, I, O> = Result<(I, O), nom::Err<IMAPParseError<'a, I>>>;
35
36#[derive(Debug)]
38pub(crate) struct IMAPParseError<'a, I> {
39 #[allow(unused)]
40 pub input: I,
41 pub kind: IMAPErrorKind<'a>,
42}
43
44#[derive(Debug)]
46pub(crate) enum IMAPErrorKind<'a> {
47 Literal {
48 tag: Option<Tag<'a>>,
49 length: u32,
50 mode: LiteralMode,
51 },
52 BadNumber,
53 BadBase64,
54 BadDateTime,
55 LiteralContainsNull,
56 RecursionLimitExceeded,
57 Nom(ErrorKind),
58}
59
60impl<'a, I> ParseError<I> for IMAPParseError<'a, I> {
61 fn from_error_kind(input: I, kind: ErrorKind) -> Self {
62 Self {
63 input,
64 kind: IMAPErrorKind::Nom(kind),
65 }
66 }
67
68 fn append(input: I, kind: ErrorKind, _: Self) -> Self {
69 Self {
70 input,
71 kind: IMAPErrorKind::Nom(kind),
72 }
73 }
74}
75
76impl<'a, I> FromExternalError<I, ParseIntError> for IMAPParseError<'a, I> {
77 fn from_external_error(input: I, _: ErrorKind, _: ParseIntError) -> Self {
78 Self {
79 input,
80 kind: IMAPErrorKind::BadNumber,
81 }
82 }
83}
84
85impl<'a, I> FromExternalError<I, TryFromIntError> for IMAPParseError<'a, I> {
86 fn from_external_error(input: I, _: ErrorKind, _: TryFromIntError) -> Self {
87 Self {
88 input,
89 kind: IMAPErrorKind::BadNumber,
90 }
91 }
92}
93
94impl<'a, I> FromExternalError<I, base64::DecodeError> for IMAPParseError<'a, I> {
95 fn from_external_error(input: I, _: ErrorKind, _: base64::DecodeError) -> Self {
96 Self {
97 input,
98 kind: IMAPErrorKind::BadBase64,
99 }
100 }
101}
102
103pub trait Decoder {
107 type Message<'a>: Sized;
108 type Error<'a>;
109
110 fn decode<'a>(&self, input: &'a [u8])
111 -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'a>>;
112
113 #[cfg(feature = "bounded-static")]
114 #[cfg_attr(docsrs, doc(cfg(feature = "bounded-static")))]
115 fn decode_static<'a>(
116 &self,
117 input: &'a [u8],
118 ) -> Result<(&'a [u8], Self::Message<'static>), Self::Error<'static>>
119 where
120 Self::Message<'a>: IntoBoundedStatic<Static = Self::Message<'static>>,
121 Self::Error<'a>: IntoBoundedStatic<Static = Self::Error<'static>>,
122 {
123 let (remaining, value) = self.decode(input).map_err(IntoBoundedStatic::into_static)?;
124 Ok((remaining, value.into_static()))
125 }
126}
127
128#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
130#[derive(Clone, Debug, Eq, PartialEq)]
131pub enum GreetingDecodeError {
132 Incomplete,
134
135 Failed,
137}
138
139#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
141#[derive(Clone, Debug, Eq, PartialEq)]
142pub enum CommandDecodeError<'a> {
143 Incomplete,
145
146 LiteralFound {
183 tag: Tag<'a>,
187
188 length: u32,
190
191 mode: LiteralMode,
193 },
194
195 Failed,
197}
198
199#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
201#[derive(Clone, Debug, Eq, PartialEq)]
202pub enum AuthenticateDataDecodeError {
203 Incomplete,
205
206 Failed,
208}
209
210#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
212#[derive(Clone, Debug, Eq, PartialEq)]
213pub enum ResponseDecodeError {
214 Incomplete,
216
217 LiteralFound {
226 length: u32,
228 },
229
230 Failed,
232}
233
234#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
236#[derive(Clone, Debug, Eq, PartialEq)]
237pub enum IdleDoneDecodeError {
238 Incomplete,
240
241 Failed,
243}
244
245impl Decoder for GreetingCodec {
248 type Message<'a> = Greeting<'a>;
249 type Error<'a> = GreetingDecodeError;
250
251 fn decode<'a>(
252 &self,
253 input: &'a [u8],
254 ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'static>> {
255 match greeting(input) {
256 Ok((rem, grt)) => Ok((rem, grt)),
257 Err(nom::Err::Incomplete(_)) => Err(GreetingDecodeError::Incomplete),
258 Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => Err(GreetingDecodeError::Failed),
259 }
260 }
261}
262
263impl Decoder for CommandCodec {
264 type Message<'a> = Command<'a>;
265 type Error<'a> = CommandDecodeError<'a>;
266
267 fn decode<'a>(
268 &self,
269 input: &'a [u8],
270 ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'a>> {
271 match command(input) {
272 Ok((rem, cmd)) => Ok((rem, cmd)),
273 Err(nom::Err::Incomplete(_)) => Err(CommandDecodeError::Incomplete),
274 Err(nom::Err::Failure(error)) => match error {
275 IMAPParseError {
276 input: _,
277 kind: IMAPErrorKind::Literal { tag, length, mode },
278 } => Err(CommandDecodeError::LiteralFound {
279 tag: tag.expect("Expected `Some(tag)` in `IMAPErrorKind::Literal`, got `None`"),
281 length,
282 mode,
283 }),
284 _ => Err(CommandDecodeError::Failed),
285 },
286 Err(nom::Err::Error(_)) => Err(CommandDecodeError::Failed),
287 }
288 }
289}
290
291impl Decoder for ResponseCodec {
292 type Message<'a> = Response<'a>;
293 type Error<'a> = ResponseDecodeError;
294
295 fn decode<'a>(
296 &self,
297 input: &'a [u8],
298 ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'static>> {
299 match response(input) {
300 Ok((rem, rsp)) => Ok((rem, rsp)),
301 Err(nom::Err::Incomplete(_)) => Err(ResponseDecodeError::Incomplete),
302 Err(nom::Err::Error(error) | nom::Err::Failure(error)) => match error {
303 IMAPParseError {
304 kind: IMAPErrorKind::Literal { length, .. },
305 ..
306 } => Err(ResponseDecodeError::LiteralFound { length }),
307 _ => Err(ResponseDecodeError::Failed),
308 },
309 }
310 }
311}
312
313impl Decoder for AuthenticateDataCodec {
314 type Message<'a> = AuthenticateData;
315 type Error<'a> = AuthenticateDataDecodeError;
316
317 fn decode<'a>(
318 &self,
319 input: &'a [u8],
320 ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'static>> {
321 match authenticate_data(input) {
322 Ok((rem, rsp)) => Ok((rem, rsp)),
323 Err(nom::Err::Incomplete(_)) => Err(AuthenticateDataDecodeError::Incomplete),
324 Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => {
325 Err(AuthenticateDataDecodeError::Failed)
326 }
327 }
328 }
329}
330
331impl Decoder for IdleDoneCodec {
332 type Message<'a> = IdleDone;
333 type Error<'a> = IdleDoneDecodeError;
334
335 fn decode<'a>(
336 &self,
337 input: &'a [u8],
338 ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'static>> {
339 match idle_done(input) {
340 Ok((rem, rsp)) => Ok((rem, rsp)),
341 Err(nom::Err::Incomplete(_)) => Err(IdleDoneDecodeError::Incomplete),
342 Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => Err(IdleDoneDecodeError::Failed),
343 }
344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use std::num::NonZeroU32;
350
351 use imap_types::{
352 command::{Command, CommandBody},
353 core::{IString, Literal, NString, NonEmptyVec},
354 extensions::idle::IdleDone,
355 fetch::MessageDataItem,
356 mailbox::Mailbox,
357 response::{Data, Greeting, GreetingKind, Response},
358 secret::Secret,
359 };
360
361 use super::*;
362
363 #[test]
364 fn test_decode_greeting() {
365 let tests = [
366 (
368 b"* OK ...\r\n".as_ref(),
369 Ok((
370 b"".as_ref(),
371 Greeting::new(GreetingKind::Ok, None, "...").unwrap(),
372 )),
373 ),
374 (
375 b"* ByE .\r\n???".as_ref(),
376 Ok((
377 b"???".as_ref(),
378 Greeting::new(GreetingKind::Bye, None, ".").unwrap(),
379 )),
380 ),
381 (
382 b"* preaUth x\r\n?".as_ref(),
383 Ok((
384 b"?".as_ref(),
385 Greeting::new(GreetingKind::PreAuth, None, "x").unwrap(),
386 )),
387 ),
388 (b"*".as_ref(), Err(GreetingDecodeError::Incomplete)),
390 (b"* ".as_ref(), Err(GreetingDecodeError::Incomplete)),
391 (b"* O".as_ref(), Err(GreetingDecodeError::Incomplete)),
392 (b"* OK".as_ref(), Err(GreetingDecodeError::Incomplete)),
393 (b"* OK ".as_ref(), Err(GreetingDecodeError::Incomplete)),
394 (b"* OK .".as_ref(), Err(GreetingDecodeError::Incomplete)),
395 (b"* OK .\r".as_ref(), Err(GreetingDecodeError::Incomplete)),
396 (b"**".as_ref(), Err(GreetingDecodeError::Failed)),
398 (b"* NO x\r\n".as_ref(), Err(GreetingDecodeError::Failed)),
399 ];
400
401 for (test, expected) in tests {
402 let got = GreetingCodec::default().decode(test);
403 dbg!((std::str::from_utf8(test).unwrap(), &expected, &got));
404 assert_eq!(expected, got);
405
406 #[cfg(feature = "bounded-static")]
407 {
408 let got = GreetingCodec::default().decode_static(test);
409 assert_eq!(expected, got);
410 }
411 }
412 }
413
414 #[test]
415 fn test_decode_command() {
416 let tests = [
417 (
419 b"a noop\r\n".as_ref(),
420 Ok((b"".as_ref(), Command::new("a", CommandBody::Noop).unwrap())),
421 ),
422 (
423 b"a noop\r\n???".as_ref(),
424 Ok((
425 b"???".as_ref(),
426 Command::new("a", CommandBody::Noop).unwrap(),
427 )),
428 ),
429 (
430 b"a select {5}\r\ninbox\r\n".as_ref(),
431 Ok((
432 b"".as_ref(),
433 Command::new(
434 "a",
435 CommandBody::Select {
436 mailbox: Mailbox::Inbox,
437 },
438 )
439 .unwrap(),
440 )),
441 ),
442 (
443 b"a select {5}\r\ninbox\r\nxxx".as_ref(),
444 Ok((
445 b"xxx".as_ref(),
446 Command::new(
447 "a",
448 CommandBody::Select {
449 mailbox: Mailbox::Inbox,
450 },
451 )
452 .unwrap(),
453 )),
454 ),
455 (b"a".as_ref(), Err(CommandDecodeError::Incomplete)),
457 (b"a ".as_ref(), Err(CommandDecodeError::Incomplete)),
458 (b"a n".as_ref(), Err(CommandDecodeError::Incomplete)),
459 (b"a no".as_ref(), Err(CommandDecodeError::Incomplete)),
460 (b"a noo".as_ref(), Err(CommandDecodeError::Incomplete)),
461 (b"a noop".as_ref(), Err(CommandDecodeError::Incomplete)),
462 (b"a noop\r".as_ref(), Err(CommandDecodeError::Incomplete)),
463 (
465 b"a select {5}\r\n".as_ref(),
466 Err(CommandDecodeError::LiteralFound {
467 tag: Tag::try_from("a").unwrap(),
468 length: 5,
469 mode: LiteralMode::Sync,
470 }),
471 ),
472 (
474 b"a select {5}\r\nxxx".as_ref(),
475 Err(CommandDecodeError::Incomplete),
476 ),
477 (b"* noop\r\n".as_ref(), Err(CommandDecodeError::Failed)),
479 (b"A noop\r\n".as_ref(), Err(CommandDecodeError::Failed)),
480 ];
481
482 for (test, expected) in tests {
483 let got = CommandCodec::default().decode(test);
484 dbg!((std::str::from_utf8(test).unwrap(), &expected, &got));
485 assert_eq!(expected, got);
486
487 #[cfg(feature = "bounded-static")]
488 {
489 let got = CommandCodec::default().decode_static(test);
490 assert_eq!(expected, got);
491 }
492 }
493 }
494
495 #[test]
496 fn test_decode_authenticate_data() {
497 let tests = [
498 (
500 b"VGVzdA==\r\n".as_ref(),
501 Ok((
502 b"".as_ref(),
503 AuthenticateData(Secret::new(b"Test".to_vec())),
504 )),
505 ),
506 (
507 b"VGVzdA==\r\nx".as_ref(),
508 Ok((
509 b"x".as_ref(),
510 AuthenticateData(Secret::new(b"Test".to_vec())),
511 )),
512 ),
513 (b"V".as_ref(), Err(AuthenticateDataDecodeError::Incomplete)),
515 (b"VG".as_ref(), Err(AuthenticateDataDecodeError::Incomplete)),
516 (
517 b"VGV".as_ref(),
518 Err(AuthenticateDataDecodeError::Incomplete),
519 ),
520 (
521 b"VGVz".as_ref(),
522 Err(AuthenticateDataDecodeError::Incomplete),
523 ),
524 (
525 b"VGVzd".as_ref(),
526 Err(AuthenticateDataDecodeError::Incomplete),
527 ),
528 (
529 b"VGVzdA".as_ref(),
530 Err(AuthenticateDataDecodeError::Incomplete),
531 ),
532 (
533 b"VGVzdA=".as_ref(),
534 Err(AuthenticateDataDecodeError::Incomplete),
535 ),
536 (
537 b"VGVzdA==".as_ref(),
538 Err(AuthenticateDataDecodeError::Incomplete),
539 ),
540 (
541 b"VGVzdA==\r".as_ref(),
542 Err(AuthenticateDataDecodeError::Incomplete),
543 ),
544 (
545 b"VGVzdA==\r\n".as_ref(),
546 Ok((
547 b"".as_ref(),
548 AuthenticateData(Secret::new(b"Test".to_vec())),
549 )),
550 ),
551 (
553 b"VGVzdA== \r\n".as_ref(),
554 Err(AuthenticateDataDecodeError::Failed),
555 ),
556 (
557 b" VGVzdA== \r\n".as_ref(),
558 Err(AuthenticateDataDecodeError::Failed),
559 ),
560 (
561 b" V GVzdA== \r\n".as_ref(),
562 Err(AuthenticateDataDecodeError::Failed),
563 ),
564 (
565 b" V GVzdA= \r\n".as_ref(),
566 Err(AuthenticateDataDecodeError::Failed),
567 ),
568 ];
569
570 for (test, expected) in tests {
571 let got = AuthenticateDataCodec::default().decode(test);
572 dbg!((std::str::from_utf8(test).unwrap(), &expected, &got));
573 assert_eq!(expected, got);
574
575 #[cfg(feature = "bounded-static")]
576 {
577 let got = AuthenticateDataCodec::default().decode_static(test);
578 assert_eq!(expected, got);
579 }
580 }
581 }
582
583 #[test]
584 fn test_decode_idle_done() {
585 let tests = [
586 (b"done\r\n".as_ref(), Ok((b"".as_ref(), IdleDone))),
588 (b"done\r\n?".as_ref(), Ok((b"?".as_ref(), IdleDone))),
589 (b"d".as_ref(), Err(IdleDoneDecodeError::Incomplete)),
591 (b"do".as_ref(), Err(IdleDoneDecodeError::Incomplete)),
592 (b"don".as_ref(), Err(IdleDoneDecodeError::Incomplete)),
593 (b"done".as_ref(), Err(IdleDoneDecodeError::Incomplete)),
594 (b"done\r".as_ref(), Err(IdleDoneDecodeError::Incomplete)),
595 (b"donee\r\n".as_ref(), Err(IdleDoneDecodeError::Failed)),
597 (b" done\r\n".as_ref(), Err(IdleDoneDecodeError::Failed)),
598 (b"done \r\n".as_ref(), Err(IdleDoneDecodeError::Failed)),
599 (b" done \r\n".as_ref(), Err(IdleDoneDecodeError::Failed)),
600 ];
601
602 for (test, expected) in tests {
603 let got = IdleDoneCodec::default().decode(test);
604 dbg!((std::str::from_utf8(test).unwrap(), &expected, &got));
605 assert_eq!(expected, got);
606
607 #[cfg(feature = "bounded-static")]
608 {
609 let got = IdleDoneCodec::default().decode_static(test);
610 assert_eq!(expected, got);
611 }
612 }
613 }
614
615 #[test]
616 fn test_decode_response() {
617 let tests = [
618 (b"".as_ref(), Err(ResponseDecodeError::Incomplete)),
620 (b"*".as_ref(), Err(ResponseDecodeError::Incomplete)),
621 (b"* ".as_ref(), Err(ResponseDecodeError::Incomplete)),
622 (b"* S".as_ref(), Err(ResponseDecodeError::Incomplete)),
623 (b"* SE".as_ref(), Err(ResponseDecodeError::Incomplete)),
624 (b"* SEA".as_ref(), Err(ResponseDecodeError::Incomplete)),
625 (b"* SEAR".as_ref(), Err(ResponseDecodeError::Incomplete)),
626 (b"* SEARC".as_ref(), Err(ResponseDecodeError::Incomplete)),
627 (b"* SEARCH".as_ref(), Err(ResponseDecodeError::Incomplete)),
628 (b"* SEARCH ".as_ref(), Err(ResponseDecodeError::Incomplete)),
629 (b"* SEARCH 1".as_ref(), Err(ResponseDecodeError::Incomplete)),
630 (
631 b"* SEARCH 1\r".as_ref(),
632 Err(ResponseDecodeError::Incomplete),
633 ),
634 (
636 b"* SEARCH 1\r\n".as_ref(),
637 Ok((
638 b"".as_ref(),
639 Response::Data(Data::Search(vec![NonZeroU32::new(1).unwrap()])),
640 )),
641 ),
642 (
643 b"* SEARCH 1\r\n???".as_ref(),
644 Ok((
645 b"???".as_ref(),
646 Response::Data(Data::Search(vec![NonZeroU32::new(1).unwrap()])),
647 )),
648 ),
649 (
650 b"* 1 FETCH (RFC822 {5}\r\nhello)\r\n".as_ref(),
651 Ok((
652 b"".as_ref(),
653 Response::Data(Data::Fetch {
654 seq: NonZeroU32::new(1).unwrap(),
655 items: NonEmptyVec::from(MessageDataItem::Rfc822(NString(Some(
656 IString::Literal(Literal::try_from(b"hello".as_ref()).unwrap()),
657 )))),
658 }),
659 )),
660 ),
661 (
662 b"* 1 FETCH (RFC822 {5}\r\n".as_ref(),
663 Err(ResponseDecodeError::LiteralFound { length: 5 }),
664 ),
665 (
667 b"* search 1 2 3\r\n".as_ref(),
668 Err(ResponseDecodeError::Failed),
669 ),
670 (b"A search\r\n".as_ref(), Err(ResponseDecodeError::Failed)),
671 ];
672
673 for (test, expected) in tests {
674 let got = ResponseCodec::default().decode(test);
675 dbg!((std::str::from_utf8(test).unwrap(), &expected, &got));
676 assert_eq!(expected, got);
677
678 #[cfg(feature = "bounded-static")]
679 {
680 let got = ResponseCodec::default().decode_static(test);
681 assert_eq!(expected, got);
682 }
683 }
684 }
685}