@@ -4,6 +4,7 @@ use std::collections::HashSet;
4
4
use std:: io:: { self , Read , Write } ;
5
5
use std:: net:: { TcpStream , ToSocketAddrs } ;
6
6
use std:: ops:: { Deref , DerefMut } ;
7
+ use std:: sync:: mpsc;
7
8
use std:: time:: Duration ;
8
9
9
10
use super :: authenticator:: Authenticator ;
@@ -44,6 +45,10 @@ fn validate_str(value: &str) -> Result<String> {
44
45
#[ derive( Debug ) ]
45
46
pub struct Session < T : Read + Write > {
46
47
conn : Connection < T > ,
48
+ /// Server responses that are not related to the current command. See also the note on
49
+ /// [unilateral server responses in RFC 3501](https://tools.ietf.org/html/rfc3501#section-7).
50
+ pub unsolicited_responses : mpsc:: Receiver < UnsolicitedResponse > ,
51
+ unsolicited_responses_tx : mpsc:: Sender < UnsolicitedResponse > ,
47
52
}
48
53
49
54
/// An (unauthenticated) handle to talk to an IMAP server. This is what you get when first
@@ -396,7 +401,7 @@ impl<T: Read + Write> Client<T> {
396
401
) ;
397
402
} else {
398
403
ok_or_unauth_client_err ! ( self . read_response_onto( & mut line) , self ) ;
399
- return Ok ( Session { conn : self . conn } ) ;
404
+ return Ok ( Session :: new ( self . conn ) ) ;
400
405
}
401
406
}
402
407
}
@@ -443,27 +448,34 @@ impl<T: Read + Write> Client<T> {
443
448
self
444
449
) ;
445
450
446
- Ok ( Session { conn : self . conn } )
451
+ Ok ( Session :: new ( self . conn ) )
447
452
}
448
453
}
449
454
450
455
impl < T : Read + Write > Session < T > {
456
+ // not public, just to avoid duplicating the channel creation code
457
+ fn new ( conn : Connection < T > ) -> Self {
458
+ let ( tx, rx) = mpsc:: channel ( ) ;
459
+ Session { conn, unsolicited_responses : rx, unsolicited_responses_tx : tx }
460
+ }
461
+
451
462
/// Selects a mailbox
452
463
///
453
464
/// Note that the server *is* allowed to unilaterally send things to the client for messages in
454
465
/// a selected mailbox whose status has changed. See the note on [unilateral server responses
455
466
/// in RFC 3501](https://tools.ietf.org/html/rfc3501#section-7). This means that if you use
456
467
/// [`Connection::run_command_and_read_response`], you *may* see additional untagged `RECENT`,
457
- /// `EXISTS`, `FETCH`, and `EXPUNGE` responses!
468
+ /// `EXISTS`, `FETCH`, and `EXPUNGE` responses. You can get them from the
469
+ /// `unsolicited_responses` channel of the [`Session`](struct.Session.html).
458
470
pub fn select ( & mut self , mailbox_name : & str ) -> Result < Mailbox > {
459
471
self . run_command_and_read_response ( & format ! ( "SELECT {}" , validate_str( mailbox_name) ?) )
460
- . and_then ( |lines| parse_mailbox ( & lines[ ..] ) )
472
+ . and_then ( |lines| parse_mailbox ( & lines[ ..] , & mut self . unsolicited_responses_tx ) )
461
473
}
462
474
463
475
/// Examine is identical to Select, but the selected mailbox is identified as read-only
464
476
pub fn examine ( & mut self , mailbox_name : & str ) -> Result < Mailbox > {
465
477
self . run_command_and_read_response ( & format ! ( "EXAMINE {}" , validate_str( mailbox_name) ?) )
466
- . and_then ( |lines| parse_mailbox ( & lines[ ..] ) )
478
+ . and_then ( |lines| parse_mailbox ( & lines[ ..] , & mut self . unsolicited_responses_tx ) )
467
479
}
468
480
469
481
/// Fetch retreives data associated with a set of messages in the mailbox.
@@ -473,7 +485,7 @@ impl<T: Read + Write> Session<T> {
473
485
/// server responses in RFC 3501](https://tools.ietf.org/html/rfc3501#section-7).
474
486
pub fn fetch ( & mut self , sequence_set : & str , query : & str ) -> ZeroCopyResult < Vec < Fetch > > {
475
487
self . run_command_and_read_response ( & format ! ( "FETCH {} {}" , sequence_set, query) )
476
- . and_then ( parse_fetches)
488
+ . and_then ( |lines| parse_fetches ( lines , & mut self . unsolicited_responses_tx ) )
477
489
}
478
490
479
491
/// Fetch retreives data associated with a set of messages by UID in the mailbox.
@@ -483,7 +495,7 @@ impl<T: Read + Write> Session<T> {
483
495
/// server responses in RFC 3501](https://tools.ietf.org/html/rfc3501#section-7).
484
496
pub fn uid_fetch ( & mut self , uid_set : & str , query : & str ) -> ZeroCopyResult < Vec < Fetch > > {
485
497
self . run_command_and_read_response ( & format ! ( "UID FETCH {} {}" , uid_set, query) )
486
- . and_then ( parse_fetches)
498
+ . and_then ( |lines| parse_fetches ( lines , & mut self . unsolicited_responses_tx ) )
487
499
}
488
500
489
501
/// Noop always succeeds, and it does nothing.
@@ -530,7 +542,7 @@ impl<T: Read + Write> Session<T> {
530
542
/// Capability requests a listing of capabilities that the server supports.
531
543
pub fn capabilities ( & mut self ) -> ZeroCopyResult < Capabilities > {
532
544
self . run_command_and_read_response ( "CAPABILITY" )
533
- . and_then ( parse_capabilities)
545
+ . and_then ( |lines| parse_capabilities ( lines , & mut self . unsolicited_responses_tx ) )
534
546
}
535
547
536
548
/// Expunge permanently removes all messages that have the \Deleted flag set from the currently
@@ -560,12 +572,12 @@ impl<T: Read + Write> Session<T> {
560
572
/// Store alters data associated with a message in the mailbox.
561
573
pub fn store ( & mut self , sequence_set : & str , query : & str ) -> ZeroCopyResult < Vec < Fetch > > {
562
574
self . run_command_and_read_response ( & format ! ( "STORE {} {}" , sequence_set, query) )
563
- . and_then ( parse_fetches)
575
+ . and_then ( |lines| parse_fetches ( lines , & mut self . unsolicited_responses_tx ) )
564
576
}
565
577
566
578
pub fn uid_store ( & mut self , uid_set : & str , query : & str ) -> ZeroCopyResult < Vec < Fetch > > {
567
579
self . run_command_and_read_response ( & format ! ( "UID STORE {} {}" , uid_set, query) )
568
- . and_then ( parse_fetches)
580
+ . and_then ( |lines| parse_fetches ( lines , & mut self . unsolicited_responses_tx ) )
569
581
}
570
582
571
583
/// Copy copies the specified message to the end of the specified destination mailbox.
@@ -612,7 +624,7 @@ impl<T: Read + Write> Session<T> {
612
624
quote!( reference_name) ,
613
625
mailbox_search_pattern
614
626
) )
615
- . and_then ( parse_names)
627
+ . and_then ( |lines| parse_names ( lines , & mut self . unsolicited_responses_tx ) )
616
628
}
617
629
618
630
/// The LSUB command returns a subset of names from the set of names
@@ -627,7 +639,7 @@ impl<T: Read + Write> Session<T> {
627
639
quote!( reference_name) ,
628
640
mailbox_search_pattern
629
641
) )
630
- . and_then ( parse_names)
642
+ . and_then ( |lines| parse_names ( lines , & mut self . unsolicited_responses_tx ) )
631
643
}
632
644
633
645
/// The STATUS command requests the status of the indicated mailbox.
@@ -637,7 +649,7 @@ impl<T: Read + Write> Session<T> {
637
649
validate_str( mailbox_name) ?,
638
650
status_data_items
639
651
) )
640
- . and_then ( |lines| parse_mailbox ( & lines[ ..] ) )
652
+ . and_then ( |lines| parse_mailbox ( & lines[ ..] , & mut self . unsolicited_responses_tx ) )
641
653
}
642
654
643
655
/// Returns a handle that can be used to block until the state of the currently selected
@@ -664,14 +676,14 @@ impl<T: Read + Write> Session<T> {
664
676
/// the list of message sequence numbers of those messages.
665
677
pub fn search ( & mut self , query : & str ) -> Result < HashSet < u32 > > {
666
678
self . run_command_and_read_response ( & format ! ( "SEARCH {}" , query) )
667
- . and_then ( parse_ids)
679
+ . and_then ( |lines| parse_ids ( lines , & mut self . unsolicited_responses_tx ) )
668
680
}
669
681
670
682
/// Searches the mailbox for messages that match the given criteria and returns
671
683
/// the list of unique identifier numbers of those messages.
672
684
pub fn uid_search ( & mut self , query : & str ) -> Result < HashSet < u32 > > {
673
685
self . run_command_and_read_response ( & format ! ( "UID SEARCH {}" , query) )
674
- . and_then ( parse_ids)
686
+ . and_then ( |lines| parse_ids ( lines , & mut self . unsolicited_responses_tx ) )
675
687
}
676
688
677
689
// these are only here because they are public interface, the rest is in `Connection`
@@ -836,9 +848,7 @@ mod tests {
836
848
837
849
macro_rules! mock_session {
838
850
( $s: expr) => {
839
- Session {
840
- conn: Client :: new( $s) . conn,
841
- }
851
+ Session :: new( Client :: new( $s) . conn)
842
852
} ;
843
853
}
844
854
0 commit comments