• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

duesee / imap-codec / 12847392793

18 Jan 2025 09:02PM UTC coverage: 91.608% (-1.3%) from 92.896%
12847392793

Pull #631

github

web-flow
Merge 88a359ee6 into a4498b1ec
Pull Request #631: feat: Implement CONDSTORE/QRESYNC (3/N)

179 of 367 new or added lines in 11 files covered. (48.77%)

3 existing lines in 3 files now uncovered.

11462 of 12512 relevant lines covered (91.61%)

886.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.29
/imap-types/src/command.rs
1
//! Client Commands.
2
//!
3
//! See <https://tools.ietf.org/html/rfc3501#section-6>.
4

5
use std::borrow::Cow;
6
#[cfg(feature = "ext_condstore_qresync")]
7
use std::num::NonZeroU32;
8
#[cfg(feature = "ext_condstore_qresync")]
9
use std::num::NonZeroU64;
10

11
#[cfg(feature = "arbitrary")]
12
use arbitrary::Arbitrary;
13
use bounded_static_derive::ToStatic;
14
#[cfg(feature = "serde")]
15
use serde::{Deserialize, Serialize};
16

17
#[cfg(feature = "ext_id")]
18
use crate::core::{IString, NString};
19
#[cfg(feature = "ext_metadata")]
20
use crate::extensions::metadata::{Entry, EntryValue, GetMetadataOption};
21
use crate::{
22
    auth::AuthMechanism,
23
    command::error::{AppendError, CopyError, ListError, LoginError, RenameError},
24
    core::{AString, Charset, Literal, Tag, Vec1},
25
    datetime::DateTime,
26
    extensions::{
27
        binary::LiteralOrLiteral8, compress::CompressionAlgorithm, enable::CapabilityEnable,
28
        quota::QuotaSet, sort::SortCriterion, thread::ThreadingAlgorithm,
29
    },
30
    fetch::MacroOrMessageDataItemNames,
31
    flag::{Flag, StoreResponse, StoreType},
32
    mailbox::{ListMailbox, Mailbox},
33
    search::SearchKey,
34
    secret::Secret,
35
    sequence::SequenceSet,
36
    status::StatusDataItemName,
37
};
38

39
/// Command.
40
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
984✔
41
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
×
42
#[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)]
4,052✔
43
pub struct Command<'a> {
44
    /// Tag.
45
    pub tag: Tag<'a>,
46
    /// Body, e.g., CAPABILITY, LOGIN, SELECT, etc.
47
    pub body: CommandBody<'a>,
48
}
49

50
impl<'a> Command<'a> {
51
    /// Create a new command.
52
    pub fn new<T>(tag: T, body: CommandBody<'a>) -> Result<Self, T::Error>
178✔
53
    where
178✔
54
        T: TryInto<Tag<'a>>,
178✔
55
    {
178✔
56
        Ok(Self {
178✔
57
            tag: tag.try_into()?,
178✔
58
            body,
160✔
59
        })
60
    }
178✔
61

62
    /// Get the command name.
63
    pub fn name(&self) -> &'static str {
×
64
        self.body.name()
×
65
    }
×
66
}
67

68
/// Command body.
69
///
70
/// This enum is used to encode all the different commands.
71
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
×
72
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
×
73
#[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)]
74
pub enum CommandBody<'a> {
75
    // ----- Any State (see https://tools.ietf.org/html/rfc3501#section-6.1) -----
76
    /// ### 6.1.1.  CAPABILITY Command
77
    ///
78
    /// * Arguments:  none
79
    /// * Responses:  REQUIRED untagged response: CAPABILITY
80
    /// * Result:
81
    ///   * OK - capability completed
82
    ///   * BAD - command unknown or arguments invalid
83
    ///
84
    /// The CAPABILITY command requests a listing of capabilities that the
85
    /// server supports.  The server MUST send a single untagged
86
    /// CAPABILITY response with "IMAP4rev1" as one of the listed
87
    /// capabilities before the (tagged) OK response.
88
    ///
89
    /// A capability name which begins with "AUTH=" indicates that the
90
    /// server supports that particular authentication mechanism.  All
91
    /// such names are, by definition, part of this specification.  For
92
    /// example, the authorization capability for an experimental
93
    /// "blurdybloop" authenticator would be "AUTH=XBLURDYBLOOP" and not
94
    /// "XAUTH=BLURDYBLOOP" or "XAUTH=XBLURDYBLOOP".
95
    ///
96
    /// Other capability names refer to extensions, revisions, or
97
    /// amendments to this specification.  See the documentation of the
98
    /// CAPABILITY response for additional information.  No capabilities,
99
    /// beyond the base IMAP4rev1 set defined in this specification, are
100
    /// enabled without explicit client action to invoke the capability.
101
    ///
102
    /// Client and server implementations MUST implement the STARTTLS,
103
    /// LOGINDISABLED, and AUTH=PLAIN (described in [IMAP-TLS])
104
    /// capabilities.  See the Security Considerations section for
105
    /// important information.
106
    ///
107
    /// See the section entitled "Client Commands -
108
    /// Experimental/Expansion" for information about the form of site or
109
    /// implementation-specific capabilities.
110
    Capability,
111

112
    /// ### 6.1.2.  NOOP Command
113
    ///
114
    /// * Arguments:  none
115
    /// * Responses:  no specific responses for this command (but see below)
116
    /// * Result:
117
    ///   * OK - noop completed
118
    ///   * BAD - command unknown or arguments invalid
119
    ///
120
    /// The NOOP command always succeeds.  It does nothing.
121
    ///
122
    /// Since any command can return a status update as untagged data, the
123
    /// NOOP command can be used as a periodic poll for new messages or
124
    /// message status updates during a period of inactivity (this is the
125
    /// preferred method to do this).  The NOOP command can also be used
126
    /// to reset any inactivity autologout timer on the server.
127
    Noop,
128

129
    /// ### 6.1.3.  LOGOUT Command
130
    ///
131
    /// * Arguments:  none
132
    /// * Responses:  REQUIRED untagged response: BYE
133
    /// * Result:
134
    ///   * OK - logout completed
135
    ///   * BAD - command unknown or arguments invalid
136
    ///
137
    /// The LOGOUT command informs the server that the client is done with
138
    /// the connection.  The server MUST send a BYE untagged response
139
    /// before the (tagged) OK response, and then close the network
140
    /// connection.
141
    Logout,
142

143
    // ----- Not Authenticated State (https://tools.ietf.org/html/rfc3501#section-6.2) -----
144
    /// ### 6.2.1.  STARTTLS Command
145
    ///
146
    /// * Arguments:  none
147
    /// * Responses:  no specific response for this command
148
    /// * Result:
149
    ///   * OK - starttls completed, begin TLS negotiation
150
    ///   * BAD - command unknown or arguments invalid
151
    ///
152
    /// A \[TLS\] negotiation begins immediately after the CRLF at the end
153
    /// of the tagged OK response from the server.  Once a client issues a
154
    /// STARTTLS command, it MUST NOT issue further commands until a
155
    /// server response is seen and the \[TLS\] negotiation is complete.
156
    ///
157
    /// The server remains in the non-authenticated state, even if client
158
    /// credentials are supplied during the \[TLS\] negotiation.  This does
159
    /// not preclude an authentication mechanism such as EXTERNAL (defined
160
    /// in \[SASL\]) from using client identity determined by the \[TLS\]
161
    /// negotiation.
162
    ///
163
    /// Once \[TLS\] has been started, the client MUST discard cached
164
    /// information about server capabilities and SHOULD re-issue the
165
    /// CAPABILITY command.  This is necessary to protect against man-in-
166
    /// the-middle attacks which alter the capabilities list prior to
167
    /// STARTTLS.  The server MAY advertise different capabilities after
168
    /// STARTTLS.
169
    ///
170
    /// <div class="warning">
171
    /// This must only be used when the server advertised support for it sending the STARTTLS capability.
172
    ///
173
    /// Try to avoid STARTTLS using implicit TLS on port 993.
174
    /// </div>
175
    #[cfg(feature = "starttls")]
176
    #[cfg_attr(docsrs, doc(cfg(feature = "starttls")))]
177
    StartTLS,
178

179
    /// ### 6.2.2.  AUTHENTICATE Command
180
    ///
181
    /// * Arguments:  authentication mechanism name
182
    /// * Responses:  continuation data can be requested
183
    /// * Result:
184
    ///   * OK - authenticate completed, now in authenticated state
185
    ///   * NO - authenticate failure: unsupported authentication
186
    ///          mechanism, credentials rejected
187
    ///   * BAD - command unknown or arguments invalid,
188
    ///           authentication exchange cancelled
189
    ///
190
    /// The AUTHENTICATE command indicates a \[SASL\] authentication
191
    /// mechanism to the server.  If the server supports the requested
192
    /// authentication mechanism, it performs an authentication protocol
193
    /// exchange to authenticate and identify the client.  It MAY also
194
    /// negotiate an OPTIONAL security layer for subsequent protocol
195
    /// interactions.  If the requested authentication mechanism is not
196
    /// supported, the server SHOULD reject the AUTHENTICATE command by
197
    /// sending a tagged NO response.
198
    ///
199
    /// The AUTHENTICATE command does not support the optional "initial
200
    /// response" feature of \[SASL\].  Section 5.1 of \[SASL\] specifies how
201
    /// to handle an authentication mechanism which uses an initial
202
    /// response.
203
    ///
204
    /// The service name specified by this protocol's profile of \[SASL\] is
205
    /// "imap".
206
    ///
207
    /// The authentication protocol exchange consists of a series of
208
    /// server challenges and client responses that are specific to the
209
    /// authentication mechanism.  A server challenge consists of a
210
    /// command continuation request response with the "+" token followed
211
    /// by a BASE64 encoded string.  The client response consists of a
212
    /// single line consisting of a BASE64 encoded string.  If the client
213
    /// wishes to cancel an authentication exchange, it issues a line
214
    /// consisting of a single "*".  If the server receives such a
215
    /// response, it MUST reject the AUTHENTICATE command by sending a
216
    /// tagged BAD response.
217
    ///
218
    /// If a security layer is negotiated through the \[SASL\]
219
    /// authentication exchange, it takes effect immediately following the
220
    /// CRLF that concludes the authentication exchange for the client,
221
    /// and the CRLF of the tagged OK response for the server.
222
    ///
223
    /// While client and server implementations MUST implement the
224
    /// AUTHENTICATE command itself, it is not required to implement any
225
    /// authentication mechanisms other than the PLAIN mechanism described
226
    /// in [IMAP-TLS].  Also, an authentication mechanism is not required
227
    /// to support any security layers.
228
    ///
229
    ///   Note: a server implementation MUST implement a
230
    ///   configuration in which it does NOT permit any plaintext
231
    ///   password mechanisms, unless either the STARTTLS command
232
    ///   has been negotiated or some other mechanism that
233
    ///   protects the session from password snooping has been
234
    ///   provided.  Server sites SHOULD NOT use any configuration
235
    ///   which permits a plaintext password mechanism without
236
    ///   such a protection mechanism against password snooping.
237
    ///   Client and server implementations SHOULD implement
238
    ///   additional \[SASL\] mechanisms that do not use plaintext
239
    ///   passwords, such the GSSAPI mechanism described in \[SASL\]
240
    ///   and/or the [DIGEST-MD5] mechanism.
241
    ///
242
    /// Servers and clients can support multiple authentication
243
    /// mechanisms.  The server SHOULD list its supported authentication
244
    /// mechanisms in the response to the CAPABILITY command so that the
245
    /// client knows which authentication mechanisms to use.
246
    ///
247
    /// A server MAY include a CAPABILITY response code in the tagged OK
248
    /// response of a successful AUTHENTICATE command in order to send
249
    /// capabilities automatically.  It is unnecessary for a client to
250
    /// send a separate CAPABILITY command if it recognizes these
251
    /// automatic capabilities.  This should only be done if a security
252
    /// layer was not negotiated by the AUTHENTICATE command, because the
253
    /// tagged OK response as part of an AUTHENTICATE command is not
254
    /// protected by encryption/integrity checking.  \[SASL\] requires the
255
    /// client to re-issue a CAPABILITY command in this case.
256
    ///
257
    /// If an AUTHENTICATE command fails with a NO response, the client
258
    /// MAY try another authentication mechanism by issuing another
259
    /// AUTHENTICATE command.  It MAY also attempt to authenticate by
260
    /// using the LOGIN command (see section 6.2.3 for more detail).  In
261
    /// other words, the client MAY request authentication types in
262
    /// decreasing order of preference, with the LOGIN command as a last
263
    /// resort.
264
    ///
265
    /// The authorization identity passed from the client to the server
266
    /// during the authentication exchange is interpreted by the server as
267
    /// the user name whose privileges the client is requesting.
268
    Authenticate {
269
        /// Authentication mechanism.
270
        mechanism: AuthMechanism<'a>,
271
        /// Initial response (if any).
272
        ///
273
        /// This type holds the raw binary data, i.e., a `Vec<u8>`, *not* the BASE64 string.
274
        ///
275
        /// <div class="warning">
276
        /// This extension must only be used when the server advertised support for it sending the SASL-IR capability.
277
        /// </div>
278
        initial_response: Option<Secret<Cow<'a, [u8]>>>,
279
    },
280

281
    /// ### 6.2.3.  LOGIN Command
282
    ///
283
    /// * Arguments:
284
    ///   * user name
285
    ///   * password
