@@ -138,6 +138,11 @@ fn validate_sequence_set(
138
138
#[ derive( Debug ) ]
139
139
pub struct Session < T : Read + Write > {
140
140
conn : Connection < T > ,
141
+
142
+ // Capabilities are almost guaranteed to chance if encryption state or authentication state
143
+ // changes, so caching them in `Connection` is inappropiate.
144
+ capability_cache : Option < Capabilities > ,
145
+
141
146
pub ( crate ) unsolicited_responses_tx : mpsc:: Sender < UnsolicitedResponse > ,
142
147
143
148
/// Server responses that are not related to the current command. See also the note on
@@ -153,6 +158,10 @@ pub struct Session<T: Read + Write> {
153
158
#[ derive( Debug ) ]
154
159
pub struct Client < T : Read + Write > {
155
160
conn : Connection < T > ,
161
+
162
+ // Capabilities are almost guaranteed to chance if encryption state or authentication state
163
+ // changes, so caching them in `Connection` is inappropiate.
164
+ capability_cache : Option < Capabilities > ,
156
165
}
157
166
158
167
/// The underlying primitives type. Both `Client`(unauthenticated) and `Session`(after succesful
@@ -333,6 +342,7 @@ impl<T: Read + Write> Client<T> {
333
342
debug : false ,
334
343
greeting_read : false ,
335
344
} ,
345
+ capability_cache : None ,
336
346
}
337
347
}
338
348
@@ -345,6 +355,47 @@ impl<T: Read + Write> Client<T> {
345
355
Ok ( res)
346
356
}
347
357
358
+ /// The [`CAPABILITY` command](https://tools.ietf.org/html/rfc3501#section-6.1.1) requests a
359
+ /// listing of capabilities that the server supports. The server will include "IMAP4rev1" as
360
+ /// one of the listed capabilities. See [`Capabilities`] for further details.
361
+ ///
362
+ /// This method will always bypass the local capabilities cache and send a `CAPABILITY` command
363
+ /// to the server. The [`Self::capabilities()`] method can be used when returning a cached
364
+ /// response is acceptable.
365
+ pub fn capabilities_refresh ( & mut self ) -> Result < & Capabilities > {
366
+ let ( mut tx, _rx) = mpsc:: channel ( ) ;
367
+ let caps = self . run_command_and_read_response ( "CAPABILITY" )
368
+ . and_then ( |lines| Capabilities :: parse ( lines, & mut tx) ) ?;
369
+ self . capability_cache = Some ( caps) ;
370
+
371
+ self . capability_cache . as_ref ( )
372
+ // This path will not be hit; if the cache is not populated the above calls will either
373
+ // populate it or return with an early error.
374
+ . ok_or_else ( || panic ! ( "CAPABILITY call did not populate capability cache!" ) )
375
+ }
376
+
377
+ /// The [`CAPABILITY` command](https://tools.ietf.org/html/rfc3501#section-6.1.1) requests a
378
+ /// listing of capabilities that the server supports. The server will include "IMAP4rev1" as
379
+ /// one of the listed capabilities. See [`Capabilities`] for further details.
380
+ ///
381
+ /// This function will not query the server if a set of capabilities was cached, but request
382
+ /// and cache capabilities from the server otherwise. The [`Self::capabilities_refresh`] method
383
+ /// can be used to refresh the cache by forcing a `CAPABILITY` command to be send.
384
+ pub fn capabilities_ref ( & mut self ) -> Result < & Capabilities > {
385
+ let ( mut tx, _rx) = mpsc:: channel ( ) ;
386
+ if self . capability_cache . is_none ( ) {
387
+ let caps = self . run_command_and_read_response ( "CAPABILITY" )
388
+ . and_then ( |lines| Capabilities :: parse ( lines, & mut tx) ) ?;
389
+
390
+ self . capability_cache = Some ( caps) ;
391
+ }
392
+
393
+ self . capability_cache . as_ref ( )
394
+ // This path will not be hit; if the cache is not populated the above `if` will either
395
+ // populate it or return with an early error.
396
+ . ok_or_else ( || panic ! ( "CAPABILITY call did not populate capability cache!" ) )
397
+ }
398
+
348
399
/// Log in to the IMAP server. Upon success a [`Session`](struct.Session.html) instance is
349
400
/// returned; on error the original `Client` instance is returned in addition to the error.
350
401
/// This is because `login` takes ownership of `self`, so in order to try again (e.g. after
@@ -502,6 +553,7 @@ impl<T: Read + Write> Session<T> {
502
553
let ( tx, rx) = mpsc:: channel ( ) ;
503
554
Session {
504
555
conn,
556
+ capability_cache : None ,
505
557
unsolicited_responses : rx,
506
558
unsolicited_responses_tx : tx,
507
559
}
@@ -771,12 +823,52 @@ impl<T: Read + Write> Session<T> {
771
823
self . run_command_and_check_ok ( & format ! ( "UNSUBSCRIBE {}" , quote!( mailbox. as_ref( ) ) ) )
772
824
}
773
825
826
+ /// The [`CAPABILITY` command](https://tools.ietf.org/html/rfc3501#section-6.1.1) requests a
827
+ /// listing of capabilities that the server supports. The server will include "IMAP4rev1" as
828
+ /// one of the listed capabilities. See [`Capabilities`] for further details.
829
+ ///
830
+ /// This method will always bypass the local capabilities cache and send a `CAPABILITY` command
831
+ /// to the server. The [`Self::capabilities()`] method can be used when returning a cached
832
+ /// response is acceptable.
833
+ pub fn capabilities_refresh ( & mut self ) -> Result < & Capabilities > {
834
+ let caps = self . run_command_and_read_response ( "CAPABILITY" )
835
+ . and_then ( |lines| Capabilities :: parse ( lines, & mut self . unsolicited_responses_tx ) ) ?;
836
+ self . capability_cache = Some ( caps) ;
837
+
838
+ self . capability_cache . as_ref ( )
839
+ // This path will not be hit; if the cache is not populated the above calls will either
840
+ // populate it or return with an early error.
841
+ . ok_or_else ( || panic ! ( "CAPABILITY call did not populate capability cache!" ) )
842
+ }
843
+
844
+ /// The [`CAPABILITY` command](https://tools.ietf.org/html/rfc3501#section-6.1.1) requests a
845
+ /// listing of capabilities that the server supports. The server will include "IMAP4rev1" as
846
+ /// one of the listed capabilities. See [`Capabilities`] for further details.
847
+ ///
848
+ /// This function will not query the server if a set of capabilities was cached, but request
849
+ /// and cache capabilities from the server otherwise. The [`Self::capabilities_refresh`] method
850
+ /// can be used to refresh the cache by forcing a `CAPABILITY` command to be send.
851
+ pub fn capabilities_ref ( & mut self ) -> Result < & Capabilities > {
852
+ if self . capability_cache . is_none ( ) {
853
+ let caps = self . run_command_and_read_response ( "CAPABILITY" )
854
+ . and_then ( |lines| Capabilities :: parse ( lines, & mut self . unsolicited_responses_tx ) ) ?;
855
+
856
+ self . capability_cache = Some ( caps) ;
857
+ }
858
+
859
+ self . capability_cache . as_ref ( )
860
+ // This path will not be hit; if the cache is not populated the above `if` will either
861
+ // populate it or return with an early error.
862
+ . ok_or_else ( || panic ! ( "CAPABILITY call did not populate capability cache!" ) )
863
+ }
864
+
774
865
/// The [`CAPABILITY` command](https://tools.ietf.org/html/rfc3501#section-6.1.1) requests a
775
866
/// listing of capabilities that the server supports. The server will include "IMAP4rev1" as
776
867
/// one of the listed capabilities. See [`Capabilities`] for further details.
777
868
pub fn capabilities ( & mut self ) -> Result < Capabilities > {
778
- self . run_command_and_read_response ( "CAPABILITY" )
779
- . and_then ( |lines| Capabilities :: parse ( lines, & mut self . unsolicited_responses_tx ) )
869
+ // TODO: This emulated the same behaviour as before, with each call issuing a command.
870
+ // It may be sensible to allow hitting the cache here.
871
+ self . capabilities_refresh ( ) . map ( |caps| caps. clone ( ) )
780
872
}
781
873
782
874
/// The [`EXPUNGE` command](https://tools.ietf.org/html/rfc3501#section-6.4.3) permanently
0 commit comments