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