286
    /// * Responses:  no specific responses for this command
287
    /// * Result:
288
    ///   * OK - login completed, now in authenticated state
289
    ///   * NO - login failure: user name or password rejected
290
    ///   * BAD - command unknown or arguments invalid
291
    ///
292
    /// The LOGIN command identifies the client to the server and carries
293
    /// the plaintext password authenticating this user.
294
    ///
295
    /// A server MAY include a CAPABILITY response code in the tagged OK
296
    /// response to a successful LOGIN command in order to send
297
    /// capabilities automatically.  It is unnecessary for a client to
298
    /// send a separate CAPABILITY command if it recognizes these
299
    /// automatic capabilities.
300
    ///
301
    ///   Note: Use of the LOGIN command over an insecure network
302
    ///   (such as the Internet) is a security risk, because anyone
303
    ///   monitoring network traffic can obtain plaintext passwords.
304
    ///   The LOGIN command SHOULD NOT be used except as a last
305
    ///   resort, and it is recommended that client implementations
306
    ///   have a means to disable any automatic use of the LOGIN
307
    ///   command.
308
    ///
309
    ///   Unless either the STARTTLS command has been negotiated or
310
    ///   some other mechanism that protects the session from
311
    ///   password snooping has been provided, a server
312
    ///   implementation MUST implement a configuration in which it
313
    ///   advertises the LOGINDISABLED capability and does NOT permit
314
    ///   the LOGIN command.  Server sites SHOULD NOT use any
315
    ///   configuration which permits the LOGIN command without such
316
    ///   a protection mechanism against password snooping.  A client
317
    ///   implementation MUST NOT send a LOGIN command if the
318
    ///   LOGINDISABLED capability is advertised.
319
    Login {
320
        /// Username.
321
        username: AString<'a>,
322
        /// Password.
323
        password: Secret<AString<'a>>,
324
    },
325

326
    // ----- Authenticated State (https://tools.ietf.org/html/rfc3501#section-6.3) -----
327
    /// ### 6.3.1.  SELECT Command
328
    ///
329
    /// * Arguments:  mailbox name
330
    /// * Responses:
331
    ///   * REQUIRED untagged responses: FLAGS, EXISTS, RECENT
332
    ///   * REQUIRED OK untagged responses: UNSEEN, PERMANENTFLAGS, UIDNEXT, UIDVALIDITY
333
    /// * Result:
334
    ///   * OK - select completed, now in selected state
335
    ///   * NO - select failure, now in authenticated state: no such mailbox, can't access mailbox
336
    ///   * BAD - command unknown or arguments invalid
337
    ///
338
    /// The SELECT command selects a mailbox so that messages in the
339
    /// mailbox can be accessed.  Before returning an OK to the client,
340
    /// the server MUST send the following untagged data to the client.
341
    /// Note that earlier versions of this protocol only required the
342
    /// FLAGS, EXISTS, and RECENT untagged data; consequently, client
343
    /// implementations SHOULD implement default behavior for missing data
344
    /// as discussed with the individual item.
345
    ///
346
    ///   FLAGS       Defined flags in the mailbox.  See the description
347
    ///               of the FLAGS response for more detail.
348
    ///
349
    ///   \<n\> EXISTS  The number of messages in the mailbox.  See the
350
    ///               description of the EXISTS response for more detail.
351
    ///
352
    ///   \<n\> RECENT  The number of messages with the \Recent flag set.
353
    ///               See the description of the RECENT response for more
354
    ///               detail.
355
    ///
356
    ///   OK [UNSEEN \<n\>]
357
    ///               The message sequence number of the first unseen
358
    ///               message in the mailbox.  If this is missing, the
359
    ///               client can not make any assumptions about the first
360
    ///               unseen message in the mailbox, and needs to issue a
361
    ///               SEARCH command if it wants to find it.
362
    ///
363
    ///   OK [PERMANENTFLAGS (\<list of flags\>)]
364
    ///               A list of message flags that the client can change
365
    ///               permanently.  If this is missing, the client should
366
    ///               assume that all flags can be changed permanently.
367
    ///
368
    ///   OK [UIDNEXT \<n\>]
369
    ///               The next unique identifier value.  Refer to section
370
    ///               2.3.1.1 for more information.  If this is missing,
371
    ///               the client can not make any assumptions about the
372
    ///               next unique identifier value.
373
    ///
374
    ///   OK [UIDVALIDITY \<n\>]
375
    ///               The unique identifier validity value.  Refer to
376
    ///               section 2.3.1.1 for more information.  If this is
377
    ///               missing, the server does not support unique
378
    ///               identifiers.
379
    ///
380
    /// Only one mailbox can be selected at a time in a connection;
381
    /// simultaneous access to multiple mailboxes requires multiple
382
    /// connections.  The SELECT command automatically deselects any
383
    /// currently selected mailbox before attempting the new selection.
384
    /// Consequently, if a mailbox is selected and a SELECT command that
385
    /// fails is attempted, no mailbox is selected.
386
    ///
387
    /// If the client is permitted to modify the mailbox, the server
388
    /// SHOULD prefix the text of the tagged OK response with the
389
    /// "[READ-WRITE]" response code.
390
    ///
391
    /// If the client is not permitted to modify the mailbox but is
392
    /// permitted read access, the mailbox is selected as read-only, and
393
    /// the server MUST prefix the text of the tagged OK response to
394
    /// SELECT with the "[READ-ONLY]" response code.  Read-only access
395
    /// through SELECT differs from the EXAMINE command in that certain
396
    /// read-only mailboxes MAY permit the change of permanent state on a
397
    /// per-user (as opposed to global) basis.  Netnews messages marked in
398
    /// a server-based .newsrc file are an example of such per-user
399
    /// permanent state that can be modified with read-only mailboxes.
400
    Select {
401
        /// Mailbox.
402
        mailbox: Mailbox<'a>,
403
        #[cfg(feature = "ext_condstore_qresync")]
404
        parameters: Vec<SelectParameter>,
405
    },
406

407
    /// Unselect a mailbox.
408
    ///
409
    /// This should bring the client back to the AUTHENTICATED state.
410
    ///
411
    /// <div class="warning">
412
    /// This extension must only be used when the server advertised support for it sending the UNSELECT capability.
413
    /// </div>
414
    Unselect,
415

416
    /// 6.3.2.  EXAMINE Command
417
    ///
418
    /// Arguments:  mailbox name
419
    /// Responses:  REQUIRED untagged responses: FLAGS, EXISTS, RECENT
420
    ///             REQUIRED OK untagged responses:  UNSEEN,  PERMANENTFLAGS,
421
    ///             UIDNEXT, UIDVALIDITY
422
    /// Result:     OK - examine completed, now in selected state
423
    ///             NO - examine failure, now in authenticated state: no
424
    ///                  such mailbox, can't access mailbox
425
    ///             BAD - command unknown or arguments invalid
426
    ///
427
    /// The EXAMINE command is identical to SELECT and returns the same
428
    /// output; however, the selected mailbox is identified as read-only.
429
    /// No changes to the permanent state of the mailbox, including
430
    /// per-user state, are permitted; in particular, EXAMINE MUST NOT
431
    /// cause messages to lose the \Recent flag.
432
    ///
433
    /// The text of the tagged OK response to the EXAMINE command MUST
434
    /// begin with the "[READ-ONLY]" response code.
435
    Examine {
436
        /// Mailbox.
437
        mailbox: Mailbox<'a>,
438
        #[cfg(feature = "ext_condstore_qresync")]
439
        parameters: Vec<SelectParameter>,
440
    },
441

442
    /// ### 6.3.3.  CREATE Command
443
    ///
444
    /// * Arguments:  mailbox name
445
    /// * Responses:  no specific responses for this command
446
    /// * Result:
447
    ///   * OK - create completed
448
    ///   * NO - create failure: can't create mailbox with that name
449
    ///   * BAD - command unknown or arguments invalid
450
    ///
451
    /// The CREATE command creates a mailbox with the given name.  An OK
452
    /// response is returned only if a new mailbox with that name has been
453
    /// created.  It is an error to attempt to create INBOX or a mailbox
454
    /// with a name that refers to an extant mailbox.  Any error in
455
    /// creation will return a tagged NO response.
456
    ///
457
    /// If the mailbox name is suffixed with the server's hierarchy
458
    /// separator character (as returned from the server by a LIST
459
    /// command), this is a declaration that the client intends to create
460
    /// mailbox names under this name in the hierarchy.  Server
461
    /// implementations that do not require this declaration MUST ignore
462
    /// the declaration.  In any case, the name created is without the
463
    /// trailing hierarchy delimiter.
464
    ///
465
    /// If the server's hierarchy separator character appears elsewhere in
466
    /// the name, the server SHOULD create any superior hierarchical names
467
    /// that are needed for the CREATE command to be successfully
468
    /// completed.  In other words, an attempt to create "foo/bar/zap" on
469
    /// a server in which "/" is the hierarchy separator character SHOULD
470
    /// create foo/ and foo/bar/ if they do not already exist.
471
    ///
472
    /// If a new mailbox is created with the same name as a mailbox which
473
    /// was deleted, its unique identifiers MUST be greater than any
474
    /// unique identifiers used in the previous incarnation of the mailbox
475
    /// UNLESS the new incarnation has a different unique identifier
476
    /// validity value.  See the description of the UID command for more
477
    /// detail.
478
    ///
479
    ///   Note: The interpretation of this example depends on whether
480
    ///   "/" was returned as the hierarchy separator from LIST.  If
481
    ///   "/" is the hierarchy separator, a new level of hierarchy
482
    ///   named "owatagusiam" with a member called "blurdybloop" is
483
    ///   created.  Otherwise, two mailboxes at the same hierarchy
484
    ///   level are created.
485
    Create {
486
        /// Mailbox.
487
        mailbox: Mailbox<'a>,
488
    },
489

490
    /// 6.3.4.  DELETE Command
491
    ///
492
    /// Arguments:  mailbox name
493
    /// Responses:  no specific responses for this command
494
    /// Result:     OK - delete completed
495
    ///             NO - delete failure: can't delete mailbox with that name
496
    ///             BAD - command unknown or arguments invalid
497
    ///
498
    /// The DELETE command permanently removes the mailbox with the given
499
    /// name.  A tagged OK response is returned only if the mailbox has
500
    /// been deleted.  It is an error to attempt to delete INBOX or a
501
    /// mailbox name that does not exist.
502
    ///
503
    /// The DELETE command MUST NOT remove inferior hierarchical names.
504
    /// For example, if a mailbox "foo" has an inferior "foo.bar"
505
    /// (assuming "." is the hierarchy delimiter character), removing
506
    /// "foo" MUST NOT remove "foo.bar".  It is an error to attempt to
507
    /// delete a name that has inferior hierarchical names and also has
508
    /// the \Noselect mailbox name attribute (see the description of the
509
    /// LIST response for more details).
510
    ///
511
    /// It is permitted to delete a name that has inferior hierarchical
512
    /// names and does not have the \Noselect mailbox name attribute.  In
513
    /// this case, all messages in that mailbox are removed, and the name
514
    /// will acquire the \Noselect mailbox name attribute.
515
    ///
516
    /// The value of the highest-used unique identifier of the deleted
517
    /// mailbox MUST be preserved so that a new mailbox created with the
518
    /// same name will not reuse the identifiers of the former
519
    /// incarnation, UNLESS the new incarnation has a different unique
520
    /// identifier validity value.  See the description of the UID command
521
    /// for more detail.
522
    Delete {
523
        /// Mailbox.
524
        mailbox: Mailbox<'a>,
525
    },
526

527
    /// 6.3.5.  RENAME Command
528
    ///
529
    /// Arguments:  existing mailbox name
530
    ///             new mailbox name
531
    /// Responses:  no specific responses for this command
532
    /// Result:     OK - rename completed
533
    ///             NO - rename failure: can't rename mailbox with that name,
534
    ///                  can't rename to mailbox with that name
535
    ///             BAD - command unknown or arguments invalid
536
    ///
537
    /// The RENAME command changes the name of a mailbox.  A tagged OK
538
    /// response is returned only if the mailbox has been renamed.  It is
539
    /// an error to attempt to rename from a mailbox name that does not
540
    /// exist or to a mailbox name that already exists.  Any error in
541
    /// renaming will return a tagged NO response.
542
    ///
543
    /// If the name has inferior hierarchical names, then the inferior
544
    /// hierarchical names MUST also be renamed.  For example, a rename of
545
    /// "foo" to "zap" will rename "foo/bar" (assuming "/" is the
546
    /// hierarchy delimiter character) to "zap/bar".
547
    ///
548
    /// If the server's hierarchy separator character appears in the name,
549
    /// the server SHOULD create any superior hierarchical names that are
550
    /// needed for the RENAME command to complete successfully.  In other
551
    /// words, an attempt to rename "foo/bar/zap" to baz/rag/zowie on a
552
    /// server in which "/" is the hierarchy separator character SHOULD
553
    /// create baz/ and baz/rag/ if they do not already exist.
554
    ///
555
    /// The value of the highest-used unique identifier of the old mailbox
556
    /// name MUST be preserved so that a new mailbox created with the same
557
    /// name will not reuse the identifiers of the former incarnation,
558
    /// UNLESS the new incarnation has a different unique identifier
559
    /// validity value.  See the description of the UID command for more
560
    /// detail.
561
    ///
562
    /// Renaming INBOX is permitted, and has special behavior.  It moves
