imap_types/
command.rs

1//! Client Commands.
2//!
3//! See <https://tools.ietf.org/html/rfc3501#section-6>.
4
5use std::borrow::Cow;
6
7#[cfg(feature = "arbitrary")]
8use arbitrary::Arbitrary;
9#[cfg(feature = "bounded-static")]
10use bounded_static::ToStatic;
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14use crate::{
15    auth::AuthMechanism,
16    command::error::{AppendError, CopyError, ListError, LoginError, RenameError},
17    core::{AString, Charset, Literal, NonEmptyVec, Tag},
18    datetime::DateTime,
19    extensions::{compress::CompressionAlgorithm, enable::CapabilityEnable, quota::QuotaSet},
20    fetch::MacroOrMessageDataItemNames,
21    flag::{Flag, StoreResponse, StoreType},
22    mailbox::{ListMailbox, Mailbox},
23    search::SearchKey,
24    secret::Secret,
25    sequence::SequenceSet,
26    status::StatusDataItemName,
27};
28
29/// Command.
30#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
31#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33#[derive(Debug, Clone, PartialEq, Eq, Hash)]
34pub struct Command<'a> {
35    /// Tag.
36    pub tag: Tag<'a>,
37    /// Body, e.g., CAPABILITY, LOGIN, SELECT, etc.
38    pub body: CommandBody<'a>,
39}
40
41impl<'a> Command<'a> {
42    /// Create a new command.
43    pub fn new<T>(tag: T, body: CommandBody<'a>) -> Result<Self, T::Error>
44    where
45        T: TryInto<Tag<'a>>,
46    {
47        Ok(Self {
48            tag: tag.try_into()?,
49            body,
50        })
51    }
52
53    /// Get the command name.
54    pub fn name(&self) -> &'static str {
55        self.body.name()
56    }
57}
58
59/// Command body.
60///
61/// This enum is used to encode all the different commands.
62#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
63#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
64#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
65#[derive(Debug, Clone, PartialEq, Eq, Hash)]
66pub enum CommandBody<'a> {
67    // ----- Any State (see https://tools.ietf.org/html/rfc3501#section-6.1) -----
68    /// ### 6.1.1.  CAPABILITY Command
69    ///
70    /// * Arguments:  none
71    /// * Responses:  REQUIRED untagged response: CAPABILITY
72    /// * Result:
73    ///   * OK - capability completed
74    ///   * BAD - command unknown or arguments invalid
75    ///
76    /// The CAPABILITY command requests a listing of capabilities that the
77    /// server supports.  The server MUST send a single untagged
78    /// CAPABILITY response with "IMAP4rev1" as one of the listed
79    /// capabilities before the (tagged) OK response.
80    ///
81    /// A capability name which begins with "AUTH=" indicates that the
82    /// server supports that particular authentication mechanism.  All
83    /// such names are, by definition, part of this specification.  For
84    /// example, the authorization capability for an experimental
85    /// "blurdybloop" authenticator would be "AUTH=XBLURDYBLOOP" and not
86    /// "XAUTH=BLURDYBLOOP" or "XAUTH=XBLURDYBLOOP".
87    ///
88    /// Other capability names refer to extensions, revisions, or
89    /// amendments to this specification.  See the documentation of the
90    /// CAPABILITY response for additional information.  No capabilities,
91    /// beyond the base IMAP4rev1 set defined in this specification, are
92    /// enabled without explicit client action to invoke the capability.
93    ///
94    /// Client and server implementations MUST implement the STARTTLS,
95    /// LOGINDISABLED, and AUTH=PLAIN (described in [IMAP-TLS])
96    /// capabilities.  See the Security Considerations section for
97    /// important information.
98    ///
99    /// See the section entitled "Client Commands -
100    /// Experimental/Expansion" for information about the form of site or
101    /// implementation-specific capabilities.
102    Capability,
103
104    /// ### 6.1.2.  NOOP Command
105    ///
106    /// * Arguments:  none
107    /// * Responses:  no specific responses for this command (but see below)
108    /// * Result:
109    ///   * OK - noop completed
110    ///   * BAD - command unknown or arguments invalid
111    ///
112    /// The NOOP command always succeeds.  It does nothing.
113    ///
114    /// Since any command can return a status update as untagged data, the
115    /// NOOP command can be used as a periodic poll for new messages or
116    /// message status updates during a period of inactivity (this is the
117    /// preferred method to do this).  The NOOP command can also be used
118    /// to reset any inactivity autologout timer on the server.
119    Noop,
120
121    /// ### 6.1.3.  LOGOUT Command
122    ///
123    /// * Arguments:  none
124    /// * Responses:  REQUIRED untagged response: BYE
125    /// * Result:
126    ///   * OK - logout completed
127    ///   * BAD - command unknown or arguments invalid
128    ///
129    /// The LOGOUT command informs the server that the client is done with
130    /// the connection.  The server MUST send a BYE untagged response
131    /// before the (tagged) OK response, and then close the network
132    /// connection.
133    Logout,
134
135    // ----- Not Authenticated State (https://tools.ietf.org/html/rfc3501#section-6.2) -----
136    /// ### 6.2.1.  STARTTLS Command
137    ///
138    /// * Arguments:  none
139    /// * Responses:  no specific response for this command
140    /// * Result:
141    ///   * OK - starttls completed, begin TLS negotiation
142    ///   * BAD - command unknown or arguments invalid
143    ///
144    /// A \[TLS\] negotiation begins immediately after the CRLF at the end
145    /// of the tagged OK response from the server.  Once a client issues a
146    /// STARTTLS command, it MUST NOT issue further commands until a
147    /// server response is seen and the \[TLS\] negotiation is complete.
148    ///
149    /// The server remains in the non-authenticated state, even if client
150    /// credentials are supplied during the \[TLS\] negotiation.  This does
151    /// not preclude an authentication mechanism such as EXTERNAL (defined
152    /// in \[SASL\]) from using client identity determined by the \[TLS\]
153    /// negotiation.
154    ///
155    /// Once \[TLS\] has been started, the client MUST discard cached
156    /// information about server capabilities and SHOULD re-issue the
157    /// CAPABILITY command.  This is necessary to protect against man-in-
158    /// the-middle attacks which alter the capabilities list prior to
159    /// STARTTLS.  The server MAY advertise different capabilities after
160    /// STARTTLS.
161    #[cfg(feature = "starttls")]
162    #[cfg_attr(docsrs, doc(cfg(feature = "starttls")))]
163    StartTLS,
164
165    /// ### 6.2.2.  AUTHENTICATE Command
166    ///
167    /// * Arguments:  authentication mechanism name
168    /// * Responses:  continuation data can be requested
169    /// * Result:
170    ///   * OK - authenticate completed, now in authenticated state
171    ///   * NO - authenticate failure: unsupported authentication
172    ///          mechanism, credentials rejected
173    ///   * BAD - command unknown or arguments invalid,
174    ///           authentication exchange cancelled
175    ///
176    /// The AUTHENTICATE command indicates a \[SASL\] authentication
177    /// mechanism to the server.  If the server supports the requested
178    /// authentication mechanism, it performs an authentication protocol
179    /// exchange to authenticate and identify the client.  It MAY also
180    /// negotiate an OPTIONAL security layer for subsequent protocol
181    /// interactions.  If the requested authentication mechanism is not
182    /// supported, the server SHOULD reject the AUTHENTICATE command by
183    /// sending a tagged NO response.
184    ///
185    /// The AUTHENTICATE command does not support the optional "initial
186    /// response" feature of \[SASL\].  Section 5.1 of \[SASL\] specifies how
187    /// to handle an authentication mechanism which uses an initial
188    /// response.
189    ///
190    /// The service name specified by this protocol's profile of \[SASL\] is
191    /// "imap".
192    ///
193    /// The authentication protocol exchange consists of a series of
194    /// server challenges and client responses that are specific to the
195    /// authentication mechanism.  A server challenge consists of a
196    /// command continuation request response with the "+" token followed
197    /// by a BASE64 encoded string.  The client response consists of a
198    /// single line consisting of a BASE64 encoded string.  If the client
199    /// wishes to cancel an authentication exchange, it issues a line
200    /// consisting of a single "*".  If the server receives such a
201    /// response, it MUST reject the AUTHENTICATE command by sending a
202    /// tagged BAD response.
203    ///
204    /// If a security layer is negotiated through the \[SASL\]
205    /// authentication exchange, it takes effect immediately following the
206    /// CRLF that concludes the authentication exchange for the client,
207    /// and the CRLF of the tagged OK response for the server.
208    ///
209    /// While client and server implementations MUST implement the
210    /// AUTHENTICATE command itself, it is not required to implement any
211    /// authentication mechanisms other than the PLAIN mechanism described
212    /// in [IMAP-TLS].  Also, an authentication mechanism is not required
213    /// to support any security layers.
214    ///
215    ///   Note: a server implementation MUST implement a
216    ///   configuration in which it does NOT permit any plaintext
217    ///   password mechanisms, unless either the STARTTLS command
218    ///   has been negotiated or some other mechanism that
219    ///   protects the session from password snooping has been
220    ///   provided.  Server sites SHOULD NOT use any configuration
221    ///   which permits a plaintext password mechanism without
222    ///   such a protection mechanism against password snooping.
223    ///   Client and server implementations SHOULD implement
224    ///   additional \[SASL\] mechanisms that do not use plaintext
225    ///   passwords, such the GSSAPI mechanism described in \[SASL\]
226    ///   and/or the [DIGEST-MD5] mechanism.
227    ///
228    /// Servers and clients can support multiple authentication
229    /// mechanisms.  The server SHOULD list its supported authentication
230    /// mechanisms in the response to the CAPABILITY command so that the
231    /// client knows which authentication mechanisms to use.
232    ///
233    /// A server MAY include a CAPABILITY response code in the tagged OK
234    /// response of a successful AUTHENTICATE command in order to send
235    /// capabilities automatically.  It is unnecessary for a client to
236    /// send a separate CAPABILITY command if it recognizes these
237    /// automatic capabilities.  This should only be done if a security
238    /// layer was not negotiated by the AUTHENTICATE command, because the
239    /// tagged OK response as part of an AUTHENTICATE command is not
240    /// protected by encryption/integrity checking.  \[SASL\] requires the
241    /// client to re-issue a CAPABILITY command in this case.
242    ///
243    /// If an AUTHENTICATE command fails with a NO response, the client
244    /// MAY try another authentication mechanism by issuing another
245    /// AUTHENTICATE command.  It MAY also attempt to authenticate by
246    /// using the LOGIN command (see section 6.2.3 for more detail).  In
247    /// other words, the client MAY request authentication types in
248    /// decreasing order of preference, with the LOGIN command as a last
249    /// resort.
250    ///
251    /// The authorization identity passed from the client to the server
252    /// during the authentication exchange is interpreted by the server as
253    /// the user name whose privileges the client is requesting.
254    Authenticate {
255        /// Authentication mechanism.
256        mechanism: AuthMechanism<'a>,
257        /// Initial response (if any).
258        ///
259        /// This type holds the raw binary data, i.e., a `Vec<u8>`, *not* the BASE64 string.
260        ///
261        /// Note: Use this only when the server advertised the `SASL-IR` capability.
262        initial_response: Option<Secret<Cow<'a, [u8]>>>,
263    },
264
265    /// ### 6.2.3.  LOGIN Command
266    ///
267    /// * Arguments:
268    ///   * user name
269    ///   * password
270    /// * Responses:  no specific responses for this command
271    /// * Result:
272    ///   * OK - login completed, now in authenticated state
273    ///   * NO - login failure: user name or password rejected
274    ///   * BAD - command unknown or arguments invalid
275    ///
276    /// The LOGIN command identifies the client to the server and carries
277    /// the plaintext password authenticating this user.
278    ///
279    /// A server MAY include a CAPABILITY response code in the tagged OK
280    /// response to a successful LOGIN command in order to send
281    /// capabilities automatically.  It is unnecessary for a client to
282    /// send a separate CAPABILITY command if it recognizes these
283    /// automatic capabilities.
284    ///
285    ///   Note: Use of the LOGIN command over an insecure network
286    ///   (such as the Internet) is a security risk, because anyone
287    ///   monitoring network traffic can obtain plaintext passwords.
288    ///   The LOGIN command SHOULD NOT be used except as a last
289    ///   resort, and it is recommended that client implementations
290    ///   have a means to disable any automatic use of the LOGIN
291    ///   command.
292    ///
293    ///   Unless either the STARTTLS command has been negotiated or
294    ///   some other mechanism that protects the session from
295    ///   password snooping has been provided, a server
296    ///   implementation MUST implement a configuration in which it
297    ///   advertises the LOGINDISABLED capability and does NOT permit
298    ///   the LOGIN command.  Server sites SHOULD NOT use any
299    ///   configuration which permits the LOGIN command without such
300    ///   a protection mechanism against password snooping.  A client
301    ///   implementation MUST NOT send a LOGIN command if the
302    ///   LOGINDISABLED capability is advertised.
303    Login {
304        /// Username.
305        username: AString<'a>,
306        /// Password.
307        password: Secret<AString<'a>>,
308    },
309
310    // ----- Authenticated State (https://tools.ietf.org/html/rfc3501#section-6.3) -----
311    /// ### 6.3.1.  SELECT Command
312    ///
313    /// * Arguments:  mailbox name
314    /// * Responses:
315    ///   * REQUIRED untagged responses: FLAGS, EXISTS, RECENT
316    ///   * REQUIRED OK untagged responses: UNSEEN, PERMANENTFLAGS, UIDNEXT, UIDVALIDITY
317    /// * Result:
318    ///   * OK - select completed, now in selected state
319    ///   * NO - select failure, now in authenticated state: no such mailbox, can't access mailbox
320    ///   * BAD - command unknown or arguments invalid
321    ///
322    /// The SELECT command selects a mailbox so that messages in the
323    /// mailbox can be accessed.  Before returning an OK to the client,
324    /// the server MUST send the following untagged data to the client.
325    /// Note that earlier versions of this protocol only required the
326    /// FLAGS, EXISTS, and RECENT untagged data; consequently, client
327    /// implementations SHOULD implement default behavior for missing data
328    /// as discussed with the individual item.
329    ///
330    ///   FLAGS       Defined flags in the mailbox.  See the description
331    ///               of the FLAGS response for more detail.
332    ///
333    ///   \<n\> EXISTS  The number of messages in the mailbox.  See the
334    ///               description of the EXISTS response for more detail.
335    ///
336    ///   \<n\> RECENT  The number of messages with the \Recent flag set.
337    ///               See the description of the RECENT response for more
338    ///               detail.
339    ///
340    ///   OK [UNSEEN \<n\>]
341    ///               The message sequence number of the first unseen
342    ///               message in the mailbox.  If this is missing, the
343    ///               client can not make any assumptions about the first
344    ///               unseen message in the mailbox, and needs to issue a
345    ///               SEARCH command if it wants to find it.
346    ///
347    ///   OK [PERMANENTFLAGS (\<list of flags\>)]
348    ///               A list of message flags that the client can change
349    ///               permanently.  If this is missing, the client should
350    ///               assume that all flags can be changed permanently.
351    ///
352    ///   OK [UIDNEXT \<n\>]
353    ///               The next unique identifier value.  Refer to section
354    ///               2.3.1.1 for more information.  If this is missing,
355    ///               the client can not make any assumptions about the
356    ///               next unique identifier value.
357    ///
358    ///   OK [UIDVALIDITY \<n\>]
359    ///               The unique identifier validity value.  Refer to
360    ///               section 2.3.1.1 for more information.  If this is
361    ///               missing, the server does not support unique
362    ///               identifiers.
363    ///
364    /// Only one mailbox can be selected at a time in a connection;
365    /// simultaneous access to multiple mailboxes requires multiple
366    /// connections.  The SELECT command automatically deselects any
367    /// currently selected mailbox before attempting the new selection.
368    /// Consequently, if a mailbox is selected and a SELECT command that
369    /// fails is attempted, no mailbox is selected.
370    ///
371    /// If the client is permitted to modify the mailbox, the server
372    /// SHOULD prefix the text of the tagged OK response with the
373    /// "[READ-WRITE]" response code.
374    ///
375    /// If the client is not permitted to modify the mailbox but is
376    /// permitted read access, the mailbox is selected as read-only, and
377    /// the server MUST prefix the text of the tagged OK response to
378    /// SELECT with the "[READ-ONLY]" response code.  Read-only access
379    /// through SELECT differs from the EXAMINE command in that certain
380    /// read-only mailboxes MAY permit the change of permanent state on a
381    /// per-user (as opposed to global) basis.  Netnews messages marked in
382    /// a server-based .newsrc file are an example of such per-user
383    /// permanent state that can be modified with read-only mailboxes.
384    Select {
385        /// Mailbox.
386        mailbox: Mailbox<'a>,
387    },
388
389    /// Unselect a mailbox.
390    ///
391    /// This should bring the client back to the AUTHENTICATED state.
392    Unselect,
393
394    /// 6.3.2.  EXAMINE Command
395    ///
396    /// Arguments:  mailbox name
397    /// Responses:  REQUIRED untagged responses: FLAGS, EXISTS, RECENT
398    ///             REQUIRED OK untagged responses:  UNSEEN,  PERMANENTFLAGS,
399    ///             UIDNEXT, UIDVALIDITY
400    /// Result:     OK - examine completed, now in selected state
401    ///             NO - examine failure, now in authenticated state: no
402    ///                  such mailbox, can't access mailbox
403    ///             BAD - command unknown or arguments invalid
404    ///
405    /// The EXAMINE command is identical to SELECT and returns the same
406    /// output; however, the selected mailbox is identified as read-only.
407    /// No changes to the permanent state of the mailbox, including
408    /// per-user state, are permitted; in particular, EXAMINE MUST NOT
409    /// cause messages to lose the \Recent flag.
410    ///
411    /// The text of the tagged OK response to the EXAMINE command MUST
412    /// begin with the "[READ-ONLY]" response code.
413    Examine {
414        /// Mailbox.
415        mailbox: Mailbox<'a>,
416    },
417
418    /// ### 6.3.3.  CREATE Command
419    ///
420    /// * Arguments:  mailbox name
421    /// * Responses:  no specific responses for this command
422    /// * Result:
423    ///   * OK - create completed
424    ///   * NO - create failure: can't create mailbox with that name
425    ///   * BAD - command unknown or arguments invalid
426    ///
427    /// The CREATE command creates a mailbox with the given name.  An OK
428    /// response is returned only if a new mailbox with that name has been
429    /// created.  It is an error to attempt to create INBOX or a mailbox
430    /// with a name that refers to an extant mailbox.  Any error in
431    /// creation will return a tagged NO response.
432    ///
433    /// If the mailbox name is suffixed with the server's hierarchy
434    /// separator character (as returned from the server by a LIST
435    /// command), this is a declaration that the client intends to create
436    /// mailbox names under this name in the hierarchy.  Server
437    /// implementations that do not require this declaration MUST ignore
438    /// the declaration.  In any case, the name created is without the
439    /// trailing hierarchy delimiter.
440    ///
441    /// If the server's hierarchy separator character appears elsewhere in
442    /// the name, the server SHOULD create any superior hierarchical names
443    /// that are needed for the CREATE command to be successfully
444    /// completed.  In other words, an attempt to create "foo/bar/zap" on
445    /// a server in which "/" is the hierarchy separator character SHOULD
446    /// create foo/ and foo/bar/ if they do not already exist.
447    ///
448    /// If a new mailbox is created with the same name as a mailbox which
449    /// was deleted, its unique identifiers MUST be greater than any
450    /// unique identifiers used in the previous incarnation of the mailbox
451    /// UNLESS the new incarnation has a different unique identifier
452    /// validity value.  See the description of the UID command for more
453    /// detail.
454    ///
455    ///   Note: The interpretation of this example depends on whether
456    ///   "/" was returned as the hierarchy separator from LIST.  If
457    ///   "/" is the hierarchy separator, a new level of hierarchy
458    ///   named "owatagusiam" with a member called "blurdybloop" is
459    ///   created.  Otherwise, two mailboxes at the same hierarchy
460    ///   level are created.
461    Create {
462        /// Mailbox.
463        mailbox: Mailbox<'a>,
464    },
465
466    /// 6.3.4.  DELETE Command
467    ///
468    /// Arguments:  mailbox name
469    /// Responses:  no specific responses for this command
470    /// Result:     OK - delete completed
471    ///             NO - delete failure: can't delete mailbox with that name
472    ///             BAD - command unknown or arguments invalid
473    ///
474    /// The DELETE command permanently removes the mailbox with the given
475    /// name.  A tagged OK response is returned only if the mailbox has
476    /// been deleted.  It is an error to attempt to delete INBOX or a
477    /// mailbox name that does not exist.
478    ///
479    /// The DELETE command MUST NOT remove inferior hierarchical names.
480    /// For example, if a mailbox "foo" has an inferior "foo.bar"
481    /// (assuming "." is the hierarchy delimiter character), removing
482    /// "foo" MUST NOT remove "foo.bar".  It is an error to attempt to
483    /// delete a name that has inferior hierarchical names and also has
484    /// the \Noselect mailbox name attribute (see the description of the
485    /// LIST response for more details).
486    ///
487    /// It is permitted to delete a name that has inferior hierarchical
488    /// names and does not have the \Noselect mailbox name attribute.  In
489    /// this case, all messages in that mailbox are removed, and the name
490    /// will acquire the \Noselect mailbox name attribute.
491    ///
492    /// The value of the highest-used unique identifier of the deleted
493    /// mailbox MUST be preserved so that a new mailbox created with the
494    /// same name will not reuse the identifiers of the former
495    /// incarnation, UNLESS the new incarnation has a different unique
496    /// identifier validity value.  See the description of the UID command
497    /// for more detail.
498    Delete {
499        /// Mailbox.
500        mailbox: Mailbox<'a>,
501    },
502
503    /// 6.3.5.  RENAME Command
504    ///
505    /// Arguments:  existing mailbox name
506    ///             new mailbox name
507    /// Responses:  no specific responses for this command
508    /// Result:     OK - rename completed
509    ///             NO - rename failure: can't rename mailbox with that name,
510    ///                  can't rename to mailbox with that name
511    ///             BAD - command unknown or arguments invalid
512    ///
513    /// The RENAME command changes the name of a mailbox.  A tagged OK
514    /// response is returned only if the mailbox has been renamed.  It is
515    /// an error to attempt to rename from a mailbox name that does not
516    /// exist or to a mailbox name that already exists.  Any error in
517    /// renaming will return a tagged NO response.
518    ///
519    /// If the name has inferior hierarchical names, then the inferior
520    /// hierarchical names MUST also be renamed.  For example, a rename of
521    /// "foo" to "zap" will rename "foo/bar" (assuming "/" is the
522    /// hierarchy delimiter character) to "zap/bar".
523    ///
524    /// If the server's hierarchy separator character appears in the name,
525    /// the server SHOULD create any superior hierarchical names that are
526    /// needed for the RENAME command to complete successfully.  In other
527    /// words, an attempt to rename "foo/bar/zap" to baz/rag/zowie on a
528    /// server in which "/" is the hierarchy separator character SHOULD
529    /// create baz/ and baz/rag/ if they do not already exist.
530    ///
531    /// The value of the highest-used unique identifier of the old mailbox
532    /// name MUST be preserved so that a new mailbox created with the same
533    /// name will not reuse the identifiers of the former incarnation,
534    /// UNLESS the new incarnation has a different unique identifier
535    /// validity value.  See the description of the UID command for more
536    /// detail.
537    ///
538    /// Renaming INBOX is permitted, and has special behavior.  It moves
539    /// all messages in INBOX to a new mailbox with the given name,
540    /// leaving INBOX empty.  If the server implementation supports
541    /// inferior hierarchical names of INBOX, these are unaffected by a
542    /// rename of INBOX.
543    Rename {
544        /// Current name.
545        from: Mailbox<'a>,
546        /// New name.
547        to: Mailbox<'a>,
548    },
549
550    /// ### 6.3.6.  SUBSCRIBE Command
551    ///
552    /// * Arguments:  mailbox
553    /// * Responses:  no specific responses for this command
554    /// * Result:
555    ///   * OK - subscribe completed
556    ///   * NO - subscribe failure: can't subscribe to that name
557    ///   * BAD - command unknown or arguments invalid
558    ///
559    /// The SUBSCRIBE command adds the specified mailbox name to the
560    /// server's set of "active" or "subscribed" mailboxes as returned by
561    /// the LSUB command.  This command returns a tagged OK response only
562    /// if the subscription is successful.
563    ///
564    /// A server MAY validate the mailbox argument to SUBSCRIBE to verify
565    /// that it exists.  However, it MUST NOT unilaterally remove an
566    /// existing mailbox name from the subscription list even if a mailbox
567    /// by that name no longer exists.
568    ///
569    ///   Note: This requirement is because a server site can
570    ///   choose to routinely remove a mailbox with a well-known
571    ///   name (e.g., "system-alerts") after its contents expire,
572    ///   with the intention of recreating it when new contents
573    ///   are appropriate.
574    Subscribe {
575        /// Mailbox.
576        mailbox: Mailbox<'a>,
577    },
578
579    /// 6.3.7.  UNSUBSCRIBE Command
580    ///
581    /// Arguments:  mailbox name
582    /// Responses:  no specific responses for this command
583    /// Result:     OK - unsubscribe completed
584    ///             NO - unsubscribe failure: can't unsubscribe that name
585    ///             BAD - command unknown or arguments invalid
586    ///
587    /// The UNSUBSCRIBE command removes the specified mailbox name from
588    /// the server's set of "active" or "subscribed" mailboxes as returned
589    /// by the LSUB command.  This command returns a tagged OK response
590    /// only if the unsubscription is successful.
591    Unsubscribe {
592        /// Mailbox.
593        mailbox: Mailbox<'a>,
594    },
595
596    /// ### 6.3.8.  LIST Command
597    ///
598    /// * Arguments:
599    ///   * reference name
600    ///   * mailbox name with possible wildcards
601    /// * Responses:  untagged responses: LIST
602    /// * Result:
603    ///   * OK - list completed
604    ///   * NO - list failure: can't list that reference or name
605    ///   * BAD - command unknown or arguments invalid
606    ///
607    /// The LIST command returns a subset of names from the complete set
608    /// of all names available to the client.  Zero or more untagged LIST
609    /// replies are returned, containing the name attributes, hierarchy
610    /// delimiter, and name; see the description of the LIST reply for
611    /// more detail.
612    ///
613    /// The LIST command SHOULD return its data quickly, without undue
614    /// delay.  For example, it SHOULD NOT go to excess trouble to
615    /// calculate the \Marked or \Unmarked status or perform other
616    /// processing; if each name requires 1 second of processing, then a
617    /// list of 1200 names would take 20 minutes!
618    ///
619    /// An empty ("" string) reference name argument indicates that the
620    /// mailbox name is interpreted as by SELECT.  The returned mailbox
621    /// names MUST match the supplied mailbox name pattern.  A non-empty
622    /// reference name argument is the name of a mailbox or a level of
623    /// mailbox hierarchy, and indicates the context in which the mailbox
624    /// name is interpreted.
625    ///
626    /// An empty ("" string) mailbox name argument is a special request to
627    /// return the hierarchy delimiter and the root name of the name given
628    /// in the reference.  The value returned as the root MAY be the empty
629    /// string if the reference is non-rooted or is an empty string.  In
630    /// all cases, a hierarchy delimiter (or NIL if there is no hierarchy)
631    /// is returned.  This permits a client to get the hierarchy delimiter
632    /// (or find out that the mailbox names are flat) even when no
633    /// mailboxes by that name currently exist.
634    ///
635    /// The reference and mailbox name arguments are interpreted into a
636    /// canonical form that represents an unambiguous left-to-right
637    /// hierarchy.  The returned mailbox names will be in the interpreted
638    /// form.
639    ///
640    ///   Note: The interpretation of the reference argument is
641    ///   implementation-defined.  It depends upon whether the
642    ///   server implementation has a concept of the "current
643    ///   working directory" and leading "break out characters",
644    ///   which override the current working directory.
645    ///
646    ///   For example, on a server which exports a UNIX or NT
647    ///   filesystem, the reference argument contains the current
648    ///   working directory, and the mailbox name argument would
649    ///   contain the name as interpreted in the current working
650    ///   directory.
651    ///
652    /// If a server implementation has no concept of break out
653    /// characters, the canonical form is normally the reference
654    /// name appended with the mailbox name.  Note that if the
655    /// server implements the namespace convention (section
656    /// 5.1.2), "#" is a break out character and must be treated
657    /// as such.
658    ///
659    /// If the reference argument is not a level of mailbox
660    /// hierarchy (that is, it is a \NoInferiors name), and/or
661    /// the reference argument does not end with the hierarchy
662    /// delimiter, it is implementation-dependent how this is
663    /// interpreted.  For example, a reference of "foo/bar" and
664    /// mailbox name of "rag/baz" could be interpreted as
665    /// "foo/bar/rag/baz", "foo/barrag/baz", or "foo/rag/baz".
666    /// A client SHOULD NOT use such a reference argument except
667    /// at the explicit request of the user.  A hierarchical
668    /// browser MUST NOT make any assumptions about server
669    /// interpretation of the reference unless the reference is
670    /// a level of mailbox hierarchy AND ends with the hierarchy
671    /// delimiter.
672    ///
673    /// Any part of the reference argument that is included in the
674    /// interpreted form SHOULD prefix the interpreted form.  It SHOULD
675    /// also be in the same form as the reference name argument.  This
676    /// rule permits the client to determine if the returned mailbox name
677    /// is in the context of the reference argument, or if something about
678    /// the mailbox argument overrode the reference argument.  Without
679    /// this rule, the client would have to have knowledge of the server's
680    /// naming semantics including what characters are "breakouts" that
681    /// override a naming context.
682    ///
683    ///   For example, here are some examples of how references
684    ///   and mailbox names might be interpreted on a UNIX-based
685    ///   server:
686    ///
687    /// ```text
688    /// Reference     Mailbox Name  Interpretation
689    /// ------------  ------------  --------------
690    /// ~smith/Mail/  foo.*         ~smith/Mail/foo.*
691    /// archive/      %             archive/%
692    /// #news.        comp.mail.*   #news.comp.mail.*
693    /// ~smith/Mail/  /usr/doc/foo  /usr/doc/foo
694    /// archive/      ~fred/Mail/*  ~fred/Mail/*
695    /// ```
696    ///
697    ///   The first three examples demonstrate interpretations in
698    ///   the context of the reference argument.  Note that
699    ///   "~smith/Mail" SHOULD NOT be transformed into something
700    ///   like "/u2/users/smith/Mail", or it would be impossible
701    ///   for the client to determine that the interpretation was
702    ///   in the context of the reference.
703    ///
704    /// The character "*" is a wildcard, and matches zero or more
705    /// characters at this position.  The character "%" is similar to "*",
706    /// but it does not match a hierarchy delimiter.  If the "%" wildcard
707    /// is the last character of a mailbox name argument, matching levels
708    /// of hierarchy are also returned.  If these levels of hierarchy are
709    /// not also selectable mailboxes, they are returned with the
710    /// \Noselect mailbox name attribute (see the description of the LIST
711    /// response for more details).
712    ///
713    /// Server implementations are permitted to "hide" otherwise
714    /// accessible mailboxes from the wildcard characters, by preventing
715    /// certain characters or names from matching a wildcard in certain
716    /// situations.  For example, a UNIX-based server might restrict the
717    /// interpretation of "*" so that an initial "/" character does not
718    /// match.
719    ///
720    /// The special name INBOX is included in the output from LIST, if
721    /// INBOX is supported by this server for this user and if the
722    /// uppercase string "INBOX" matches the interpreted reference and
723    /// mailbox name arguments with wildcards as described above.  The
724    /// criteria for omitting INBOX is whether SELECT INBOX will return
725    /// failure; it is not relevant whether the user's real INBOX resides
726    /// on this or some other server.
727    List {
728        /// Reference.
729        reference: Mailbox<'a>,
730        /// Mailbox (wildcard).
731        mailbox_wildcard: ListMailbox<'a>,
732    },
733
734    /// ### 6.3.9.  LSUB Command
735    ///
736    /// * Arguments:
737    ///   * reference name
738    ///   * mailbox name with possible wildcards
739    /// * Responses:  untagged responses: LSUB
740    /// * Result:
741    ///   * OK - lsub completed
742    ///   * NO - lsub failure: can't list that reference or name
743    ///   * BAD - command unknown or arguments invalid
744    ///
745    /// The LSUB command returns a subset of names from the set of names
746    /// that the user has declared as being "active" or "subscribed".
747    /// Zero or more untagged LSUB replies are returned.  The arguments to
748    /// LSUB are in the same form as those for LIST.
749    ///
750    /// The returned untagged LSUB response MAY contain different mailbox
751    /// flags from a LIST untagged response.  If this should happen, the
752    /// flags in the untagged LIST are considered more authoritative.
753    ///
754    /// A special situation occurs when using LSUB with the % wildcard.
755    /// Consider what happens if "foo/bar" (with a hierarchy delimiter of
756    /// "/") is subscribed but "foo" is not.  A "%" wildcard to LSUB must
757    /// return foo, not foo/bar, in the LSUB response, and it MUST be
758    /// flagged with the \Noselect attribute.
759    ///
760    /// The server MUST NOT unilaterally remove an existing mailbox name
761    /// from the subscription list even if a mailbox by that name no
762    /// longer exists.
763    Lsub {
764        /// Reference.
765        reference: Mailbox<'a>,
766        /// Mailbox (wildcard).
767        mailbox_wildcard: ListMailbox<'a>,
768    },
769
770    /// ### 6.3.10. STATUS Command
771    ///
772    /// * Arguments:
773    ///   * mailbox name
774    ///   * status data item names
775    /// * Responses:  untagged responses: STATUS
776    /// * Result:
777    ///   * OK - status completed
778    ///   * NO - status failure: no status for that name
779    ///   * BAD - command unknown or arguments invalid
780    ///
781    /// The STATUS command requests the status of the indicated mailbox.
782    /// It does not change the currently selected mailbox, nor does it
783    /// affect the state of any messages in the queried mailbox (in
784    /// particular, STATUS MUST NOT cause messages to lose the \Recent
785    /// flag).
786    ///
787    /// The STATUS command provides an alternative to opening a second
788    /// IMAP4rev1 connection and doing an EXAMINE command on a mailbox to
789    /// query that mailbox's status without deselecting the current
790    /// mailbox in the first IMAP4rev1 connection.
791    ///
792    /// Unlike the LIST command, the STATUS command is not guaranteed to
793    /// be fast in its response.  Under certain circumstances, it can be
794    /// quite slow.  In some implementations, the server is obliged to
795    /// open the mailbox read-only internally to obtain certain status
796    /// information.  Also unlike the LIST command, the STATUS command
797    /// does not accept wildcards.
798    ///
799    ///   Note: The STATUS command is intended to access the
800    ///   status of mailboxes other than the currently selected
801    ///   mailbox.  Because the STATUS command can cause the
802    ///   mailbox to be opened internally, and because this
803    ///   information is available by other means on the selected
804    ///   mailbox, the STATUS command SHOULD NOT be used on the
805    ///   currently selected mailbox.
806    ///
807    ///   The STATUS command MUST NOT be used as a "check for new
808    ///   messages in the selected mailbox" operation (refer to
809    ///   sections 7, 7.3.1, and 7.3.2 for more information about
810    ///   the proper method for new message checking).
811    ///
812    ///   Because the STATUS command is not guaranteed to be fast
813    ///   in its results, clients SHOULD NOT expect to be able to
814    ///   issue many consecutive STATUS commands and obtain
815    ///   reasonable performance.
816    Status {
817        /// Mailbox.
818        mailbox: Mailbox<'a>,
819        /// Status data items.
820        item_names: Cow<'a, [StatusDataItemName]>,
821    },
822
823    /// 6.3.11. APPEND Command
824    ///
825    /// Arguments:  mailbox name
826    ///             OPTIONAL flag parenthesized list
827    ///             OPTIONAL date/time string
828    ///             message literal
829    /// Responses:  no specific responses for this command
830    /// Result:     OK - append completed
831    ///             NO - append error: can't append to that mailbox, error
832    ///                  in flags or date/time or message text
833    ///             BAD - command unknown or arguments invalid
834    ///
835    /// The APPEND command appends the literal argument as a new message
836    /// to the end of the specified destination mailbox.  This argument
837    /// SHOULD be in the format of an [RFC-2822] message.  8-bit
838    /// characters are permitted in the message.  A server implementation
839    /// that is unable to preserve 8-bit data properly MUST be able to
840    /// reversibly convert 8-bit APPEND data to 7-bit using a [MIME-IMB]
841    /// content transfer encoding.
842    ///
843    ///   Note: There MAY be exceptions, e.g., draft messages, in
844    ///   which required [RFC-2822] header lines are omitted in
845    ///   the message literal argument to APPEND.  The full
846    ///   implications of doing so MUST be understood and
847    ///   carefully weighed.
848    ///
849    /// If a flag parenthesized list is specified, the flags SHOULD be set
850    /// in the resulting message; otherwise, the flag list of the
851    /// resulting message is set to empty by default.  In either case, the
852    /// Recent flag is also set.
853    ///
854    /// If a date-time is specified, the internal date SHOULD be set in
855    /// the resulting message; otherwise, the internal date of the
856    /// resulting message is set to the current date and time by default.
857    ///
858    /// If the append is unsuccessful for any reason, the mailbox MUST be
859    /// restored to its state before the APPEND attempt; no partial
860    /// appending is permitted.
861    ///
862    /// If the destination mailbox does not exist, a server MUST return an
863    /// error, and MUST NOT automatically create the mailbox.  Unless it
864    /// is certain that the destination mailbox can not be created, the
865    /// server MUST send the response code "\[TRYCREATE\]" as the prefix of
866    /// the text of the tagged NO response.  This gives a hint to the
867    /// client that it can attempt a CREATE command and retry the APPEND
868    /// if the CREATE is successful.
869    ///
870    /// If the mailbox is currently selected, the normal new message
871    /// actions SHOULD occur.  Specifically, the server SHOULD notify the
872    /// client immediately via an untagged EXISTS response.  If the server
873    /// does not do so, the client MAY issue a NOOP command (or failing
874    /// that, a CHECK command) after one or more APPEND commands.
875    ///
876    ///   Note: The APPEND command is not used for message delivery,
877    ///   because it does not provide a mechanism to transfer \[SMTP\]
878    ///   envelope information.
879    Append {
880        /// Mailbox.
881        mailbox: Mailbox<'a>,
882        /// Flags.
883        flags: Vec<Flag<'a>>,
884        /// Datetime.
885        date: Option<DateTime>,
886        /// Message to append.
887        message: Literal<'a>,
888    },
889
890    // ----- Selected State (https://tools.ietf.org/html/rfc3501#section-6.4) -----
891    /// ### 6.4.1.  CHECK Command
892    ///
893    /// * Arguments:  none
894    /// * Responses:  no specific responses for this command
895    /// * Result:
896    ///   * OK - check completed
897    ///   * BAD - command unknown or arguments invalid
898    ///
899    /// The CHECK command requests a checkpoint of the currently selected
900    /// mailbox.  A checkpoint refers to any implementation-dependent
901    /// housekeeping associated with the mailbox (e.g., resolving the
902    /// server's in-memory state of the mailbox with the state on its
903    /// disk) that is not normally executed as part of each command.  A
904    /// checkpoint MAY take a non-instantaneous amount of real time to
905    /// complete.  If a server implementation has no such housekeeping
906    /// considerations, CHECK is equivalent to NOOP.
907    ///
908    /// There is no guarantee that an EXISTS untagged response will happen
909    /// as a result of CHECK.  NOOP, not CHECK, SHOULD be used for new
910    /// message polling.
911    Check,
912
913    /// ### 6.4.2.  CLOSE Command
914    ///
915    ///    * Arguments:  none
916    ///    * Responses:  no specific responses for this command
917    ///    * Result:
918    ///      * OK - close completed, now in authenticated state
919    ///      * BAD - command unknown or arguments invalid
920    ///
921    ///       The CLOSE command permanently removes all messages that have the
922    ///       \Deleted flag set from the currently selected mailbox, and returns
923    ///       to the authenticated state from the selected state.  No untagged
924    ///       EXPUNGE responses are sent.
925    ///
926    ///       No messages are removed, and no error is given, if the mailbox is
927    ///       selected by an EXAMINE command or is otherwise selected read-only.
928    ///
929    ///       Even if a mailbox is selected, a SELECT, EXAMINE, or LOGOUT
930    ///       command MAY be issued without previously issuing a CLOSE command.
931    ///       The SELECT, EXAMINE, and LOGOUT commands implicitly close the
932    ///       currently selected mailbox without doing an expunge.  However,
933    ///       when many messages are deleted, a CLOSE-LOGOUT or CLOSE-SELECT
934    ///       sequence is considerably faster than an EXPUNGE-LOGOUT or
935    ///       EXPUNGE-SELECT because no untagged EXPUNGE responses (which the
936    ///       client would probably ignore) are sent.
937    Close,
938
939    /// 6.4.3.  EXPUNGE Command
940    ///
941    /// Arguments:  none
942    /// Responses:  untagged responses: EXPUNGE
943    /// Result:     OK - expunge completed
944    ///             NO - expunge failure: can't expunge (e.g., permission
945    ///                  denied)
946    ///             BAD - command unknown or arguments invalid
947    ///
948    /// The EXPUNGE command permanently removes all messages that have the
949    /// \Deleted flag set from the currently selected mailbox.  Before
950    /// returning an OK to the client, an untagged EXPUNGE response is
951    /// sent for each message that is removed.
952    ///
953    ///   Note: In this example, messages 3, 4, 7, and 11 had the
954    ///   \Deleted flag set.  See the description of the EXPUNGE
955    ///   response for further explanation.
956    Expunge,
957
958    /// ### 6.4.4.  SEARCH Command
959    ///
960    /// * Arguments:
961    ///   * OPTIONAL \[CHARSET\] specification
962    ///   * searching criteria (one or more)
963    /// * Responses:  REQUIRED untagged response: SEARCH
964    /// * Result:
965    ///   * OK - search completed
966    ///   * NO - search error: can't search that \[CHARSET\] or criteria
967    ///   * BAD - command unknown or arguments invalid
968    ///
969    /// The SEARCH command searches the mailbox for messages that match
970    /// the given searching criteria.  Searching criteria consist of one
971    /// or more search keys.  The untagged SEARCH response from the server
972    /// contains a listing of message sequence numbers corresponding to
973    /// those messages that match the searching criteria.
974    ///
975    /// When multiple keys are specified, the result is the intersection
976    /// (AND function) of all the messages that match those keys.  For
977    /// example, the criteria DELETED FROM "SMITH" SINCE 1-Feb-1994 refers
978    /// to all deleted messages from Smith that were placed in the mailbox
979    /// since February 1, 1994.  A search key can also be a parenthesized
980    /// list of one or more search keys (e.g., for use with the OR and NOT
981    /// keys).
982    ///
983    /// Server implementations MAY exclude [MIME-IMB] body parts with
984    /// terminal content media types other than TEXT and MESSAGE from
985    /// consideration in SEARCH matching.
986    ///
987    /// The OPTIONAL \[CHARSET\] specification consists of the word
988    /// "CHARSET" followed by a registered \[CHARSET\].  It indicates the
989    /// \[CHARSET\] of the strings that appear in the search criteria.
990    /// [MIME-IMB] content transfer encodings, and [MIME-HDRS] strings in
991    /// [RFC-2822]/[MIME-IMB] headers, MUST be decoded before comparing
992    /// text in a \[CHARSET\] other than US-ASCII.  US-ASCII MUST be
993    /// supported; other \[CHARSET\]s MAY be supported.
994    ///
995    /// If the server does not support the specified \[CHARSET\], it MUST
996    /// return a tagged NO response (not a BAD).  This response SHOULD
997    /// contain the BADCHARSET response code, which MAY list the
998    /// \[CHARSET\]s supported by the server.
999    ///
1000    /// In all search keys that use strings, a message matches the key if
1001    /// the string is a substring of the field.  The matching is
1002    /// case-insensitive.
1003    ///
1004    /// See [SearchKey] enum.
1005    ///
1006    /// Note: Since this document is restricted to 7-bit ASCII
1007    /// text, it is not possible to show actual UTF-8 data.  The
1008    /// "XXXXXX" is a placeholder for what would be 6 octets of
1009    /// 8-bit data in an actual transaction.
1010    Search {
1011        /// Charset.
1012        charset: Option<Charset<'a>>,
1013        /// Criteria.
1014        criteria: SearchKey<'a>,
1015        /// Use UID variant.
1016        uid: bool,
1017    },
1018
1019    /// ### 6.4.5.  FETCH Command
1020    ///
1021    /// * Arguments:
1022    ///   * sequence set
1023    ///   * message data item names or macro
1024    /// * Responses:  untagged responses: FETCH
1025    /// * Result:
1026    ///   * OK - fetch completed
1027    ///   * NO - fetch error: can't fetch that data
1028    ///   * BAD - command unknown or arguments invalid
1029    ///
1030    /// The FETCH command retrieves data associated with a message in the
1031    /// mailbox.  The data items to be fetched can be either a single atom
1032    /// or a parenthesized list.
1033    ///
1034    /// Most data items, identified in the formal syntax under the
1035    /// msg-att-static rule, are static and MUST NOT change for any
1036    /// particular message.  Other data items, identified in the formal
1037    /// syntax under the msg-att-dynamic rule, MAY change, either as a
1038    /// result of a STORE command or due to external events.
1039    ///
1040    ///   For example, if a client receives an ENVELOPE for a
1041    ///   message when it already knows the envelope, it can
1042    ///   safely ignore the newly transmitted envelope.
1043    Fetch {
1044        /// Set of messages.
1045        sequence_set: SequenceSet,
1046        /// Message data items (or a macro).
1047        macro_or_item_names: MacroOrMessageDataItemNames<'a>,
1048        /// Use UID variant.
1049        uid: bool,
1050    },
1051
1052    /// ### 6.4.6.  STORE Command
1053    ///
1054    /// * Arguments:
1055    ///   * sequence set
1056    ///   * message data item name
1057    ///   * value for message data item
1058    /// * Responses:  untagged responses: FETCH
1059    /// * Result:
1060    ///   * OK - store completed
1061    ///   * NO - store error: can't store that data
1062    ///   * BAD - command unknown or arguments invalid
1063    ///
1064    /// The STORE command alters data associated with a message in the
1065    /// mailbox.  Normally, STORE will return the updated value of the
1066    /// data with an untagged FETCH response.  A suffix of ".SILENT" in
1067    /// the data item name prevents the untagged FETCH, and the server
1068    /// SHOULD assume that the client has determined the updated value
1069    /// itself or does not care about the updated value.
1070    ///
1071    ///   Note: Regardless of whether or not the ".SILENT" suffix
1072    ///   was used, the server SHOULD send an untagged FETCH
1073    ///   response if a change to a message's flags from an
1074    ///   external source is observed.  The intent is that the
1075    ///   status of the flags is determinate without a race
1076    ///   condition.
1077    ///
1078    /// The currently defined data items that can be stored are:
1079    ///
1080    /// FLAGS \<flag list\>
1081    ///    Replace the flags for the message (other than \Recent) with the
1082    ///    argument.  The new value of the flags is returned as if a FETCH
1083    ///    of those flags was done.
1084    ///
1085    /// FLAGS.SILENT \<flag list\>
1086    ///    Equivalent to FLAGS, but without returning a new value.
1087    ///
1088    /// +FLAGS \<flag list\>
1089    ///    Add the argument to the flags for the message.  The new value
1090    ///    of the flags is returned as if a FETCH of those flags was done.
1091    ///
1092    /// +FLAGS.SILENT \<flag list\>
1093    ///    Equivalent to +FLAGS, but without returning a new value.
1094    ///
1095    /// -FLAGS \<flag list\>
1096    ///    Remove the argument from the flags for the message.  The new
1097    ///    value of the flags is returned as if a FETCH of those flags was
1098    ///    done.
1099    ///
1100    /// -FLAGS.SILENT \<flag list\>
1101    ///    Equivalent to -FLAGS, but without returning a new value.
1102    Store {
1103        /// Set of messages.
1104        sequence_set: SequenceSet,
1105        /// Kind of storage, i.e., replace, add, or remove.
1106        kind: StoreType,
1107        /// Kind of response, i.e., answer or silent.
1108        response: StoreResponse,
1109        /// Flags.
1110        flags: Vec<Flag<'a>>, // FIXME(misuse): must not accept "\*" or "\Recent"
1111        /// Use UID variant.
1112        uid: bool,
1113    },
1114
1115    /// 6.4.7.  COPY Command
1116    ///
1117    /// Arguments:  sequence set
1118    ///             mailbox name
1119    /// Responses:  no specific responses for this command
1120    /// Result:     OK - copy completed
1121    ///             NO - copy error: can't copy those messages or to that
1122    ///                  name
1123    ///             BAD - command unknown or arguments invalid
1124    ///
1125    /// The COPY command copies the specified message(s) to the end of the
1126    /// specified destination mailbox.  The flags and internal date of the
1127    /// message(s) SHOULD be preserved, and the Recent flag SHOULD be set,
1128    /// in the copy.
1129    ///
1130    /// If the destination mailbox does not exist, a server SHOULD return
1131    /// an error.  It SHOULD NOT automatically create the mailbox.  Unless
1132    /// it is certain that the destination mailbox can not be created, the
1133    /// server MUST send the response code "\[TRYCREATE\]" as the prefix of
1134    /// the text of the tagged NO response.  This gives a hint to the
1135    /// client that it can attempt a CREATE command and retry the COPY if
1136    /// the CREATE is successful.
1137    ///
1138    /// If the COPY command is unsuccessful for any reason, server
1139    /// implementations MUST restore the destination mailbox to its state
1140    /// before the COPY attempt.
1141    Copy {
1142        /// Set of messages.
1143        sequence_set: SequenceSet,
1144        /// Destination mailbox.
1145        mailbox: Mailbox<'a>,
1146        /// Use UID variant.
1147        uid: bool,
1148    },
1149
1150    /// The UID mechanism was inlined into copy, fetch, store, and search.
1151    /// as an additional parameter.
1152    ///
1153    /// ### 6.4.8.  UID Command
1154    ///
1155    /// * Arguments:
1156    ///   * command name
1157    ///   * command arguments
1158    /// * Responses:  untagged responses: FETCH, SEARCH
1159    /// * Result:
1160    ///   * OK - UID command completed
1161    ///   * NO - UID command error
1162    ///   * BAD - command unknown or arguments invalid
1163    ///
1164    /// The UID command has two forms.  In the first form, it takes as its
1165    /// arguments a COPY, FETCH, or STORE command with arguments
1166    /// appropriate for the associated command.  However, the numbers in
1167    /// the sequence set argument are unique identifiers instead of
1168    /// message sequence numbers.  Sequence set ranges are permitted, but
1169    /// there is no guarantee that unique identifiers will be contiguous.
1170    ///
1171    /// A non-existent unique identifier is ignored without any error
1172    /// message generated.  Thus, it is possible for a UID FETCH command
1173    /// to return an OK without any data or a UID COPY or UID STORE to
1174    /// return an OK without performing any operations.
1175    ///
1176    /// In the second form, the UID command takes a SEARCH command with
1177    /// SEARCH command arguments.  The interpretation of the arguments is
1178    /// the same as with SEARCH; however, the numbers returned in a SEARCH
1179    /// response for a UID SEARCH command are unique identifiers instead
1180    /// of message sequence numbers.  For example, the command UID SEARCH
1181    /// 1:100 UID 443:557 returns the unique identifiers corresponding to
1182    /// the intersection of two sequence sets, the message sequence number
1183    /// range 1:100 and the UID range 443:557.
1184    ///
1185    ///   Note: in the above example, the UID range 443:557
1186    ///   appears.  The same comment about a non-existent unique
1187    ///   identifier being ignored without any error message also
1188    ///   applies here.  Hence, even if neither UID 443 or 557
1189    ///   exist, this range is valid and would include an existing
1190    ///   UID 495.
1191    ///
1192    ///   Also note that a UID range of 559:* always includes the
1193    ///   UID of the last message in the mailbox, even if 559 is
1194    ///   higher than any assigned UID value.  This is because the
1195    ///   contents of a range are independent of the order of the
1196    ///   range endpoints.  Thus, any UID range with * as one of
1197    ///   the endpoints indicates at least one message (the
1198    ///   message with the highest numbered UID), unless the
1199    ///   mailbox is empty.
1200    ///
1201    ///   The number after the "*" in an untagged FETCH response is always a
1202    ///   message sequence number, not a unique identifier, even for a UID
1203    ///   command response.  However, server implementations MUST implicitly
1204    ///   include the UID message data item as part of any FETCH response
1205    ///   caused by a UID command, regardless of whether a UID was specified
1206    ///   as a message data item to the FETCH.
1207    ///
1208    ///   Note: The rule about including the UID message data item as part
1209    ///   of a FETCH response primarily applies to the UID FETCH and UID
1210    ///   STORE commands, including a UID FETCH command that does not
1211    ///   include UID as a message data item.  Although it is unlikely that
1212    ///   the other UID commands will cause an untagged FETCH, this rule
1213    ///   applies to these commands as well.
1214
1215    // ----- Experimental/Expansion (https://tools.ietf.org/html/rfc3501#section-6.5) -----
1216
1217    // ### 6.5.1.  X<atom> Command
1218    //
1219    // * Arguments:  implementation defined
1220    // * Responses:  implementation defined
1221    // * Result:
1222    //   * OK - command completed
1223    //   * NO - failure
1224    //   * BAD - command unknown or arguments invalid
1225    //
1226    // Any command prefixed with an X is an experimental command.
1227    // Commands which are not part of this specification, a standard or
1228    // standards-track revision of this specification, or an
1229    // IESG-approved experimental protocol, MUST use the X prefix.
1230    //
1231    // Any added untagged responses issued by an experimental command
1232    // MUST also be prefixed with an X.  Server implementations MUST NOT
1233    // send any such untagged responses, unless the client requested it
1234    // by issuing the associated experimental command.
1235    //X,
1236    /// IDLE command.
1237    Idle,
1238
1239    /// ENABLE command.
1240    Enable {
1241        /// Capabilities to enable.
1242        capabilities: NonEmptyVec<CapabilityEnable<'a>>,
1243    },
1244
1245    /// COMPRESS command.
1246    Compress {
1247        /// Compression algorithm.
1248        algorithm: CompressionAlgorithm,
1249    },
1250
1251    /// Takes the name of a quota root and returns the quota root's resource usage and limits in an untagged QUOTA response.
1252    ///
1253    /// Arguments:
1254    /// * quota root
1255    ///
1256    /// Responses:
1257    /// * REQUIRED untagged responses: QUOTA
1258    ///
1259    /// Result:
1260    /// * OK - getquota completed
1261    /// * NO - getquota error: no such quota root, permission denied
1262    /// * BAD - command unknown or arguments invalid
1263    ///
1264    /// # Example (IMAP)
1265    ///
1266    /// ```imap
1267    /// S: * CAPABILITY [...] QUOTA QUOTA=RES-STORAGE [...]
1268    /// [...]
1269    /// C: G0001 GETQUOTA "!partition/sda4"
1270    /// S: * QUOTA "!partition/sda4" (STORAGE 104 10923847)
1271    /// S: G0001 OK Getquota complete
1272    /// ```
1273    GetQuota {
1274        /// Name of quota root.
1275        root: AString<'a>,
1276    },
1277
1278    /// Takes a mailbox name and returns the list of quota roots for the mailbox in an untagged QUOTAROOT response.
1279    /// For each listed quota root, it also returns the quota root's resource usage and limits in an untagged QUOTA response.
1280    ///
1281    /// Arguments:
1282    /// * mailbox name
1283    ///
1284    /// Responses:
1285    /// * REQUIRED untagged responses: QUOTAROOT, QUOTA
1286    ///
1287    /// Result:
1288    /// * OK - getquotaroot completed
1289    /// * NO - getquotaroot error: permission denied
1290    /// * BAD - command unknown or arguments invalid
1291    ///
1292    /// Note that the mailbox name parameter doesn't have to reference an existing mailbox.
1293    /// This can be handy in order to determine which quota root would apply to a mailbox when it gets created
1294    ///
1295    /// # Example (IMAP)
1296    ///
1297    /// ```imap
1298    /// S: * CAPABILITY [...] QUOTA QUOTA=RES-STORAGE QUOTA=RES-MESSAGE
1299    /// [...]
1300    /// C: G0002 GETQUOTAROOT INBOX
1301    /// S: * QUOTAROOT INBOX "#user/alice" "!partition/sda4"
1302    /// S: * QUOTA "#user/alice" (MESSAGE 42 1000)
1303    /// S: * QUOTA "!partition/sda4" (STORAGE 104 10923847)
1304    /// S: G0002 OK Getquotaroot complete
1305    /// ```
1306    GetQuotaRoot {
1307        /// Name of mailbox.
1308        mailbox: Mailbox<'a>,
1309    },
1310
1311    /// Changes the mailbox quota root resource limits to the specified limits.
1312    ///
1313    /// Arguments:
1314    /// * quota root list of resource limits
1315    ///
1316    /// Responses:
1317    /// * untagged responses: QUOTA
1318    ///
1319    /// Result:
1320    ///
1321    /// * OK - setquota completed
1322    /// * NO - setquota error: can't set that data
1323    /// * BAD - command unknown or arguments invalid
1324    ///
1325    /// Note: requires the server to advertise the "QUOTASET" capability.
1326    ///
1327    /// # Example (IMAP)
1328    ///
1329    /// ```imap
1330    /// S: * CAPABILITY [...] QUOTA QUOTASET QUOTA=RES-STORAGE QUOTA=RES-
1331    /// MESSAGE [...]
1332    /// [...]
1333    /// C: S0000 GETQUOTA "#user/alice"
1334    /// S: * QUOTA "#user/alice" (STORAGE 54 111 MESSAGE 42 1000)
1335    /// S: S0000 OK Getquota completed
1336    /// C: S0001 SETQUOTA "#user/alice" (STORAGE 510)
1337    /// S: * QUOTA "#user/alice" (STORAGE 58 512)
1338    /// // The server has rounded the STORAGE quota limit requested to
1339    /// the nearest 512 blocks of 1024 octets; otherwise, another client
1340    /// has performed a near-simultaneous SETQUOTA using a limit of 512.
1341    /// S: S0001 OK Rounded quota
1342    /// C: S0002 SETQUOTA "!partition/sda4" (STORAGE 99999999)
1343    /// S: * QUOTA "!partition/sda4" (STORAGE 104 10923847)
1344    /// // The server has not changed the quota, since this is a
1345    /// filesystem limit, and it cannot be changed. The QUOTA
1346    /// response here is entirely optional.
1347    /// S: S0002 NO Cannot change system limit
1348    /// ```
1349    SetQuota {
1350        /// Name of quota root.
1351        root: AString<'a>,
1352        /// List of resource limits.
1353        quotas: Vec<QuotaSet<'a>>,
1354    },
1355
1356    /// MOVE command.
1357    Move {
1358        /// Set of messages.
1359        sequence_set: SequenceSet,
1360        /// Destination mailbox.
1361        mailbox: Mailbox<'a>,
1362        /// Use UID variant.
1363        uid: bool,
1364    },
1365}
1366
1367impl<'a> CommandBody<'a> {
1368    /// Prepend a tag to finalize the command body to a command.
1369    pub fn tag<T>(self, tag: T) -> Result<Command<'a>, T::Error>
1370    where
1371        T: TryInto<Tag<'a>>,
1372    {
1373        Ok(Command {
1374            tag: tag.try_into()?,
1375            body: self,
1376        })
1377    }
1378
1379    // ----- Constructors -----
1380
1381    /// Construct an AUTHENTICATE command.
1382    pub fn authenticate(mechanism: AuthMechanism<'a>) -> Self {
1383        CommandBody::Authenticate {
1384            mechanism,
1385            initial_response: None,
1386        }
1387    }
1388
1389    /// Construct an AUTHENTICATE command (with an initial response, SASL-IR).
1390    ///
1391    /// Note: Use this only when the server advertised the `SASL-IR` capability.
1392    pub fn authenticate_with_ir<I>(mechanism: AuthMechanism<'a>, initial_response: I) -> Self
1393    where
1394        I: Into<Cow<'a, [u8]>>,
1395    {
1396        CommandBody::Authenticate {
1397            mechanism,
1398            initial_response: Some(Secret::new(initial_response.into())),
1399        }
1400    }
1401
1402    /// Construct a LOGIN command.
1403    pub fn login<U, P>(username: U, password: P) -> Result<Self, LoginError<U::Error, P::Error>>
1404    where
1405        U: TryInto<AString<'a>>,
1406        P: TryInto<AString<'a>>,
1407    {
1408        Ok(CommandBody::Login {
1409            username: username.try_into().map_err(LoginError::Username)?,
1410            password: Secret::new(password.try_into().map_err(LoginError::Password)?),
1411        })
1412    }
1413
1414    /// Construct a SELECT command.
1415    pub fn select<M>(mailbox: M) -> Result<Self, M::Error>
1416    where
1417        M: TryInto<Mailbox<'a>>,
1418    {
1419        Ok(CommandBody::Select {
1420            mailbox: mailbox.try_into()?,
1421        })
1422    }
1423
1424    /// Construct an EXAMINE command.
1425    pub fn examine<M>(mailbox: M) -> Result<Self, M::Error>
1426    where
1427        M: TryInto<Mailbox<'a>>,
1428    {
1429        Ok(CommandBody::Examine {
1430            mailbox: mailbox.try_into()?,
1431        })
1432    }
1433
1434    /// Construct a CREATE command.
1435    pub fn create<M>(mailbox: M) -> Result<Self, M::Error>
1436    where
1437        M: TryInto<Mailbox<'a>>,
1438    {
1439        Ok(CommandBody::Create {
1440            mailbox: mailbox.try_into()?,
1441        })
1442    }
1443
1444    /// Construct a DELETE command.
1445    pub fn delete<M>(mailbox: M) -> Result<Self, M::Error>
1446    where
1447        M: TryInto<Mailbox<'a>>,
1448    {
1449        Ok(CommandBody::Delete {
1450            mailbox: mailbox.try_into()?,
1451        })
1452    }
1453
1454    /// Construct a RENAME command.
1455    pub fn rename<F, T>(mailbox: F, new_mailbox: T) -> Result<Self, RenameError<F::Error, T::Error>>
1456    where
1457        F: TryInto<Mailbox<'a>>,
1458        T: TryInto<Mailbox<'a>>,
1459    {
1460        Ok(CommandBody::Rename {
1461            from: mailbox.try_into().map_err(RenameError::From)?,
1462            to: new_mailbox.try_into().map_err(RenameError::To)?,
1463        })
1464    }
1465
1466    /// Construct a SUBSCRIBE command.
1467    pub fn subscribe<M>(mailbox: M) -> Result<Self, M::Error>
1468    where
1469        M: TryInto<Mailbox<'a>>,
1470    {
1471        Ok(CommandBody::Subscribe {
1472            mailbox: mailbox.try_into()?,
1473        })
1474    }
1475
1476    /// Construct an UNSUBSCRIBE command.
1477    pub fn unsubscribe<M>(mailbox: M) -> Result<Self, M::Error>
1478    where
1479        M: TryInto<Mailbox<'a>>,
1480    {
1481        Ok(CommandBody::Unsubscribe {
1482            mailbox: mailbox.try_into()?,
1483        })
1484    }
1485
1486    /// Construct a LIST command.
1487    pub fn list<A, B>(
1488        reference: A,
1489        mailbox_wildcard: B,
1490    ) -> Result<Self, ListError<A::Error, B::Error>>
1491    where
1492        A: TryInto<Mailbox<'a>>,
1493        B: TryInto<ListMailbox<'a>>,
1494    {
1495        Ok(CommandBody::List {
1496            reference: reference.try_into().map_err(ListError::Reference)?,
1497            mailbox_wildcard: mailbox_wildcard.try_into().map_err(ListError::Mailbox)?,
1498        })
1499    }
1500
1501    /// Construct a LSUB command.
1502    pub fn lsub<A, B>(
1503        reference: A,
1504        mailbox_wildcard: B,
1505    ) -> Result<Self, ListError<A::Error, B::Error>>
1506    where
1507        A: TryInto<Mailbox<'a>>,
1508        B: TryInto<ListMailbox<'a>>,
1509    {
1510        Ok(CommandBody::Lsub {
1511            reference: reference.try_into().map_err(ListError::Reference)?,
1512            mailbox_wildcard: mailbox_wildcard.try_into().map_err(ListError::Mailbox)?,
1513        })
1514    }
1515
1516    /// Construct a STATUS command.
1517    pub fn status<M, I>(mailbox: M, item_names: I) -> Result<Self, M::Error>
1518    where
1519        M: TryInto<Mailbox<'a>>,
1520        I: Into<Cow<'a, [StatusDataItemName]>>,
1521    {
1522        let mailbox = mailbox.try_into()?;
1523
1524        Ok(CommandBody::Status {
1525            mailbox,
1526            item_names: item_names.into(),
1527        })
1528    }
1529
1530    /// Construct an APPEND command.
1531    pub fn append<M, D>(
1532        mailbox: M,
1533        flags: Vec<Flag<'a>>,
1534        date: Option<DateTime>,
1535        message: D,
1536    ) -> Result<Self, AppendError<M::Error, D::Error>>
1537    where
1538        M: TryInto<Mailbox<'a>>,
1539        D: TryInto<Literal<'a>>,
1540    {
1541        Ok(CommandBody::Append {
1542            mailbox: mailbox.try_into().map_err(AppendError::Mailbox)?,
1543            flags,
1544            date,
1545            message: message.try_into().map_err(AppendError::Data)?,
1546        })
1547    }
1548
1549    /// Construct a SEARCH command.
1550    pub fn search(charset: Option<Charset<'a>>, criteria: SearchKey<'a>, uid: bool) -> Self {
1551        CommandBody::Search {
1552            charset,
1553            criteria,
1554            uid,
1555        }
1556    }
1557
1558    /// Construct a FETCH command.
1559    pub fn fetch<S, I>(sequence_set: S, macro_or_item_names: I, uid: bool) -> Result<Self, S::Error>
1560    where
1561        S: TryInto<SequenceSet>,
1562        I: Into<MacroOrMessageDataItemNames<'a>>,
1563    {
1564        let sequence_set = sequence_set.try_into()?;
1565
1566        Ok(CommandBody::Fetch {
1567            sequence_set,
1568            macro_or_item_names: macro_or_item_names.into(),
1569            uid,
1570        })
1571    }
1572
1573    /// Construct a STORE command.
1574    pub fn store<S>(
1575        sequence_set: S,
1576        kind: StoreType,
1577        response: StoreResponse,
1578        flags: Vec<Flag<'a>>,
1579        uid: bool,
1580    ) -> Result<Self, S::Error>
1581    where
1582        S: TryInto<SequenceSet>,
1583    {
1584        let sequence_set = sequence_set.try_into()?;
1585
1586        Ok(CommandBody::Store {
1587            sequence_set,
1588            kind,
1589            response,
1590            flags,
1591            uid,
1592        })
1593    }
1594
1595    /// Construct a COPY command.
1596    pub fn copy<S, M>(
1597        sequence_set: S,
1598        mailbox: M,
1599        uid: bool,
1600    ) -> Result<Self, CopyError<S::Error, M::Error>>
1601    where
1602        S: TryInto<SequenceSet>,
1603        M: TryInto<Mailbox<'a>>,
1604    {
1605        Ok(CommandBody::Copy {
1606            sequence_set: sequence_set.try_into().map_err(CopyError::Sequence)?,
1607            mailbox: mailbox.try_into().map_err(CopyError::Mailbox)?,
1608            uid,
1609        })
1610    }
1611
1612    /// Get the name of the command.
1613    pub fn name(&self) -> &'static str {
1614        match self {
1615            Self::Capability => "CAPABILITY",
1616            Self::Noop => "NOOP",
1617            Self::Logout => "LOGOUT",
1618            #[cfg(feature = "starttls")]
1619            Self::StartTLS => "STARTTLS",
1620            Self::Authenticate { .. } => "AUTHENTICATE",
1621            Self::Login { .. } => "LOGIN",
1622            Self::Select { .. } => "SELECT",
1623            Self::Unselect => "UNSELECT",
1624            Self::Examine { .. } => "EXAMINE",
1625            Self::Create { .. } => "CREATE",
1626            Self::Delete { .. } => "DELETE",
1627            Self::Rename { .. } => "RENAME",
1628            Self::Subscribe { .. } => "SUBSCRIBE",
1629            Self::Unsubscribe { .. } => "UNSUBSCRIBE",
1630            Self::List { .. } => "LIST",
1631            Self::Lsub { .. } => "LSUB",
1632            Self::Status { .. } => "STATUS",
1633            Self::Append { .. } => "APPEND",
1634            Self::Check => "CHECK",
1635            Self::Close => "CLOSE",
1636            Self::Expunge => "EXPUNGE",
1637            Self::Search { .. } => "SEARCH",
1638            Self::Fetch { .. } => "FETCH",
1639            Self::Store { .. } => "STORE",
1640            Self::Copy { .. } => "COPY",
1641            Self::Idle => "IDLE",
1642            Self::Enable { .. } => "ENABLE",
1643            Self::Compress { .. } => "COMPRESS",
1644            Self::GetQuota { .. } => "GETQUOTA",
1645            Self::GetQuotaRoot { .. } => "GETQUOTAROOT",
1646            Self::SetQuota { .. } => "SETQUOTA",
1647            Self::Move { .. } => "MOVE",
1648        }
1649    }
1650}
1651
1652/// Error-related types.
1653pub mod error {
1654    use thiserror::Error;
1655
1656    #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)]
1657    pub enum LoginError<U, P> {
1658        #[error("Invalid username: {0}")]
1659        Username(U),
1660        #[error("Invalid password: {0}")]
1661        Password(P),
1662    }
1663
1664    #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)]
1665    pub enum RenameError<F, T> {
1666        #[error("Invalid (from) mailbox: {0}")]
1667        From(F),
1668        #[error("Invalid (to) mailbox: {0}")]
1669        To(T),
1670    }
1671
1672    #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)]
1673    pub enum ListError<R, M> {
1674        #[error("Invalid reference: {0}")]
1675        Reference(R),
1676        #[error("Invalid mailbox: {0}")]
1677        Mailbox(M),
1678    }
1679
1680    #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)]
1681    pub enum AppendError<M, D> {
1682        #[error("Invalid mailbox: {0}")]
1683        Mailbox(M),
1684        #[error("Invalid data: {0}")]
1685        Data(D),
1686    }
1687
1688    #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)]
1689    pub enum CopyError<S, M> {
1690        #[error("Invalid sequence: {0}")]
1691        Sequence(S),
1692        #[error("Invalid mailbox: {0}")]
1693        Mailbox(M),
1694    }
1695}
1696
1697#[cfg(test)]
1698mod tests {
1699    use chrono::DateTime as ChronoDateTime;
1700
1701    use super::*;
1702    use crate::{
1703        auth::AuthMechanism,
1704        core::{AString, Charset, IString, Literal, NonEmptyVec},
1705        datetime::DateTime,
1706        extensions::{
1707            compress::CompressionAlgorithm,
1708            enable::{CapabilityEnable, Utf8Kind},
1709        },
1710        fetch::{Macro, MacroOrMessageDataItemNames, MessageDataItemName, Part, Section},
1711        flag::{Flag, StoreType},
1712        mailbox::{ListMailbox, Mailbox},
1713        search::SearchKey,
1714        secret::Secret,
1715        sequence::{SeqOrUid, Sequence, SequenceSet},
1716        status::StatusDataItemName,
1717    };
1718
1719    #[test]
1720    fn test_conversion_command_body() {
1721        let cmds = vec![
1722            CommandBody::Capability,
1723            CommandBody::Noop,
1724            CommandBody::Logout,
1725            #[cfg(feature = "starttls")]
1726            CommandBody::StartTLS,
1727            CommandBody::authenticate(AuthMechanism::Plain),
1728            CommandBody::authenticate(AuthMechanism::Login),
1729            CommandBody::authenticate_with_ir(AuthMechanism::Plain, b"XXXXXXXX".as_ref()),
1730            CommandBody::authenticate_with_ir(AuthMechanism::Login, b"YYYYYYYY".as_ref()),
1731            CommandBody::login("alice", "I_am_an_atom").unwrap(),
1732            CommandBody::login("alice", "I am \\ \"quoted\"").unwrap(),
1733            CommandBody::login("alice", "I am a literal²").unwrap(),
1734            CommandBody::login(
1735                AString::Atom("alice".try_into().unwrap()),
1736                AString::String(crate::core::IString::Literal(
1737                    vec![0xff, 0xff, 0xff].try_into().unwrap(),
1738                )),
1739            )
1740            .unwrap(),
1741            CommandBody::select("inbox").unwrap(),
1742            CommandBody::select("atom").unwrap(),
1743            CommandBody::select("C:\\").unwrap(),
1744            CommandBody::select("²").unwrap(),
1745            CommandBody::select("Trash").unwrap(),
1746            CommandBody::examine("inbox").unwrap(),
1747            CommandBody::examine("atom").unwrap(),
1748            CommandBody::examine("C:\\").unwrap(),
1749            CommandBody::examine("²").unwrap(),
1750            CommandBody::examine("Trash").unwrap(),
1751            CommandBody::create("inBoX").unwrap(),
1752            CommandBody::delete("inBOX").unwrap(),
1753            CommandBody::rename("iNBoS", "INboX").unwrap(),
1754            CommandBody::subscribe("inbox").unwrap(),
1755            CommandBody::unsubscribe("INBOX").unwrap(),
1756            CommandBody::list("iNbOx", "test").unwrap(),
1757            CommandBody::list("inbox", ListMailbox::Token("test".try_into().unwrap())).unwrap(),
1758            CommandBody::lsub(
1759                "inbox",
1760                ListMailbox::String(IString::Quoted("\x7f".try_into().unwrap())),
1761            )
1762            .unwrap(),
1763            CommandBody::list("inBoX", ListMailbox::Token("test".try_into().unwrap())).unwrap(),
1764            CommandBody::lsub(
1765                "INBOX",
1766                ListMailbox::String(IString::Quoted("\x7f".try_into().unwrap())),
1767            )
1768            .unwrap(),
1769            CommandBody::status("inbox", vec![StatusDataItemName::Messages]).unwrap(),
1770            CommandBody::append(
1771                "inbox",
1772                vec![],
1773                Some(
1774                    DateTime::try_from(
1775                        ChronoDateTime::parse_from_rfc2822("Tue, 1 Jul 2003 10:52:37 +0200")
1776                            .unwrap(),
1777                    )
1778                    .unwrap(),
1779                ),
1780                vec![0xff, 0xff, 0xff],
1781            )
1782            .unwrap(),
1783            CommandBody::append(
1784                "inbox",
1785                vec![Flag::Keyword("test".try_into().unwrap())],
1786                Some(
1787                    DateTime::try_from(
1788                        ChronoDateTime::parse_from_rfc2822("Tue, 1 Jul 2003 10:52:37 +0200")
1789                            .unwrap(),
1790                    )
1791                    .unwrap(),
1792                ),
1793                vec![0xff, 0xff, 0xff],
1794            )
1795            .unwrap(),
1796            CommandBody::Check,
1797            CommandBody::Close,
1798            CommandBody::Expunge,
1799            CommandBody::search(
1800                None,
1801                SearchKey::And(
1802                    vec![SearchKey::All, SearchKey::New, SearchKey::Unseen]
1803                        .try_into()
1804                        .unwrap(),
1805                ),
1806                false,
1807            ),
1808            CommandBody::search(
1809                None,
1810                SearchKey::And(
1811                    vec![SearchKey::All, SearchKey::New, SearchKey::Unseen]
1812                        .try_into()
1813                        .unwrap(),
1814                ),
1815                true,
1816            ),
1817            CommandBody::search(
1818                None,
1819                SearchKey::And(
1820                    vec![SearchKey::SequenceSet(SequenceSet(
1821                        vec![Sequence::Single(SeqOrUid::Value(42.try_into().unwrap()))]
1822                            .try_into()
1823                            .unwrap(),
1824                    ))]
1825                    .try_into()
1826                    .unwrap(),
1827                ),
1828                true,
1829            ),
1830            CommandBody::search(None, SearchKey::SequenceSet("42".try_into().unwrap()), true),
1831            CommandBody::search(None, SearchKey::SequenceSet("*".try_into().unwrap()), true),
1832            CommandBody::search(
1833                None,
1834                SearchKey::Or(Box::new(SearchKey::Draft), Box::new(SearchKey::All)),
1835                true,
1836            ),
1837            CommandBody::search(
1838                Some(Charset::try_from("UTF-8").unwrap()),
1839                SearchKey::Or(Box::new(SearchKey::Draft), Box::new(SearchKey::All)),
1840                true,
1841            ),
1842            CommandBody::fetch(
1843                "1",
1844                vec![MessageDataItemName::BodyExt {
1845                    partial: None,
1846                    section: Some(Section::Part(Part(
1847                        vec![1.try_into().unwrap(), 1.try_into().unwrap()]
1848                            .try_into()
1849                            .unwrap(),
1850                    ))),
1851                    peek: true,
1852                }],
1853                false,
1854            )
1855            .unwrap(),
1856            CommandBody::fetch("1:*,2,3", Macro::Full, true).unwrap(),
1857            CommandBody::store(
1858                "1,2:*",
1859                StoreType::Remove,
1860                StoreResponse::Answer,
1861                vec![Flag::Seen, Flag::Draft],
1862                false,
1863            )
1864            .unwrap(),
1865            CommandBody::store(
1866                "1:5",
1867                StoreType::Add,
1868                StoreResponse::Answer,
1869                vec![Flag::Keyword("TEST".try_into().unwrap())],
1870                true,
1871            )
1872            .unwrap(),
1873            CommandBody::copy("1", "inbox", false).unwrap(),
1874            CommandBody::copy("1337", "archive", true).unwrap(),
1875        ];
1876
1877        for (no, cmd_body) in cmds.into_iter().enumerate() {
1878            println!("Test: {}, {:?}", no, cmd_body);
1879
1880            let _ = cmd_body.tag(format!("A{}", no)).unwrap();
1881        }
1882    }
1883
1884    #[test]
1885    fn test_command_body_name() {
1886        let tests = [
1887            (CommandBody::Capability, "CAPABILITY"),
1888            (CommandBody::Noop, "NOOP"),
1889            (CommandBody::Logout, "LOGOUT"),
1890            #[cfg(feature = "starttls")]
1891            (CommandBody::StartTLS, "STARTTLS"),
1892            (
1893                CommandBody::Authenticate {
1894                    mechanism: AuthMechanism::Plain,
1895                    initial_response: None,
1896                },
1897                "AUTHENTICATE",
1898            ),
1899            (
1900                CommandBody::Login {
1901                    username: AString::try_from("user").unwrap(),
1902                    password: Secret::new(AString::try_from("pass").unwrap()),
1903                },
1904                "LOGIN",
1905            ),
1906            (
1907                CommandBody::Select {
1908                    mailbox: Mailbox::Inbox,
1909                },
1910                "SELECT",
1911            ),
1912            (CommandBody::Unselect, "UNSELECT"),
1913            (
1914                CommandBody::Examine {
1915                    mailbox: Mailbox::Inbox,
1916                },
1917                "EXAMINE",
1918            ),
1919            (
1920                CommandBody::Create {
1921                    mailbox: Mailbox::Inbox,
1922                },
1923                "CREATE",
1924            ),
1925            (
1926                CommandBody::Delete {
1927                    mailbox: Mailbox::Inbox,
1928                },
1929                "DELETE",
1930            ),
1931            (
1932                CommandBody::Rename {
1933                    from: Mailbox::Inbox,
1934                    to: Mailbox::Inbox,
1935                },
1936                "RENAME",
1937            ),
1938            (
1939                CommandBody::Subscribe {
1940                    mailbox: Mailbox::Inbox,
1941                },
1942                "SUBSCRIBE",
1943            ),
1944            (
1945                CommandBody::Unsubscribe {
1946                    mailbox: Mailbox::Inbox,
1947                },
1948                "UNSUBSCRIBE",
1949            ),
1950            (
1951                CommandBody::List {
1952                    reference: Mailbox::Inbox,
1953                    mailbox_wildcard: ListMailbox::try_from("").unwrap(),
1954                },
1955                "LIST",
1956            ),
1957            (
1958                CommandBody::Lsub {
1959                    reference: Mailbox::Inbox,
1960                    mailbox_wildcard: ListMailbox::try_from("").unwrap(),
1961                },
1962                "LSUB",
1963            ),
1964            (
1965                CommandBody::Status {
1966                    mailbox: Mailbox::Inbox,
1967                    item_names: vec![].into(),
1968                },
1969                "STATUS",
1970            ),
1971            (
1972                CommandBody::Append {
1973                    mailbox: Mailbox::Inbox,
1974                    date: None,
1975                    message: Literal::try_from("").unwrap(),
1976                    flags: vec![],
1977                },
1978                "APPEND",
1979            ),
1980            (CommandBody::Check, "CHECK"),
1981            (CommandBody::Close, "CLOSE"),
1982            (CommandBody::Expunge, "EXPUNGE"),
1983            (
1984                CommandBody::Search {
1985                    charset: None,
1986                    criteria: SearchKey::Recent,
1987                    uid: true,
1988                },
1989                "SEARCH",
1990            ),
1991            (
1992                CommandBody::Fetch {
1993                    sequence_set: SequenceSet::try_from(1u32).unwrap(),
1994                    macro_or_item_names: MacroOrMessageDataItemNames::Macro(Macro::Full),
1995                    uid: true,
1996                },
1997                "FETCH",
1998            ),
1999            (
2000                CommandBody::Store {
2001                    sequence_set: SequenceSet::try_from(1).unwrap(),
2002                    flags: vec![],
2003                    response: StoreResponse::Silent,
2004                    kind: StoreType::Add,
2005                    uid: true,
2006                },
2007                "STORE",
2008            ),
2009            (
2010                CommandBody::Copy {
2011                    sequence_set: SequenceSet::try_from(1).unwrap(),
2012                    mailbox: Mailbox::Inbox,
2013                    uid: true,
2014                },
2015                "COPY",
2016            ),
2017            (CommandBody::Idle, "IDLE"),
2018            (
2019                CommandBody::Enable {
2020                    capabilities: NonEmptyVec::from(CapabilityEnable::Utf8(Utf8Kind::Only)),
2021                },
2022                "ENABLE",
2023            ),
2024            (
2025                CommandBody::Compress {
2026                    algorithm: CompressionAlgorithm::Deflate,
2027                },
2028                "COMPRESS",
2029            ),
2030            (
2031                CommandBody::GetQuota {
2032                    root: AString::try_from("root").unwrap(),
2033                },
2034                "GETQUOTA",
2035            ),
2036            (
2037                CommandBody::GetQuotaRoot {
2038                    mailbox: Mailbox::Inbox,
2039                },
2040                "GETQUOTAROOT",
2041            ),
2042            (
2043                CommandBody::SetQuota {
2044                    root: AString::try_from("root").unwrap(),
2045                    quotas: vec![],
2046                },
2047                "SETQUOTA",
2048            ),
2049            (
2050                CommandBody::Move {
2051                    sequence_set: SequenceSet::try_from(1).unwrap(),
2052                    mailbox: Mailbox::Inbox,
2053                    uid: true,
2054                },
2055                "MOVE",
2056            ),
2057        ];
2058
2059        for (test, expected) in tests {
2060            assert_eq!(test.name(), expected);
2061        }
2062    }
2063}