563
    /// all messages in INBOX to a new mailbox with the given name,
564
    /// leaving INBOX empty.  If the server implementation supports
565
    /// inferior hierarchical names of INBOX, these are unaffected by a
566
    /// rename of INBOX.
567
    Rename {
568
        /// Current name.
569
        from: Mailbox<'a>,
570
        /// New name.
571
        to: Mailbox<'a>,
572
    },
573

574
    /// ### 6.3.6.  SUBSCRIBE Command
575
    ///
576
    /// * Arguments:  mailbox
577
    /// * Responses:  no specific responses for this command
578
    /// * Result:
579
    ///   * OK - subscribe completed
580
    ///   * NO - subscribe failure: can't subscribe to that name
581
    ///   * BAD - command unknown or arguments invalid
582
    ///
583
    /// The SUBSCRIBE command adds the specified mailbox name to the
584
    /// server's set of "active" or "subscribed" mailboxes as returned by
585
    /// the LSUB command.  This command returns a tagged OK response only
586
    /// if the subscription is successful.
587
    ///
588
    /// A server MAY validate the mailbox argument to SUBSCRIBE to verify
589
    /// that it exists.  However, it MUST NOT unilaterally remove an
590
    /// existing mailbox name from the subscription list even if a mailbox
591
    /// by that name no longer exists.
592
    ///
593
    ///   Note: This requirement is because a server site can
594
    ///   choose to routinely remove a mailbox with a well-known
595
    ///   name (e.g., "system-alerts") after its contents expire,
596
    ///   with the intention of recreating it when new contents
597
    ///   are appropriate.
598
    Subscribe {
599
        /// Mailbox.
600
        mailbox: Mailbox<'a>,
601
    },
602

603
    /// 6.3.7.  UNSUBSCRIBE Command
604
    ///
605
    /// Arguments:  mailbox name
606
    /// Responses:  no specific responses for this command
607
    /// Result:     OK - unsubscribe completed
608
    ///             NO - unsubscribe failure: can't unsubscribe that name
609
    ///             BAD - command unknown or arguments invalid
610
    ///
611
    /// The UNSUBSCRIBE command removes the specified mailbox name from
612
    /// the server's set of "active" or "subscribed" mailboxes as returned
613
    /// by the LSUB command.  This command returns a tagged OK response
614
    /// only if the unsubscription is successful.
615
    Unsubscribe {
616
        /// Mailbox.
617
        mailbox: Mailbox<'a>,
618
    },
619

620
    /// ### 6.3.8.  LIST Command
621
    ///
622
    /// * Arguments:
623
    ///   * reference name
624
    ///   * mailbox name with possible wildcards
625
    /// * Responses:  untagged responses: LIST
626
    /// * Result:
627
    ///   * OK - list completed
628
    ///   * NO - list failure: can't list that reference or name
629
    ///   * BAD - command unknown or arguments invalid
630
    ///
631
    /// The LIST command returns a subset of names from the complete set
632
    /// of all names available to the client.  Zero or more untagged LIST
633
    /// replies are returned, containing the name attributes, hierarchy
634
    /// delimiter, and name; see the description of the LIST reply for
635
    /// more detail.
636
    ///
637
    /// The LIST command SHOULD return its data quickly, without undue
638
    /// delay.  For example, it SHOULD NOT go to excess trouble to
639
    /// calculate the \Marked or \Unmarked status or perform other
640
    /// processing; if each name requires 1 second of processing, then a
641
    /// list of 1200 names would take 20 minutes!
642
    ///
643
    /// An empty ("" string) reference name argument indicates that the
644
    /// mailbox name is interpreted as by SELECT.  The returned mailbox
645
    /// names MUST match the supplied mailbox name pattern.  A non-empty
646
    /// reference name argument is the name of a mailbox or a level of
647
    /// mailbox hierarchy, and indicates the context in which the mailbox
648
    /// name is interpreted.
649
    ///
650
    /// An empty ("" string) mailbox name argument is a special request to
651
    /// return the hierarchy delimiter and the root name of the name given
652
    /// in the reference.  The value returned as the root MAY be the empty
653
    /// string if the reference is non-rooted or is an empty string.  In
654
    /// all cases, a hierarchy delimiter (or NIL if there is no hierarchy)
655
    /// is returned.  This permits a client to get the hierarchy delimiter
656
    /// (or find out that the mailbox names are flat) even when no
657
    /// mailboxes by that name currently exist.
658
    ///
659
    /// The reference and mailbox name arguments are interpreted into a
660
    /// canonical form that represents an unambiguous left-to-right
661
    /// hierarchy.  The returned mailbox names will be in the interpreted
662
    /// form.
663
    ///
664
    ///   Note: The interpretation of the reference argument is
665
    ///   implementation-defined.  It depends upon whether the
666
    ///   server implementation has a concept of the "current
667
    ///   working directory" and leading "break out characters",
668
    ///   which override the current working directory.
669
    ///
670
    ///   For example, on a server which exports a UNIX or NT
671
    ///   filesystem, the reference argument contains the current
672
    ///   working directory, and the mailbox name argument would
673
    ///   contain the name as interpreted in the current working
674
    ///   directory.
675
    ///
676
    /// If a server implementation has no concept of break out
677
    /// characters, the canonical form is normally the reference
678
    /// name appended with the mailbox name.  Note that if the
679
    /// server implements the namespace convention (section
680
    /// 5.1.2), "#" is a break out character and must be treated
681
    /// as such.
682
    ///
683
    /// If the reference argument is not a level of mailbox
684
    /// hierarchy (that is, it is a \NoInferiors name), and/or
685
    /// the reference argument does not end with the hierarchy
686
    /// delimiter, it is implementation-dependent how this is
687
    /// interpreted.  For example, a reference of "foo/bar" and
688
    /// mailbox name of "rag/baz" could be interpreted as
689
    /// "foo/bar/rag/baz", "foo/barrag/baz", or "foo/rag/baz".
690
    /// A client SHOULD NOT use such a reference argument except
691
    /// at the explicit request of the user.  A hierarchical
692
    /// browser MUST NOT make any assumptions about server
693
    /// interpretation of the reference unless the reference is
694
    /// a level of mailbox hierarchy AND ends with the hierarchy
695
    /// delimiter.
696
    ///
697
    /// Any part of the reference argument that is included in the
698
    /// interpreted form SHOULD prefix the interpreted form.  It SHOULD
699
    /// also be in the same form as the reference name argument.  This
700
    /// rule permits the client to determine if the returned mailbox name
701
    /// is in the context of the reference argument, or if something about
702
    /// the mailbox argument overrode the reference argument.  Without
703
    /// this rule, the client would have to have knowledge of the server's
704
    /// naming semantics including what characters are "breakouts" that
705
    /// override a naming context.
706
    ///
707
    ///   For example, here are some examples of how references
708
    ///   and mailbox names might be interpreted on a UNIX-based
709
    ///   server:
710
    ///
711
    /// ```text
712
    /// Reference     Mailbox Name  Interpretation
713
    /// ------------  ------------  --------------
714
    /// ~smith/Mail/  foo.*         ~smith/Mail/foo.*
715
    /// archive/      %             archive/%
716
    /// #news.        comp.mail.*   #news.comp.mail.*
717
    /// ~smith/Mail/  /usr/doc/foo  /usr/doc/foo
718
    /// archive/      ~fred/Mail/*  ~fred/Mail/*
719
    /// ```
720
    ///
721
    ///   The first three examples demonstrate interpretations in
722
    ///   the context of the reference argument.  Note that
723
    ///   "~smith/Mail" SHOULD NOT be transformed into something
724
    ///   like "/u2/users/smith/Mail", or it would be impossible
725
    ///   for the client to determine that the interpretation was
726
    ///   in the context of the reference.
727
    ///
728
    /// The character "*" is a wildcard, and matches zero or more
729
    /// characters at this position.  The character "%" is similar to "*",
730
    /// but it does not match a hierarchy delimiter.  If the "%" wildcard
731
    /// is the last character of a mailbox name argument, matching levels
732
    /// of hierarchy are also returned.  If these levels of hierarchy are
733
    /// not also selectable mailboxes, they are returned with the
734
    /// \Noselect mailbox name attribute (see the description of the LIST
735
    /// response for more details).
736
    ///
737
    /// Server implementations are permitted to "hide" otherwise
738
    /// accessible mailboxes from the wildcard characters, by preventing
739
    /// certain characters or names from matching a wildcard in certain
740
    /// situations.  For example, a UNIX-based server might restrict the
741
    /// interpretation of "*" so that an initial "/" character does not
742
    /// match.
743
    ///
744
    /// The special name INBOX is included in the output from LIST, if
745
    /// INBOX is supported by this server for this user and if the
746
    /// uppercase string "INBOX" matches the interpreted reference and
747
    /// mailbox name arguments with wildcards as described above.  The
748
    /// criteria for omitting INBOX is whether SELECT INBOX will return
749
    /// failure; it is not relevant whether the user's real INBOX resides
750
    /// on this or some other server.
751
    List {
752
        /// Reference.
753
        reference: Mailbox<'a>,
754
        /// Mailbox (wildcard).
755
        mailbox_wildcard: ListMailbox<'a>,
756
    },
757

758
    /// ### 6.3.9.  LSUB Command
759
    ///
760
    /// * Arguments:
761
    ///   * reference name
762
    ///   * mailbox name with possible wildcards
763
    /// * Responses:  untagged responses: LSUB
764
    /// * Result:
765
    ///   * OK - lsub completed
766
    ///   * NO - lsub failure: can't list that reference or name
767
    ///   * BAD - command unknown or arguments invalid
768
    ///
769
    /// The LSUB command returns a subset of names from the set of names
770
    /// that the user has declared as being "active" or "subscribed".
771
    /// Zero or more untagged LSUB replies are returned.  The arguments to
772
    /// LSUB are in the same form as those for LIST.
773
    ///
774
    /// The returned untagged LSUB response MAY contain different mailbox
775
    /// flags from a LIST untagged response.  If this should happen, the
776
    /// flags in the untagged LIST are considered more authoritative.
777
    ///
778
    /// A special situation occurs when using LSUB with the % wildcard.
779
    /// Consider what happens if "foo/bar" (with a hierarchy delimiter of
780
    /// "/") is subscribed but "foo" is not.  A "%" wildcard to LSUB must
781
    /// return foo, not foo/bar, in the LSUB response, and it MUST be
782
    /// flagged with the \Noselect attribute.
783
    ///
784
    /// The server MUST NOT unilaterally remove an existing mailbox name
785
    /// from the subscription list even if a mailbox by that name no
786
    /// longer exists.
787
    Lsub {
788
        /// Reference.
789
        reference: Mailbox<'a>,
790
        /// Mailbox (wildcard).
791
        mailbox_wildcard: ListMailbox<'a>,
792
    },
793

794
    /// ### 6.3.10. STATUS Command
795
    ///
796
    /// * Arguments:
797
    ///   * mailbox name
798
    ///   * status data item names
799
    /// * Responses:  untagged responses: STATUS
800
    /// * Result:
801
    ///   * OK - status completed
802
    ///   * NO - status failure: no status for that name
803
    ///   * BAD - command unknown or arguments invalid
804
    ///
805
    /// The STATUS command requests the status of the indicated mailbox.
806
    /// It does not change the currently selected mailbox, nor does it
807
    /// affect the state of any messages in the queried mailbox (in
808
    /// particular, STATUS MUST NOT cause messages to lose the \Recent
809
    /// flag).
810
    ///
811
    /// The STATUS command provides an alternative to opening a second
812
    /// IMAP4rev1 connection and doing an EXAMINE command on a mailbox to
813
    /// query that mailbox's status without deselecting the current
814
    /// mailbox in the first IMAP4rev1 connection.
815
    ///
816
    /// Unlike the LIST command, the STATUS command is not guaranteed to
817
    /// be fast in its response.  Under certain circumstances, it can be
818
    /// quite slow.  In some implementations, the server is obliged to
819
    /// open the mailbox read-only internally to obtain certain status
820
    /// information.  Also unlike the LIST command, the STATUS command
821
    /// does not accept wildcards.
822
    ///
823
    ///   Note: The STATUS command is intended to access the
824
    ///   status of mailboxes other than the currently selected
825
    ///   mailbox.  Because the STATUS command can cause the
826
    ///   mailbox to be opened internally, and because this
827
    ///   information is available by other means on the selected
828
    ///   mailbox, the STATUS command SHOULD NOT be used on the
829
    ///   currently selected mailbox.
830
    ///
831
    ///   The STATUS command MUST NOT be used as a "check for new
832
    ///   messages in the selected mailbox" operation (refer to
833
    ///   sections 7, 7.3.1, and 7.3.2 for more information about
834
    ///   the proper method for new message checking).
835
    ///
836
    ///   Because the STATUS command is not guaranteed to be fast
837
    ///   in its results, clients SHOULD NOT expect to be able to
838
    ///   issue many consecutive STATUS commands and obtain
839
    ///   reasonable performance.
840
    Status {
841
        /// Mailbox.
842
        mailbox: Mailbox<'a>,
843
        /// Status data items.
844
        item_names: Cow<'a, [StatusDataItemName]>,
845
    },
846

847
    /// 6.3.11. APPEND Command
848
    ///
849
    /// Arguments:  mailbox name
850
    ///             OPTIONAL flag parenthesized list
851
    ///             OPTIONAL date/time string
852
    ///             message literal
853
    /// Responses:  no specific responses for this command
854
    /// Result:     OK - append completed
855
    ///             NO - append error: can't append to that mailbox, error
856
    ///                  in flags or date/time or message text
857
    ///             BAD - command unknown or arguments invalid
858
    ///
859
    /// The APPEND command appends the literal argument as a new message
860
    /// to the end of the specified destination mailbox.  This argument
861
    /// SHOULD be in the format of an [RFC-2822] message.  8-bit
862
    /// characters are permitted in the message.  A server implementation
863
    /// that is unable to preserve 8-bit data properly MUST be able to
864
    /// reversibly convert 8-bit APPEND data to 7-bit using a [MIME-IMB]
865
    /// content transfer encoding.
866
    ///
867
    ///   Note: There MAY be exceptions, e.g., draft messages, in
868
    ///   which required [RFC-2822] header lines are omitted in
869
    ///   the message literal argument to APPEND.  The full
870
    ///   implications of doing so MUST be understood and
871
    ///   carefully weighed.
872
    ///
873
    /// If a flag parenthesized list is specified, the flags SHOULD be set
874
    /// in the resulting message; otherwise, the flag list of the
875
    /// resulting message is set to empty by default.  In either case, the
876
    /// Recent flag is also set.
877
    ///
878
    /// If a date-time is specified, the internal date SHOULD be set in
879
    /// the resulting message; otherwise, the internal date of the
880
    /// resulting message is set to the current date and time by default.
881
    ///
882
    /// If the append is unsuccessful for any reason, the mailbox MUST be
883
    /// restored to its state before the APPEND attempt; no partial
884
    /// appending is permitted.
885
    ///
886
    /// If the destination mailbox does not exist, a server MUST return an
887
    /// error, and MUST NOT automatically create the mailbox.  Unless it
888
    /// is certain that the destination mailbox can not be created, the
889
    /// server MUST send the response code "\[TRYCREATE\]" as the prefix of
890
    /// the text of the tagged NO response.  This gives a hint to the
891
    /// client that it can attempt a CREATE command and retry the APPEND
892
    /// if the CREATE is successful.
893
    ///
894
    /// If the mailbox is currently selected, the normal new message
895
    /// actions SHOULD occur.  Specifically, the server SHOULD notify the
896
    /// client immediately via an untagged EXISTS response.  If the server
897
    /// does not do so, the client MAY issue a NOOP command (or failing
898
    /// that, a CHECK command) after one or more APPEND commands.
899
    ///
900
    ///   Note: The APPEND command is not used for message delivery,
901
    ///   because it does not provide a mechanism to transfer \[SMTP\]
902
    ///   envelope information.
903
    Append {
904
        /// Mailbox.
905
        mailbox: Mailbox<'a>,
906
        /// Flags.
907
        flags: Vec<Flag<'a>>,
908
        /// Datetime.
909
        date: Option<DateTime>,
910
        /// Message to append.
911
        ///
912
        /// <div class="warning">
913
        /// Use [`LiteralOrLiteral8::Literal8`] only when the server advertised [`Capability::Binary`](crate::response::Capability::Binary).
914
        /// </div>
915
        message: LiteralOrLiteral8<'a>,
916
    },
917

918
    // ----- Selected State (https://tools.ietf.org/html/rfc3501#section-6.4) -----
919
    /// ### 6.4.1.  CHECK Command
920
    ///
921
    /// * Arguments:  none
922
    /// * Responses:  no specific responses for this command
923
    /// * Result:
924
    ///   * OK - check completed
925
    ///   * BAD - command unknown or arguments invalid
926
    ///
927
    /// The CHECK command requests a checkpoint of the currently selected
928
    /// mailbox.  A checkpoint refers to any implementation-dependent
929
    /// housekeeping associated with the mailbox (e.g., resolving the
930
    /// server's in-memory state of the mailbox with the state on its
931
    /// disk) that is not normally executed as part of each command.  A
932
    /// checkpoint MAY take a non-instantaneous amount of real time to
933
    /// complete.  If a server implementation has no such housekeeping
934
    /// considerations, CHECK is equivalent to NOOP.
935
    ///
936
    /// There is no guarantee that an EXISTS untagged response will happen
937
    /// as a result of CHECK.  NOOP, not CHECK, SHOULD be used for new
938
    /// message polling.
939
    Check,
940

941
    /// ### 6.4.2.  CLOSE Command
942
    ///
943
    ///    * Arguments:  none
944
    ///    * Responses:  no specific responses for this command
945
    ///    * Result:
946
    ///      * OK - close completed, now in authenticated state
947
    ///      * BAD - command unknown or arguments invalid
948
    ///
949
    ///       The CLOSE command permanently removes all messages that have the
950
    ///       \Deleted flag set from the currently selected mailbox, and returns
951
    ///       to the authenticated state from the selected state.  No untagged
952
    ///       EXPUNGE responses are sent.
953
    ///
954
    ///       No messages are removed, and no error is given, if the mailbox is
955
    ///       selected by an EXAMINE command or is otherwise selected read-only.
956
    ///
957
    ///       Even if a mailbox is selected, a SELECT, EXAMINE, or LOGOUT
958
    ///       command MAY be issued without previously issuing a CLOSE command.
959
    ///       The SELECT, EXAMINE, and LOGOUT commands implicitly close the
960
    ///       currently selected mailbox without doing an expunge.  However,
961
    ///       when many messages are deleted, a CLOSE-LOGOUT or CLOSE-SELECT
962
    ///       sequence is considerably faster than an EXPUNGE-LOGOUT or
963
    ///       EXPUNGE-SELECT because no untagged EXPUNGE responses (which the
964
    ///       client would probably ignore) are sent.
965
    Close,
966

967
    /// 6.4.3.  EXPUNGE Command
968
    ///
969
    /// Arguments: none
970
    /// Responses: untagged responses: EXPUNGE
971
    /// Result:    OK - expunge completed
972
    ///            NO - expunge failure: can't expunge (e.g., permission denied)
973
    ///            BAD - command unknown or arguments invalid
974
    ///
975
    /// The EXPUNGE command permanently removes all messages that have the
976
    /// \Deleted flag set from the currently selected mailbox.  Before
977
    /// returning an OK to the client, an untagged EXPUNGE response is
978
    /// sent for each message that is removed.
979
    Expunge,
980

981
    /// 2.1.  UID EXPUNGE Command (RFC 4315)
982
    ///
983
    /// Arguments: sequence set
984
    /// Data:      untagged responses: EXPUNGE
985
    /// Result:    OK - expunge completed
986
    ///            NO - expunge failure (e.g., permission denied)
987
    ///            BAD - command unknown or arguments invalid
988
    ///
989
    /// The UID EXPUNGE command permanently removes all messages that both
990
    /// have the \Deleted flag set and have a UID that is included in the
991
    /// specified sequence set from the currently selected mailbox.  If a
992
    /// message either does not have the \Deleted flag set or has a UID
993
    /// that is not included in the specified sequence set, it is not
994
    /// affected.
995
    ///
996
    /// This command is particularly useful for disconnected use clients.
997
    /// By using UID EXPUNGE instead of EXPUNGE when resynchronizing with
998
    /// the server, the client can ensure that it does not inadvertantly
999
    /// remove any messages that have been marked as \Deleted by other
1000
    /// clients between the time that the client was last connected and
1001
    /// the time the client resynchronizes.
1002
    ///
1003
    /// If the server does not support the UIDPLUS capability, the client
1004
    /// should fall back to using the STORE command to temporarily remove
1005
    /// the \Deleted flag from messages it does not want to remove, then
1006
    /// issuing the EXPUNGE command.  Finally, the client should use the
1007
    /// STORE command to restore the \Deleted flag on the messages in
1008
    /// which it was temporarily removed.
1009
    ///
1010
    /// Alternatively, the client may fall back to using just the EXPUNGE
1011
    /// command, risking the unintended removal of some messages.
1012
    ExpungeUid { sequence_set: SequenceSet },
1013

1014
    /// ### 6.4.4.  SEARCH Command
1015
    ///
1016
    /// * Arguments:
1017
    ///   * OPTIONAL \[CHARSET\] specification
1018
    ///   * searching criteria (one or more)
1019
    /// * Responses:  REQUIRED untagged response: SEARCH
1020
    /// * Result:
1021
    ///   * OK - search completed
1022
    ///   * NO - search error: can't search that \[CHARSET\] or criteria
1023
    ///   * BAD - command unknown or arguments invalid
1024
    ///
1025
    /// The SEARCH command searches the mailbox for messages that match
1026
    /// the given searching criteria.  Searching criteria consist of one
1027
    /// or more search keys.  The untagged SEARCH response from the server
1028
    /// contains a listing of message sequence numbers corresponding to
1029
    /// those messages that match the searching criteria.
1030
    ///
1031
    /// When multiple keys are specified, the result is the intersection
1032
    /// (AND function) of all the messages that match those keys.  For
1033
    /// example, the criteria DELETED FROM "SMITH" SINCE 1-Feb-1994 refers
1034
    /// to all deleted messages from Smith that were placed in the mailbox
1035
    /// since February 1, 1994.  A search key can also be a parenthesized
1036
    /// list of one or more search keys (e.g., for use with the OR and NOT
1037
    /// keys).
1038
    ///
1039
    /// Server implementations MAY exclude [MIME-IMB] body parts with
1040
    /// terminal content media types other than TEXT and MESSAGE from
1041
    /// consideration in SEARCH matching.
1042
    ///
1043
    /// The OPTIONAL \[CHARSET\] specification consists of the word
1044
    /// "CHARSET" followed by a registered \[CHARSET\].  It indicates the
1045
    /// \[CHARSET\] of the strings that appear in the search criteria.
1046
    /// [MIME-IMB] content transfer encodings, and [MIME-HDRS] strings in
1047
    /// [RFC-2822]/[MIME-IMB] headers, MUST be decoded before comparing
1048
    /// text in a \[CHARSET\] other than US-ASCII.  US-ASCII MUST be
1049
    /// supported; other \[CHARSET\]s MAY be supported.
1050
    ///
1051
    /// If the server does not support the specified \[CHARSET\], it MUST
1052
    /// return a tagged NO response (not a BAD).  This response SHOULD
1053
    /// contain the BADCHARSET response code, which MAY list the
1054
    /// \[CHARSET\]s supported by the server.
1055
    ///
1056
    /// In all search keys that use strings, a message matches the key if
1057
    /// the string is a substring of the field.  The matching is
1058
    /// case-insensitive.
1059
    ///
1060
    /// See [SearchKey] enum.
1061
    ///
1062
    /// Note: Since this document is restricted to 7-bit ASCII
1063
    /// text, it is not possible to show actual UTF-8 data.  The
1064
    /// "XXXXXX" is a placeholder for what would be 6 octets of
1065
    /// 8-bit data in an actual transaction.
1066
    Search {
1067
        /// Charset.
1068
        charset: Option<Charset<'a>>,
1069
        /// Criteria.
1070
        criteria: Vec1<SearchKey<'a>>,
1071
        /// Use UID variant.
1072
        uid: bool,
1073
    },
1074

1075
    /// SORT command.
1076
    ///
1077
    /// The SORT command is a variant of SEARCH with sorting semantics for the results.
1078
    ///
1079
    /// Data:
1080
    /// * untagged responses: SORT
1081
    ///
1082
    /// Result:
1083
    /// * OK - sort completed
1084
    /// * NO - sort error: can't sort that charset or criteria
1085
    /// * BAD - command unknown or arguments invalid
1086
    ///
1087
    /// <div class="warning">
1088
    /// This extension must only be used when the server advertised support for it sending the SORT capability.
1089
    /// </div>
1090
    Sort {
1091
        /// Sort criteria.
1092
        sort_criteria: Vec1<SortCriterion>,
1093
        /// Charset.
1094
        charset: Charset<'a>,
1095
        /// Search criteria.
1096
        search_criteria: Vec1<SearchKey<'a>>,
1097
        /// Use UID variant.
1098
        uid: bool,
1099
    },
1100

1101
    /// THREAD command.
1102
    ///
1103
    /// The THREAD command is a variant of SEARCH with threading semantics for the results.
1104
    ///
1105
    /// Data:
1106
    /// * untagged responses: THREAD
1107
    ///
1108
    /// Result:
1109
    /// * OK - thread completed
1110
    /// * NO - thread error: can't thread that charset or criteria
1111
    /// * BAD - command unknown or arguments invalid
1112
    ///
1113
    /// <div class="warning">
1114
    /// This extension must only be used when the server advertised support for it sending the THREAD capability.
1115
    /// </div>
1116
    Thread {
1117
        /// Threading algorithm.
1118
        algorithm: ThreadingAlgorithm<'a>,
1119
        /// Charset.
1120
        charset: Charset<'a>,
1121
        /// Search criteria.
1122
        search_criteria: Vec1<SearchKey<'a>>,
1123
        /// Use UID variant.
1124
        uid: bool,
1125
    },
1126

1127
    /// ### 6.4.5.  FETCH Command
1128
    ///
1129
    /// * Arguments:
1130
    ///   * sequence set
1131
    ///   * message data item names or macro
1132
    /// * Responses:  untagged responses: FETCH
1133
    /// * Result:
1134
    ///   * OK - fetch completed
1135
    ///   * NO - fetch error: can't fetch that data
1136
    ///   * BAD - command unknown or arguments invalid
1137
    ///
1138
    /// The FETCH command retrieves data associated with a message in the
1139
    /// mailbox.  The data items to be fetched can be either a single atom
1140
    /// or a parenthesized list.
1141
    ///
1142
    /// Most data items, identified in the formal syntax under the
1143
    /// msg-att-static rule, are static and MUST NOT change for any
1144
    /// particular message.  Other data items, identified in the formal
1145
    /// syntax under the msg-att-dynamic rule, MAY change, either as a
1146
    /// result of a STORE command or due to external events.
1147
    ///
1148
    ///   For example, if a client receives an ENVELOPE for a
1149
    ///   message when it already knows the envelope, it can
1150
    ///   safely ignore the newly transmitted envelope.
1151
    Fetch {
1152
        /// Set of messages.
1153
        sequence_set: SequenceSet,
1154
        /// Message data items (or a macro).
1155
        macro_or_item_names: MacroOrMessageDataItemNames<'a>,
1156
        /// Use UID variant.
1157
        uid: bool,
1158
        #[cfg(feature = "ext_condstore_qresync")]
1159
        modifiers: Vec<FetchModifier>,
1160
    },
1161

1162
    /// ### 6.4.6.  STORE Command
1163
    ///
1164
    /// * Arguments:
1165
    ///   * sequence set
1166
    ///   * message data item name
1167
    ///   * value for message data item
1168
    /// * Responses:  untagged responses: FETCH
1169
    /// * Result:
1170
    ///   * OK - store completed
1171
    ///   * NO - store error: can't store that data
1172
    ///   * BAD - command unknown or arguments invalid
1173
    ///
1174
    /// The STORE command alters data associated with a message in the
1175
    /// mailbox.  Normally, STORE will return the updated value of the
1176
    /// data with an untagged FETCH response.  A suffix of ".SILENT" in
1177
    /// the data item name prevents the untagged FETCH, and the server
1178
    /// SHOULD assume that the client has determined the updated value
1179
    /// itself or does not care about the updated value.
1180
    ///
1181
    ///   Note: Regardless of whether or not the ".SILENT" suffix
1182
    ///   was used, the server SHOULD send an untagged FETCH
1183
    ///   response if a change to a message's flags from an
1184
    ///   external source is observed.  The intent is that the
1185
    ///   status of the flags is determinate without a race
1186
    ///   condition.
1187
    ///
1188
    /// The currently defined data items that can be stored are:
1189
    ///
1190
    /// FLAGS \<flag list\>
1191
    ///    Replace the flags for the message (other than \Recent) with the
1192
    ///    argument.  The new value of the flags is returned as if a FETCH
1193
    ///    of those flags was done.
1194
    ///
1195
    /// FLAGS.SILENT \<flag list\>
1196
    ///    Equivalent to FLAGS, but without returning a new value.
1197
    ///
1198
    /// +FLAGS \<flag list\>
1199
    ///    Add the argument to the flags for the message.  The new value
1200
    ///    of the flags is returned as if a FETCH of those flags was done.
1201
    ///
1202
    /// +FLAGS.SILENT \<flag list\>
1203
    ///    Equivalent to +FLAGS, but without returning a new value.
1204
    ///
1205
    /// -FLAGS \<flag list\>
1206
    ///    Remove the argument from the flags for the message.  The new
1207
    ///    value of the flags is returned as if a FETCH of those flags was
1208
    ///    done.
1209
    ///
1210
    /// -FLAGS.SILENT \<flag list\>
1211
    ///    Equivalent to -FLAGS, but without returning a new value.
1212
    Store {
1213
        /// Set of messages.
1214
        sequence_set: SequenceSet,
1215
        /// Kind of storage, i.e., replace, add, or remove.
1216
        kind: StoreType,
1217
        /// Kind of response, i.e., answer or silent.
1218
        response: StoreResponse,
1219
        /// Flags.
1220
        flags: Vec<Flag<'a>>, // FIXME(misuse): must not accept "\*" or "\Recent"
1221
        /// Use UID variant.
1222
        uid: bool,
1223
        /// --- Modifiers ---
1224
        #[cfg(feature = "ext_condstore_qresync")]
1225
        modifiers: Vec<StoreModifier>,
1226
    },
1227

1228
    /// 6.4.7.  COPY Command
1229
    ///
1230
    /// Arguments:  sequence set
1231
    ///             mailbox name
1232
    /// Responses:  no specific responses for this command
1233
    /// Result:     OK - copy completed
1234
    ///             NO - copy error: can't copy those messages or to that
1235
    ///                  name
1236
    ///             BAD - command unknown or arguments invalid
1237
    ///
1238
    /// The COPY command copies the specified message(s) to the end of the
1239
    /// specified destination mailbox.  The flags and internal date of the
1240
    /// message(s) SHOULD be preserved, and the Recent flag SHOULD be set,
1241
    /// in the copy.
1242
    ///
1243
    /// If the destination mailbox does not exist, a server SHOULD return
1244
    /// an error.  It SHOULD NOT automatically create the mailbox.  Unless
1245
    /// it is certain that the destination mailbox can not be created, the
1246
    /// server MUST send the response code "\[TRYCREATE\]" as the prefix of
1247
    /// the text of the tagged NO response.  This gives a hint to the
1248
    /// client that it can attempt a CREATE command and retry the COPY if
1249
    /// the CREATE is successful.
1250
    ///
1251
    /// If the COPY command is unsuccessful for any reason, server
1252
    /// implementations MUST restore the destination mailbox to its state
1253
    /// before the COPY attempt.
1254
    Copy {
1255
        /// Set of messages.
1256
        sequence_set: SequenceSet,
1257
        /// Destination mailbox.
1258
        mailbox: Mailbox<'a>,
1259
        /// Use UID variant.
1260
        uid: bool,
1261
    },
1262

1263
    // The UID mechanism was inlined into copy, fetch, store, and search.
1264
    // as an additional parameter.
1265
    //
1266
    // ### 6.4.8.  UID Command
1267
    //
1268
    // * Arguments:
1269
    //   * command name
1270
    //   * command arguments
1271
    // * Responses:  untagged responses: FETCH, SEARCH
1272
    // * Result:
1273
    //   * OK - UID command completed
1274
    //   * NO - UID command error
1275
    //   * BAD - command unknown or arguments invalid
1276
    //
1277
    // The UID command has two forms.  In the first form, it takes as its
1278
    // arguments a COPY, FETCH, or STORE command with arguments
1279
    // appropriate for the associated command.  However, the numbers in
1280
    // the sequence set argument are unique identifiers instead of
1281
    // message sequence numbers.  Sequence set ranges are permitted, but
1282
    // there is no guarantee that unique identifiers will be contiguous.
1283
    //
1284
    // A non-existent unique identifier is ignored without any error
1285
    // message generated.  Thus, it is possible for a UID FETCH command
1286
    // to return an OK without any data or a UID COPY or UID STORE to
1287
    // return an OK without performing any operations.
1288
    //
1289
    // In the second form, the UID command takes a SEARCH command with
1290
    // SEARCH command arguments.  The interpretation of the arguments is
1291
    // the same as with SEARCH; however, the numbers returned in a SEARCH
1292
    // response for a UID SEARCH command are unique identifiers instead
1293
    // of message sequence numbers.  For example, the command UID SEARCH
1294
    // 1:100 UID 443:557 returns the unique identifiers corresponding to
1295
    // the intersection of two sequence sets, the message sequence number
1296
    // range 1:100 and the UID range 443:557.
1297
    //
1298
    //   Note: in the above example, the UID range 443:557
1299
    //   appears.  The same comment about a non-existent unique
1300
    //   identifier being ignored without any error message also
1301
    //   applies here.  Hence, even if neither UID 443 or 557
1302
    //   exist, this range is valid and would include an existing
1303
    //   UID 495.
1304
    //
1305
    //   Also note that a UID range of 559:* always includes the
1306
    //   UID of the last message in the mailbox, even if 559 is
1307
    //   higher than any assigned UID value.  This is because the
1308
    //   contents of a range are independent of the order of the
1309
    //   range endpoints.  Thus, any UID range with * as one of
1310
    //   the endpoints indicates at least one message (the
1311
    //   message with the highest numbered UID), unless the
1312
    //   mailbox is empty.
1313
    //
1314
    //   The number after the "*" in an untagged FETCH response is always a
1315
    //   message sequence number, not a unique identifier, even for a UID
1316
    //   command response.  However, server implementations MUST implicitly
1317
    //   include the UID message data item as part of any FETCH response
1318
    //   caused by a UID command, regardless of whether a UID was specified
1319
    //   as a message data item to the FETCH.
1320
    //
1321
    //   Note: The rule about including the UID message data item as part
1322
    //   of a FETCH response primarily applies to the UID FETCH and UID
1323
    //   STORE commands, including a UID FETCH command that does not
1324
    //   include UID as a message data item.  Although it is unlikely that
1325
    //   the other UID commands will cause an untagged FETCH, this rule
1326
    //   applies to these commands as well.
1327

1328
    // ----- Experimental/Expansion (https://tools.ietf.org/html/rfc3501#section-6.5) -----
1329

1330
    // ### 6.5.1.  X<atom> Command
1331
    //
1332
    // * Arguments:  implementation defined
1333
    // * Responses:  implementation defined
1334
    // * Result:
1335
    //   * OK - command completed
1336
    //   * NO - failure
1337
    //   * BAD - command unknown or arguments invalid
1338
    //
1339
    // Any command prefixed with an X is an experimental command.
1340
    // Commands which are not part of this specification, a standard or
1341
    // standards-track revision of this specification, or an
1342
    // IESG-approved experimental protocol, MUST use the X prefix.
1343
    //
1344
    // Any added untagged responses issued by an experimental command
1345
    // MUST also be prefixed with an X.  Server implementations MUST NOT
1346
    // send any such untagged responses, unless the client requested it
1347
    // by issuing the associated experimental command.
1348
    //X,
1349
    /// IDLE command.
1350
    ///
1351
    /// <div class="warning">
1352
    /// This extension must only be used when the server advertised support for it sending the IDLE capability.
1353
    /// </div>
1354
    Idle,
1355

1356
    /// ENABLE command.
1357
    ///
1358
    /// <div class="warning">
1359
    /// This extension must only be used when the server advertised support for it sending the ENABLE capability.
1360
    /// </div>
1361
    Enable {
1362
        /// Capabilities to enable.
1363
        capabilities: Vec1<CapabilityEnable<'a>>,
1364
    },
1365

1366
    /// COMPRESS command.
1367
    ///
1368
    /// <div class="warning">
1369
    /// This extension must only be used when the server advertised support for it sending the COMPRESS capability.
1370
    /// </div>
1371
    Compress {
1372
        /// Compression algorithm.
1373
        algorithm: CompressionAlgorithm,
1374
    },
1375

1376
    /// Takes the name of a quota root and returns the quota root's resource usage and limits in an untagged QUOTA response.
1377
    ///
1378
    /// Arguments:
1379
    /// * quota root
1380
    ///
1381
    /// Responses:
1382
    /// * REQUIRED untagged responses: QUOTA
1383
    ///
1384
    /// Result:
1385
    /// * OK - getquota completed
1386
    /// * NO - getquota error: no such quota root, permission denied
1387
    /// * BAD - command unknown or arguments invalid
1388
    ///
1389
    /// # Example (IMAP)
1390
    ///
1391
    /// ```imap
1392
    /// S: * CAPABILITY [...] QUOTA QUOTA=RES-STORAGE [...]
1393
    /// [...]
1394
    /// C: G0001 GETQUOTA "!partition/sda4"
1395
    /// S: * QUOTA "!partition/sda4" (STORAGE 104 10923847)
1396
    /// S: G0001 OK Getquota complete
1397
    /// ```
1398
    ///
1399
    /// <div class="warning">
1400
    /// This extension must only be used when the server advertised support for it sending the QUOTA* capability.
1401
    /// </div>
1402
    GetQuota {
1403
        /// Name of quota root.
1404
        root: AString<'a>,
1405
    },
1406

1407
    /// Takes a mailbox name and returns the list of quota roots for the mailbox in an untagged QUOTAROOT response.
1408
    /// For each listed quota root, it also returns the quota root's resource usage and limits in an untagged QUOTA response.
1409
    ///
1410
    /// Arguments:
1411
    /// * mailbox name
1412
    ///
1413
    /// Responses:
1414
    /// * REQUIRED untagged responses: QUOTAROOT, QUOTA
1415
    ///
1416
    /// Result:
1417
    /// * OK - getquotaroot completed
1418
    /// * NO - getquotaroot error: permission denied
1419
    /// * BAD - command unknown or arguments invalid
1420
    ///
1421
    /// Note that the mailbox name parameter doesn't have to reference an existing mailbox.
1422
    /// This can be handy in order to determine which quota root would apply to a mailbox when it gets created
1423
    ///
1424
    /// # Example (IMAP)
1425
    ///
1426
    /// ```imap
1427
    /// S: * CAPABILITY [...] QUOTA QUOTA=RES-STORAGE QUOTA=RES-MESSAGE
1428
    /// [...]
1429
    /// C: G0002 GETQUOTAROOT INBOX
1430
    /// S: * QUOTAROOT INBOX "#user/alice" "!partition/sda4"
1431
    /// S: * QUOTA "#user/alice" (MESSAGE 42 1000)
1432
    /// S: * QUOTA "!partition/sda4" (STORAGE 104 10923847)
1433
    /// S: G0002 OK Getquotaroot complete
1434
    /// ```
1435
    ///
1436
    /// <div class="warning">
1437
    /// This extension must only be used when the server advertised support for it sending the QUOTA* capability.
1438
    /// </div>
1439
    GetQuotaRoot {
1440
        /// Name of mailbox.
1441
        mailbox: Mailbox<'a>,
1442
    },
1443

1444
    /// Changes the mailbox quota root resource limits to the specified limits.
1445
    ///
1446
    /// Arguments:
1447
    /// * quota root list of resource limits
1448
    ///
1449
    /// Responses:
1450
    /// * untagged responses: QUOTA
1451
    ///
1452
    /// Result:
1453
    ///
1454
    /// * OK - setquota completed
1455
    /// * NO - setquota error: can't set that data
1456
    /// * BAD - command unknown or arguments invalid
1457
    ///
1458
    /// Note: requires the server to advertise the "QUOTASET" capability.
1459
    ///
1460
    /// # Example (IMAP)
1461
    ///
1462
    /// ```imap
1463
    /// S: * CAPABILITY [...] QUOTA QUOTASET QUOTA=RES-STORAGE QUOTA=RES-
1464
    /// MESSAGE [...]
1465
    /// [...]
1466
    /// C: S0000 GETQUOTA "#user/alice"
1467
    /// S: * QUOTA "#user/alice" (STORAGE 54 111 MESSAGE 42 1000)
1468
    /// S: S0000 OK Getquota completed
1469
    /// C: S0001 SETQUOTA "#user/alice" (STORAGE 510)
1470
    /// S: * QUOTA "#user/alice" (STORAGE 58 512)
1471
    /// // The server has rounded the STORAGE quota limit requested to
1472
    /// the nearest 512 blocks of 1024 octets; otherwise, another client
1473
    /// has performed a near-simultaneous SETQUOTA using a limit of 512.
1474
    /// S: S0001 OK Rounded quota
1475
    /// C: S0002 SETQUOTA "!partition/sda4" (STORAGE 99999999)
1476
    /// S: * QUOTA "!partition/sda4" (STORAGE 104 10923847)
1477
    /// // The server has not changed the quota, since this is a
1478
    /// filesystem limit, and it cannot be changed. The QUOTA
1479
    /// response here is entirely optional.
1480
    /// S: S0002 NO Cannot change system limit
1481
    /// ```
1482
    ///
1483
    /// <div class="warning">
1484
    /// This extension must only be used when the server advertised support for it sending the QUOTA* capability.
1485
    /// </div>
1486
    SetQuota {
1487
        /// Name of quota root.
1488
        root: AString<'a>,
1489
        /// List of resource limits.
1490
        quotas: Vec<QuotaSet<'a>>,
1491
    },
1492

1493
    /// MOVE command.
1494
    ///
1495
    /// <div class="warning">
1496
    /// This extension must only be used when the server advertised support for it sending the MOVE capability.
1497
    /// </div>
1498
    Move {
1499
        /// Set of messages.
1500
        sequence_set: SequenceSet,
1501
        /// Destination mailbox.
1502
        mailbox: Mailbox<'a>,
1503
        /// Use UID variant.
1504
        uid: bool,
1505
    },
1506

1507
    #[cfg(feature = "ext_id")]
1508
    /// ID command.
1509
    ///
1510
    /// <div class="warning">
1511
    /// This extension must only be used when the server advertised support for it sending the ID capability.
1512
    /// </div>
1513
    Id {
1514
        /// Parameters.
1515
        parameters: Option<Vec<(IString<'a>, NString<'a>)>>,
1516
    },
1517

1518
    #[cfg(feature = "ext_metadata")]
1519
    /// Set annotation(s).
1520
    ///
1521
    /// <div class="warning">
1522
    /// This extension must only be used when the server advertised support for it sending the METADATA* capability.
1523
    /// </div>
1524
    SetMetadata {
1525
        mailbox: Mailbox<'a>,
1526
        entry_values: Vec1<EntryValue<'a>>,
1527
    },
1528

1529
    #[cfg(feature = "ext_metadata")]
1530
    /// Retrieve server or mailbox annotation(s).
1531
    ///
1532
    /// <div class="warning">
1533
    /// This extension must only be used when the server advertised support for it sending the METADATA* capability.
1534
    /// </div>
1535
    GetMetadata {
1536
        options: Vec<GetMetadataOption>,
1537
        mailbox: Mailbox<'a>,
1538
        entries: Vec1<Entry<'a>>,
1539
    },
1540
}
1541

1542
impl<'a> CommandBody<'a> {
1543
    /// Prepend a tag to finalize the command body to a command.
1544
    pub fn tag<T>(self, tag: T) -> Result<Command<'a>, T::Error>
120✔
1545
    where
120✔
1546
        T: TryInto<Tag<'a>>,
120✔
1547
    {
120✔
1548
        Ok(Command {
120✔
1549
            tag: tag.try_into()?,
120✔
1550
            body: self,
120✔
1551
        })
1552
    }
120✔
1553

1554
    // ----- Constructors -----
1555

1556
    /// Construct an AUTHENTICATE command.
1557
    pub fn authenticate(mechanism: AuthMechanism<'a>) -> Self {
30✔
1558
        CommandBody::Authenticate {
30✔
1559
            mechanism,
30✔
1560
            initial_response: None,
30✔
1561
        }
30✔
1562
    }
30✔
1563

1564
    /// Construct an AUTHENTICATE command (with an initial response, SASL-IR).
1565
    ///
1566
    /// Note: Use this only when the server advertised the `SASL-IR` capability.
1567
    ///
1568
    /// <div class="warning">
1569
    /// This extension must only be used when the server advertised support for it sending the SASL-IR capability.
1570
    /// </div>
1571
    pub fn authenticate_with_ir<I>(mechanism: AuthMechanism<'a>, initial_response: I) -> Self
8✔
1572
    where
8✔
1573
        I: Into<Cow<'a, [u8]>>,
8✔
1574
    {
8✔
1575
        CommandBody::Authenticate {
8✔
1576
            mechanism,
8✔
1577
            initial_response: Some(Secret::new(initial_response.into())),
8✔
1578
        }
8✔
1579
    }
8✔
1580

1581
    /// Construct a LOGIN command.
1582
    pub fn login<U, P>(username: U, password: P) -> Result<Self, LoginError<U::Error, P::Error>>
50✔
1583
    where
50✔
1584
        U: TryInto<AString<'a>>,
50✔
1585
        P: TryInto<AString<'a>>,
50✔
1586
    {
50✔
1587
        Ok(CommandBody::Login {
50✔
1588
            username: username.try_into().map_err(LoginError::Username)?,
50✔
1589
            password: Secret::new(password.try_into().map_err(LoginError::Password)?),
44✔
1590
        })
1591
    }
50✔
1592

1593
    /// Construct a SELECT command.
1594
    pub fn select<M>(mailbox: M) -> Result<Self, M::Error>
14✔
1595
    where
14✔
1596
        M: TryInto<Mailbox<'a>>,
14✔
1597
    {
14✔
1598
        Ok(CommandBody::Select {
14✔
1599
            mailbox: mailbox.try_into()?,
14✔
1600
            #[cfg(feature = "ext_condstore_qresync")]
1601
            parameters: Vec::default(),
14✔
1602
        })
1603
    }
14✔
1604

1605
    /// Construct an EXAMINE command.
1606
    pub fn examine<M>(mailbox: M) -> Result<Self, M::Error>
12✔
1607
    where
12✔
1608
        M: TryInto<Mailbox<'a>>,
12✔
1609
    {
12✔
1610
        Ok(CommandBody::Examine {
12✔
1611
            mailbox: mailbox.try_into()?,
12✔
1612
            #[cfg(feature = "ext_condstore_qresync")]
1613
            parameters: Vec::default(),
12✔
1614
        })
1615
    }
12✔
1616

1617
    /// Construct a CREATE command.
1618
    pub fn create<M>(mailbox: M) -> Result<Self, M::Error>
2✔
1619
    where
2✔
1620
        M: TryInto<Mailbox<'a>>,
2✔
1621
    {
2✔
1622
        Ok(CommandBody::Create {
2✔
1623
            mailbox: mailbox.try_into()?,
2✔
1624
        })
1625
    }
2✔
1626

1627
    /// Construct a DELETE command.
1628
    pub fn delete<M>(mailbox: M) -> Result<Self, M::Error>
2✔
1629
    where
2✔
1630
        M: TryInto<Mailbox<'a>>,
2✔
1631
    {
2✔
1632
        Ok(CommandBody::Delete {
2✔
1633
            mailbox: mailbox.try_into()?,
2✔
1634
        })
1635
    }
2✔
1636

1637
    /// Construct a RENAME command.
1638
    pub fn rename<F, T>(mailbox: F, new_mailbox: T) -> Result<Self, RenameError<F::Error, T::Error>>
2✔
1639
    where
2✔
1640
        F: TryInto<Mailbox<'a>>,
2✔
1641
        T: TryInto<Mailbox<'a>>,
2✔
1642
    {
2✔
1643
        Ok(CommandBody::Rename {
2✔
1644
            from: mailbox.try_into().map_err(RenameError::From)?,
2✔
1645
            to: new_mailbox.try_into().map_err(RenameError::To)?,
2✔
1646
        })
1647
    }
2✔
1648

1649
    /// Construct a SUBSCRIBE command.
1650
    pub fn subscribe<M>(mailbox: M) -> Result<Self, M::Error>
2✔
1651
    where
2✔
1652
        M: TryInto<Mailbox<'a>>,
2✔
1653
    {
2✔
1654
        Ok(CommandBody::Subscribe {
2✔
1655
            mailbox: mailbox.try_into()?,
2✔
1656
        })
1657
    }
2✔
1658

1659
    /// Construct an UNSUBSCRIBE command.
1660
    pub fn unsubscribe<M>(mailbox: M) -> Result<Self, M::Error>
2✔
1661
    where
2✔
1662
        M: TryInto<Mailbox<'a>>,
2✔
1663
    {
2✔
1664
        Ok(CommandBody::Unsubscribe {
2✔
1665
            mailbox: mailbox.try_into()?,
2✔
1666
        })
1667
    }
2✔
1668

1669
    /// Construct a LIST command.
1670
    pub fn list<A, B>(
6✔
1671
        reference: A,
6✔
1672
        mailbox_wildcard: B,
6✔
1673
    ) -> Result<Self, ListError<A::Error, B::Error>>
6✔
1674
    where
6✔
1675
        A: TryInto<Mailbox<'a>>,
6✔
1676
        B: TryInto<ListMailbox<'a>>,
6✔
1677
    {
6✔
1678
        Ok(CommandBody::List {
6✔
1679
            reference: reference.try_into().map_err(ListError::Reference)?,
6✔
1680
            mailbox_wildcard: mailbox_wildcard.try_into().map_err(ListError::Mailbox)?,
6✔
1681
        })
1682
    }
6✔
1683

1684
    /// Construct a LSUB command.
1685
    pub fn lsub<A, B>(
4✔
1686
        reference: A,
4✔
1687
        mailbox_wildcard: B,
4✔
1688
    ) -> Result<Self, ListError<A::Error, B::Error>>
4✔
1689
    where
4✔
1690
        A: TryInto<Mailbox<'a>>,
4✔
1691
        B: TryInto<ListMailbox<'a>>,
4✔
1692
    {
4✔
1693
        Ok(CommandBody::Lsub {
4✔
1694
            reference: reference.try_into().map_err(ListError::Reference)?,
4✔
1695
            mailbox_wildcard: mailbox_wildcard.try_into().map_err(ListError::Mailbox)?,
4✔
1696
        })
1697
    }
4✔
1698

1699
    /// Construct a STATUS command.
1700
    pub fn status<M, I>(mailbox: M, item_names: I) -> Result<Self, M::Error>
4✔
1701
    where
4✔
1702
        M: TryInto<Mailbox<'a>>,
4✔
1703
        I: Into<Cow<'a, [StatusDataItemName]>>,
4✔
1704
    {
4✔
1705
        let mailbox = mailbox.try_into()?;
4✔
1706

1707
        Ok(CommandBody::Status {
4✔
1708
            mailbox,
4✔
1709
            item_names: item_names.into(),
4✔
1710
        })
4✔
1711
    }
4✔
1712

1713
    /// Construct an APPEND command.
1714
    pub fn append<M, D>(
4✔
1715
        mailbox: M,
4✔
1716
        flags: Vec<Flag<'a>>,
4✔
1717
        date: Option<DateTime>,
4✔
1718
        message: D,
4✔
1719
    ) -> Result<Self, AppendError<M::Error, D::Error>>
4✔
1720
    where
4✔
1721
        M: TryInto<Mailbox<'a>>,
4✔
1722
        D: TryInto<Literal<'a>>,
4✔
1723
    {
4✔
1724
        Ok(CommandBody::Append {
4✔
1725
            mailbox: mailbox.try_into().map_err(AppendError::Mailbox)?,
4✔
1726
            flags,
4✔
1727
            date,
4✔
1728
            message: LiteralOrLiteral8::Literal(message.try_into().map_err(AppendError::Data)?),
4✔
1729
        })
1730
    }
4✔
1731

1732
    /// Construct a SEARCH command.
1733
    pub fn search(charset: Option<Charset<'a>>, criteria: Vec1<SearchKey<'a>>, uid: bool) -> Self {
14✔
1734
        CommandBody::Search {
14✔
1735
            charset,
14✔
1736
            criteria,
14✔
1737
            uid,
14✔
1738
        }
14✔
1739
    }
14✔
1740

1741
    /// Construct a FETCH command.
1742
    pub fn fetch<S, I>(sequence_set: S, macro_or_item_names: I, uid: bool) -> Result<Self, S::Error>
8✔
1743
    where
8✔
1744
        S: TryInto<SequenceSet>,
8✔
1745
        I: Into<MacroOrMessageDataItemNames<'a>>,
8✔
1746
    {
8✔
1747
        let sequence_set = sequence_set.try_into()?;
8✔
1748

1749
        Ok(CommandBody::Fetch {
8✔
1750
            sequence_set,
8✔
1751
            macro_or_item_names: macro_or_item_names.into(),
8✔
1752
            uid,
8✔
1753
            #[cfg(feature = "ext_condstore_qresync")]
8✔
1754
            modifiers: Vec::default(),
8✔
1755
        })
8✔
1756
    }
8✔
1757

1758
    /// Construct a STORE command.
1759
    pub fn store<S>(
6✔
1760
        sequence_set: S,
6✔
1761
        kind: StoreType,
6✔
1762
        response: StoreResponse,
6✔
1763
        flags: Vec<Flag<'a>>,
6✔
1764
        uid: bool,
6✔
1765
    ) -> Result<Self, S::Error>
6✔
1766
    where
6✔
1767
        S: TryInto<SequenceSet>,
6✔
1768
    {
6✔
1769
        let sequence_set = sequence_set.try_into()?;
6✔
1770

1771
        Ok(CommandBody::Store {
6✔
1772
            sequence_set,
6✔
1773
            kind,
6✔
1774
            response,
6✔
1775
            flags,
6✔
1776
            uid,
6✔
1777
            #[cfg(feature = "ext_condstore_qresync")]
6✔
1778
            modifiers: Vec::default(),
6✔
1779
        })
6✔
1780
    }
6✔
1781

1782
    /// Construct a COPY command.
1783
    pub fn copy<S, M>(
4✔
1784
        sequence_set: S,
4✔
1785
        mailbox: M,
4✔
1786
        uid: bool,
4✔
1787
    ) -> Result<Self, CopyError<S::Error, M::Error>>
4✔
1788
    where
4✔
1789
        S: TryInto<SequenceSet>,
4✔
1790
        M: TryInto<Mailbox<'a>>,
4✔
1791
    {
4✔
1792
        Ok(CommandBody::Copy {
4✔
1793
            sequence_set: sequence_set.try_into().map_err(CopyError::Sequence)?,
4✔
1794
            mailbox: mailbox.try_into().map_err(CopyError::Mailbox)?,
4✔
1795
            uid,
4✔
1796
        })
1797
    }
4✔
1798

1799
    /// Get the name of the command.
1800
    pub fn name(&self) -> &'static str {
66✔
1801
        match self {
66✔
1802
            Self::Capability => "CAPABILITY",
2✔
1803
            Self::Noop => "NOOP",
2✔
1804
            Self::Logout => "LOGOUT",
2✔
1805
            #[cfg(feature = "starttls")]
1806
            Self::StartTLS => "STARTTLS",
2✔
1807
            Self::Authenticate { .. } => "AUTHENTICATE",
2✔
1808
            Self::Login { .. } => "LOGIN",
2✔
1809
            Self::Select { .. } => "SELECT",
2✔
1810
            Self::Sort { .. } => "SORT",
×
1811
            Self::Thread { .. } => "THREAD",
×
1812
            Self::Unselect => "UNSELECT",
2✔
1813
            Self::Examine { .. } => "EXAMINE",
2✔
1814
            Self::Create { .. } => "CREATE",
2✔
1815
            Self::Delete { .. } => "DELETE",
2✔
1816
            Self::Rename { .. } => "RENAME",
2✔
1817
            Self::Subscribe { .. } => "SUBSCRIBE",
2✔
1818
            Self::Unsubscribe { .. } => "UNSUBSCRIBE",
2✔
1819
            Self::List { .. } => "LIST",
2✔
1820
            Self::Lsub { .. } => "LSUB",
2✔
1821
            Self::Status { .. } => "STATUS",
2✔
1822
            Self::Append { .. } => "APPEND",
4✔
1823
            Self::Check => "CHECK",
2✔
1824
            Self::Close => "CLOSE",
2✔
1825
            Self::Expunge => "EXPUNGE",
2✔
1826
            Self::ExpungeUid { .. } => "EXPUNGE",
×
1827
            Self::Search { .. } => "SEARCH",
2✔
1828
            Self::Fetch { .. } => "FETCH",
2✔
1829
            Self::Store { .. } => "STORE",
2✔
1830
            Self::Copy { .. } => "COPY",
2✔
1831
            Self::Idle => "IDLE",
2✔
1832
            Self::Enable { .. } => "ENABLE",
2✔
1833
            Self::Compress { .. } => "COMPRESS",
2✔
1834
            Self::GetQuota { .. } => "GETQUOTA",
2✔
1835
            Self::GetQuotaRoot { .. } => "GETQUOTAROOT",
2✔
1836
            Self::SetQuota { .. } => "SETQUOTA",
2✔
1837
            Self::Move { .. } => "MOVE",
2✔
1838
            #[cfg(feature = "ext_id")]
1839
            Self::Id { .. } => "ID",
×
1840
            #[cfg(feature = "ext_metadata")]
1841
            Self::SetMetadata { .. } => "SETMETADATA",
×
1842
            #[cfg(feature = "ext_metadata")]
1843
            Self::GetMetadata { .. } => "GETMETADATA",
×
1844
        }
1845
    }
66✔
1846
}
1847

1848
#[cfg(feature = "ext_condstore_qresync")]
1849
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
8✔
NEW
1850
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
×
1851
#[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)]
1852
pub enum SelectParameter {
1853
    CondStore,
1854
    QResync {
1855
        uid_validity: NonZeroU32,
1856
        mod_sequence_value: NonZeroU64,
1857
        known_uids: Option<SequenceSet>, // TODO(misuse): "*" is not allowed.
1858
        seq_match_data: Option<(SequenceSet, SequenceSet)>, // TODO(misuse): ensure both have the same length?
1859
    },
1860
}
1861

1862
#[cfg(feature = "ext_condstore_qresync")]
NEW
1863
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
×
NEW
1864
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
×
1865
#[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)]
1866
pub enum FetchModifier {
1867
    ChangedSince(NonZeroU64),
1868
    Vanished,
1869
}
1870

1871
#[cfg(feature = "ext_condstore_qresync")]
NEW
1872
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
×
NEW
1873
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
×
1874
#[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)]
32✔
1875
pub enum StoreModifier {
1876
    UnchangedSince(u64),
1877
}
1878

1879
/// Error-related types.
1880
pub mod error {
1881
    use thiserror::Error;
1882

1883
    #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)]
1884
    pub enum LoginError<U, P> {
1885
        #[error("Invalid username: {0}")]
1886
        Username(U),
1887
        #[error("Invalid password: {0}")]
1888
        Password(P),
1889
    }
1890

1891
    #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)]
1892
    pub enum RenameError<F, T> {
1893
        #[error("Invalid (from) mailbox: {0}")]
1894
        From(F),
1895
        #[error("Invalid (to) mailbox: {0}")]
1896
        To(T),
1897
    }
1898

1899
    #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)]
1900
    pub enum ListError<R, M> {
1901
        #[error("Invalid reference: {0}")]
1902
        Reference(R),
1903
        #[error("Invalid mailbox: {0}")]
1904
        Mailbox(M),
1905
    }
1906

1907
    #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)]
1908
    pub enum AppendError<M, D> {
1909
        #[error("Invalid mailbox: {0}")]
1910
        Mailbox(M),
1911
        #[error("Invalid data: {0}")]
1912
        Data(D),
1913
    }
1914

1915
    #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)]
1916
    pub enum CopyError<S, M> {
1917
        #[error("Invalid sequence: {0}")]
1918
        Sequence(S),
1919
        #[error("Invalid mailbox: {0}")]
1920
        Mailbox(M),
1921
    }
1922
}
1923

1924
#[cfg(test)]
1925
mod tests {
1926
    use chrono::DateTime as ChronoDateTime;
1927

1928
    use super::*;
1929
    use crate::{
1930
        auth::AuthMechanism,
1931
        core::{AString, Charset, IString, Literal, LiteralMode, Vec1},
1932
        datetime::DateTime,
1933
        extensions::{
1934
            binary::Literal8,
1935
            compress::CompressionAlgorithm,
1936
            enable::{CapabilityEnable, Utf8Kind},
1937
        },
1938
        fetch::{Macro, MacroOrMessageDataItemNames, MessageDataItemName, Part, Section},
1939
        flag::{Flag, StoreType},
1940
        mailbox::{ListMailbox, Mailbox},
1941
        search::SearchKey,
1942
        secret::Secret,
1943
        sequence::{SeqOrUid, Sequence, SequenceSet},
1944
        status::StatusDataItemName,
1945
    };
1946

1947
    #[test]
1948
    fn test_conversion_command_body() {
2✔
1949
        let cmds = vec![
2✔
1950
            CommandBody::Capability,
2✔
1951
            CommandBody::Noop,
2✔
1952
            CommandBody::Logout,
2✔
1953
            #[cfg(feature = "starttls")]
2✔
1954
            CommandBody::StartTLS,
2✔
1955
            CommandBody::authenticate(AuthMechanism::Plain),
2✔
1956
            CommandBody::authenticate(AuthMechanism::Login),
2✔
1957
            CommandBody::authenticate_with_ir(AuthMechanism::Plain, b"XXXXXXXX".as_ref()),
2✔
1958
            CommandBody::authenticate_with_ir(AuthMechanism::Login, b"YYYYYYYY".as_ref()),
2✔
1959
            CommandBody::login("alice", "I_am_an_atom").unwrap(),
2✔
1960
            CommandBody::login("alice", "I am \\ \"quoted\"").unwrap(),
2✔
1961
            CommandBody::login("alice", "I am a literal²").unwrap(),
2✔
1962
            CommandBody::login(
2✔
1963
                AString::Atom("alice".try_into().unwrap()),
2✔
1964
                AString::String(crate::core::IString::Literal(
2✔
1965
                    vec![0xff, 0xff, 0xff].try_into().unwrap(),
2✔
1966
                )),
2✔
1967
            )
2✔
1968
            .unwrap(),
2✔
1969
            CommandBody::select("inbox").unwrap(),
2✔
1970
            CommandBody::select("atom").unwrap(),
2✔
1971
            CommandBody::select("C:\\").unwrap(),
2✔
1972
            CommandBody::select("²").unwrap(),
2✔
1973
            CommandBody::select("Trash").unwrap(),
2✔
1974
            CommandBody::examine("inbox").unwrap(),
2✔
1975
            CommandBody::examine("atom").unwrap(),
2✔
1976
            CommandBody::examine("C:\\").unwrap(),
2✔
1977
            CommandBody::examine("²").unwrap(),
2✔
1978
            CommandBody::examine("Trash").unwrap(),
2✔
1979
            CommandBody::create("inBoX").unwrap(),
2✔
1980
            CommandBody::delete("inBOX").unwrap(),
2✔
1981
            CommandBody::rename("iNBoS", "INboX").unwrap(),
2✔
1982
            CommandBody::subscribe("inbox").unwrap(),
2✔
1983
            CommandBody::unsubscribe("INBOX").unwrap(),
2✔
1984
            CommandBody::list("iNbOx", "test").unwrap(),
2✔
1985
            CommandBody::list("inbox", ListMailbox::Token("test".try_into().unwrap())).unwrap(),
2✔
1986
            CommandBody::lsub(
2✔
1987
                "inbox",
2✔
1988
                ListMailbox::String(IString::Quoted("\x7f".try_into().unwrap())),
2✔
1989
            )
2✔
1990
            .unwrap(),
2✔
1991
            CommandBody::list("inBoX", ListMailbox::Token("test".try_into().unwrap())).unwrap(),
2✔
1992
            CommandBody::lsub(
2✔
1993
                "INBOX",
2✔
1994
                ListMailbox::String(IString::Quoted("\x7f".try_into().unwrap())),
2✔
1995
            )
2✔
1996
            .unwrap(),
2✔
1997
            CommandBody::status("inbox", vec![StatusDataItemName::Messages]).unwrap(),
2✔
1998
            CommandBody::append(
2✔
1999
                "inbox",
2✔
2000
                vec![],
2✔
2001
                Some(
2✔
2002
                    DateTime::try_from(
2✔
2003
                        ChronoDateTime::parse_from_rfc2822("Tue, 1 Jul 2003 10:52:37 +0200")
2✔
2004
                            .unwrap(),
2✔
2005
                    )
2✔
2006
                    .unwrap(),
2✔
2007
                ),
2✔
2008
                vec![0xff, 0xff, 0xff],
2✔
2009
            )
2✔
2010
            .unwrap(),
2✔
2011
            CommandBody::append(
2✔
2012
                "inbox",
2✔
2013
                vec![Flag::Keyword("test".try_into().unwrap())],
2✔
2014
                Some(
2✔
2015
                    DateTime::try_from(
2✔
2016
                        ChronoDateTime::parse_from_rfc2822("Tue, 1 Jul 2003 10:52:37 +0200")
2✔
2017
                            .unwrap(),
2✔
2018
                    )
2✔
2019
                    .unwrap(),
2✔
2020
                ),
2✔
2021
                vec![0xff, 0xff, 0xff],
2✔
2022
            )
2✔
2023
            .unwrap(),
2✔
2024
            CommandBody::Check,
2✔
2025
            CommandBody::Close,
2✔
2026
            CommandBody::Expunge,
2✔
2027
            CommandBody::search(
2✔
2028
                None,
2✔
2029
                Vec1::from(SearchKey::And(
2✔
2030
                    vec![SearchKey::All, SearchKey::New, SearchKey::Unseen]
2✔
2031
                        .try_into()
2✔
2032
                        .unwrap(),
2✔
2033
                )),
2✔
2034
                false,
2✔
2035
            ),
2✔
2036
            CommandBody::search(
2✔
2037
                None,
2✔
2038
                Vec1::from(SearchKey::And(
2✔
2039
                    vec![SearchKey::All, SearchKey::New, SearchKey::Unseen]
2✔
2040
                        .try_into()
2✔
2041
                        .unwrap(),
2✔
2042
                )),
2✔
2043
                true,
2✔
2044
            ),
2✔
2045
            CommandBody::search(
2✔
2046
                None,
2✔
2047
                Vec1::from(SearchKey::And(
2✔
2048
                    vec![SearchKey::SequenceSet(SequenceSet(
2✔
2049
                        vec![Sequence::Single(SeqOrUid::Value(42.try_into().unwrap()))]
2✔
2050
                            .try_into()
2✔
2051
                            .unwrap(),
2✔
2052
                    ))]
2✔
2053
                    .try_into()
2✔
2054
                    .unwrap(),
2✔
2055
                )),
2✔
2056
                true,
2✔
2057
            ),
2✔
2058
            CommandBody::search(
2✔
2059
                None,
2✔
2060
                Vec1::from(SearchKey::SequenceSet("42".try_into().unwrap())),
2✔
2061
                true,
2✔
2062
            ),
2✔
2063
            CommandBody::search(
2✔
2064
                None,
2✔
2065
                Vec1::from(SearchKey::SequenceSet("*".try_into().unwrap())),
2✔
2066
                true,
2✔
2067
            ),
2✔
2068
            CommandBody::search(
2✔
2069
                None,
2✔
2070
                Vec1::from(SearchKey::Or(
2✔
2071
                    Box::new(SearchKey::Draft),
2✔
2072
                    Box::new(SearchKey::All),
2✔
2073
                )),
2✔
2074
                true,
2✔
2075
            ),
2✔
2076
            CommandBody::search(
2✔
2077
                Some(Charset::try_from("UTF-8").unwrap()),
2✔
2078
                Vec1::from(SearchKey::Or(
2✔
2079
                    Box::new(SearchKey::Draft),
2✔
2080
                    Box::new(SearchKey::All),
2✔
2081
                )),
2✔
2082
                true,
2✔
2083
            ),
2✔
2084
            CommandBody::fetch(
2✔
2085
                "1",
2✔
2086
                vec![MessageDataItemName::BodyExt {
2✔
2087
                    partial: None,
2✔
2088
                    section: Some(Section::Part(Part(
2✔
2089
                        vec![1.try_into().unwrap(), 1.try_into().unwrap()]
2✔
2090
                            .try_into()
2✔
2091
                            .unwrap(),
2✔
2092
                    ))),
2✔
2093
                    peek: true,
2✔
2094
                }],
2✔
2095
                false,
2✔
2096
            )
2✔
2097
            .unwrap(),
2✔
2098
            CommandBody::fetch("1:*,2,3", Macro::Full, true).unwrap(),
2✔
2099
            CommandBody::store(
2✔
2100
                "1,2:*",
2✔
2101
                StoreType::Remove,
2✔
2102
                StoreResponse::Answer,
2✔
2103
                vec![Flag::Seen, Flag::Draft],
2✔
2104
                false,
2✔
2105
            )
2✔
2106
            .unwrap(),
2✔
2107
            CommandBody::store(
2✔
2108
                "1:5",
2✔
2109
                StoreType::Add,
2✔
2110
                StoreResponse::Answer,
2✔
2111
                vec![Flag::Keyword("TEST".try_into().unwrap())],
2✔
2112
                true,
2✔
2113
            )
2✔
2114
            .unwrap(),
2✔
2115
            CommandBody::copy("1", "inbox", false).unwrap(),
2✔
2116
            CommandBody::copy("1337", "archive", true).unwrap(),
2✔
2117
        ];
2✔
2118

2119
        for (no, cmd_body) in cmds.into_iter().enumerate() {
102✔
2120
            println!("Test: {}, {:?}", no, cmd_body);
102✔
2121

102✔
2122
            let _ = cmd_body.tag(format!("A{}", no)).unwrap();
102✔
2123
        }
102✔
2124
    }
2✔
2125

2126
    #[test]
2127
    fn test_command_body_name() {
2✔
2128
        let tests = [
2✔
2129
            (CommandBody::Capability, "CAPABILITY"),
2✔
2130
            (CommandBody::Noop, "NOOP"),
2✔
2131
            (CommandBody::Logout, "LOGOUT"),
2✔
2132
            #[cfg(feature = "starttls")]
2✔
2133
            (CommandBody::StartTLS, "STARTTLS"),
2✔
2134
            (
2✔
2135
                CommandBody::Authenticate {
2✔
2136
                    mechanism: AuthMechanism::Plain,
2✔
2137
                    initial_response: None,
2✔
2138
                },
2✔
2139
                "AUTHENTICATE",
2✔
2140
            ),
2✔
2141
            (
2✔
2142
                CommandBody::Login {
2✔
2143
                    username: AString::try_from("user").unwrap(),
2✔
2144
                    password: Secret::new(AString::try_from("pass").unwrap()),
2✔
2145
                },
2✔
2146
                "LOGIN",
2✔
2147
            ),
2✔
2148
            (
2✔
2149
                CommandBody::Select {
2✔
2150
                    mailbox: Mailbox::Inbox,
2✔
2151
                    #[cfg(feature = "ext_condstore_qresync")]
2✔
2152
                    parameters: Vec::default(),
2✔
2153
                },
2✔
2154
                "SELECT",
2✔
2155
            ),
2✔
2156
            (CommandBody::Unselect, "UNSELECT"),
2✔
2157
            (
2✔
2158
                CommandBody::Examine {
2✔
2159
                    mailbox: Mailbox::Inbox,
2✔
2160
                    #[cfg(feature = "ext_condstore_qresync")]
2✔
2161
                    parameters: Vec::default(),
2✔
2162
                },
2✔
2163
                "EXAMINE",
2✔
2164
            ),
2✔
2165
            (
2✔
2166
                CommandBody::Create {
2✔
2167
                    mailbox: Mailbox::Inbox,
2✔
2168
                },
2✔
2169
                "CREATE",
2✔
2170
            ),
2✔
2171
            (
2✔
2172
                CommandBody::Delete {
2✔
2173
                    mailbox: Mailbox::Inbox,
2✔
2174
                },
2✔
2175
                "DELETE",
2✔
2176
            ),
2✔
2177
            (
2✔
2178
                CommandBody::Rename {
2✔
2179
                    from: Mailbox::Inbox,
2✔
2180
                    to: Mailbox::Inbox,
2✔
2181
                },
2✔
2182
                "RENAME",
2✔
2183
            ),
2✔
2184
            (
2✔
2185
                CommandBody::Subscribe {
2✔
2186
                    mailbox: Mailbox::Inbox,
2✔
2187
                },
2✔
2188
                "SUBSCRIBE",
2✔
2189
            ),
2✔
2190
            (
2✔
2191
                CommandBody::Unsubscribe {
2✔
2192
                    mailbox: Mailbox::Inbox,
2✔
2193
                },
2✔
2194
                "UNSUBSCRIBE",
2✔
2195
            ),
2✔
2196
            (
2✔
2197
                CommandBody::List {
2✔
2198
                    reference: Mailbox::Inbox,
2✔
2199
                    mailbox_wildcard: ListMailbox::try_from("").unwrap(),
2✔
2200
                },
2✔
2201
                "LIST",
2✔
2202
            ),
2✔
2203
            (
2✔
2204
                CommandBody::Lsub {
2✔
2205
                    reference: Mailbox::Inbox,
2✔
2206
                    mailbox_wildcard: ListMailbox::try_from("").unwrap(),
2✔
2207
                },
2✔
2208
                "LSUB",
2✔
2209
            ),
2✔
2210
            (
2✔
2211
                CommandBody::Status {
2✔
2212
                    mailbox: Mailbox::Inbox,
2✔
2213
                    item_names: vec![].into(),
2✔
2214
                },
2✔
2215
                "STATUS",
2✔
2216
            ),
2✔
2217
            (
2✔
2218
                CommandBody::Append {
2✔
2219
                    mailbox: Mailbox::Inbox,
2✔
2220
                    flags: vec![],
2✔
2221
                    date: None,
2✔
2222
                    message: LiteralOrLiteral8::Literal(Literal::try_from("").unwrap()),
2✔
2223
                },
2✔
2224
                "APPEND",
2✔
2225
            ),
2✔
2226
            (
2✔
2227
                CommandBody::Append {
2✔
2228
                    mailbox: Mailbox::Inbox,
2✔
2229
                    flags: vec![],
2✔
2230
                    date: None,
2✔
2231
                    message: LiteralOrLiteral8::Literal8(Literal8 {
2✔
2232
                        data: b"Hello\x00World\x00".as_ref().into(),
2✔
2233
                        mode: LiteralMode::NonSync,
2✔
2234
                    }),
2✔
2235
                },
2✔
2236
                "APPEND",
2✔
2237
            ),
2✔
2238
            (CommandBody::Check, "CHECK"),
2✔
2239
            (CommandBody::Close, "CLOSE"),
2✔
2240
            (CommandBody::Expunge, "EXPUNGE"),
2✔
2241
            (
2✔
2242
                CommandBody::Search {
2✔
2243
                    charset: None,
2✔
2244
                    criteria: Vec1::from(SearchKey::Recent),
2✔
2245
                    uid: true,
2✔
2246
                },
2✔
2247
                "SEARCH",
2✔
2248
            ),
2✔
2249
            (
2✔
2250
                CommandBody::Fetch {
2✔
2251
                    sequence_set: SequenceSet::try_from(1u32).unwrap(),
2✔
2252
                    macro_or_item_names: MacroOrMessageDataItemNames::Macro(Macro::Full),
2✔
2253
                    uid: true,
2✔
2254
                    #[cfg(feature = "ext_condstore_qresync")]
2✔
2255
                    modifiers: Vec::default(),
2✔
2256
                },
2✔
2257
                "FETCH",
2✔
2258
            ),
2✔
2259
            (
2✔
2260
                CommandBody::Store {
2✔
2261
                    sequence_set: SequenceSet::try_from(1).unwrap(),
2✔
2262
                    flags: vec![],
2✔
2263
                    response: StoreResponse::Silent,
2✔
2264
                    kind: StoreType::Add,
2✔
2265
                    uid: true,
2✔
2266
                    #[cfg(feature = "ext_condstore_qresync")]
2✔
2267
                    modifiers: Vec::default(),
2✔
2268
                },
2✔
2269
                "STORE",
2✔
2270
            ),
2✔
2271
            (
2✔
2272
                CommandBody::Copy {
2✔
2273
                    sequence_set: SequenceSet::try_from(1).unwrap(),
2✔
2274
                    mailbox: Mailbox::Inbox,
2✔
2275
                    uid: true,
2✔
2276
                },
2✔
2277
                "COPY",
2✔
2278
            ),
2✔
2279
            (CommandBody::Idle, "IDLE"),
2✔
2280
            (
2✔
2281
                CommandBody::Enable {
2✔
2282
                    capabilities: Vec1::from(CapabilityEnable::Utf8(Utf8Kind::Only)),
2✔
2283
                },
2✔
2284
                "ENABLE",
2✔
2285
            ),
2✔
2286
            (
2✔
2287
                CommandBody::Compress {
2✔
2288
                    algorithm: CompressionAlgorithm::Deflate,
2✔
2289
                },
2✔
2290
                "COMPRESS",
2✔
2291
            ),
2✔
2292
            (
2✔
2293
                CommandBody::GetQuota {
2✔
2294
                    root: AString::try_from("root").unwrap(),
2✔
2295
                },
2✔
2296
                "GETQUOTA",
2✔
2297
            ),
2✔
2298
            (
2✔
2299
                CommandBody::GetQuotaRoot {
2✔
2300
                    mailbox: Mailbox::Inbox,
2✔
2301
                },
2✔
2302
                "GETQUOTAROOT",
2✔
2303
            ),
2✔
2304
            (
2✔
2305
                CommandBody::SetQuota {
2✔
2306
                    root: AString::try_from("root").unwrap(),
2✔
2307
                    quotas: vec![],
2✔
2308
                },
2✔
2309
                "SETQUOTA",
2✔
2310
            ),
2✔
2311
            (
2✔
2312
                CommandBody::Move {
2✔
2313
                    sequence_set: SequenceSet::try_from(1).unwrap(),
2✔
2314
                    mailbox: Mailbox::Inbox,
2✔
2315
                    uid: true,
2✔
2316
                },
2✔
2317
                "MOVE",
2✔
2318
            ),
2✔
2319
        ];
2✔
2320

2321
        for (test, expected) in tests {
68✔
2322
            assert_eq!(test.name(), expected);
66✔
2323
        }
2324
    }
2✔
2325
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc