Skip to content

Commit 0637d8e

Browse files
committed
add quota support
1 parent ad5df73 commit 0637d8e

File tree

4 files changed

+106
-4
lines changed

4 files changed

+106
-4
lines changed

src/client.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1-
use std::collections::HashSet;
21
use std::fmt;
32
use std::ops::{Deref, DerefMut};
43
use std::pin::Pin;
54
use std::str;
5+
use std::{borrow::Cow, collections::HashSet};
66

77
use async_native_tls::{TlsConnector, TlsStream};
88
use async_std::channel;
99
use async_std::io::{self, Read, Write};
1010
use async_std::net::{TcpStream, ToSocketAddrs};
1111
use async_std::prelude::*;
12-
use imap_proto::{RequestId, Response};
12+
use extensions::quota::parse_get_quota_root;
13+
use imap_proto::{Quota, QuotaRoot, RequestId, Response};
1314

1415
use super::authenticator::Authenticator;
1516
use super::error::{Error, ParseError, Result, ValidateError};
1617
use super::parse::*;
1718
use super::types::*;
18-
use crate::extensions;
19+
use crate::extensions::{self, quota::parse_get_quota};
1920
use crate::imap_stream::ImapStream;
2021

2122
macro_rules! quote {
@@ -1245,6 +1246,37 @@ impl<T: Read + Write + Unpin + fmt::Debug + Send> Session<T> {
12451246
Ok(uids)
12461247
}
12471248

1249+
/// The [`GETQUOTA` command](https://tools.ietf.org/html/rfc2087#section-4.2)
1250+
pub async fn get_quota(&mut self, quota_root: Cow<'_, str>) -> Result<Quota<'_>> {
1251+
let id = self
1252+
.run_command(format!("GETQUOTA {}", quote!(quota_root)))
1253+
.await?;
1254+
let c = parse_get_quota(
1255+
&mut self.conn.stream,
1256+
self.unsolicited_responses_tx.clone(),
1257+
id,
1258+
)
1259+
.await?;
1260+
Ok(c)
1261+
}
1262+
1263+
/// The [`GETQUOTAROOT` command](https://tools.ietf.org/html/rfc2087#section-4.3)
1264+
pub async fn get_quota_root(
1265+
&mut self,
1266+
mailbox_name: Cow<'_, str>,
1267+
) -> Result<(Vec<QuotaRoot<'_>>, Vec<Quota<'_>>)> {
1268+
let id = self
1269+
.run_command(format!("GETQUOTAROOT {}", quote!(mailbox_name)))
1270+
.await?;
1271+
let c = parse_get_quota_root(
1272+
&mut self.conn.stream,
1273+
self.unsolicited_responses_tx.clone(),
1274+
id,
1275+
)
1276+
.await?;
1277+
Ok(c)
1278+
}
1279+
12481280
// these are only here because they are public interface, the rest is in `Connection`
12491281
/// Runs a command and checks if it returns OK.
12501282
pub async fn run_command_and_check_ok<S: AsRef<str>>(&mut self, command: S) -> Result<()> {

src/extensions/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
//! Implementations of various IMAP extensions.
22
pub mod idle;
3+
4+
pub mod quota;

src/extensions/quota.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//! Adds support for the GETQUOTA and GETQUOTAROOT commands specificed in [RFC2087](https://tools.ietf.org/html/rfc2087).
2+
3+
use async_std::channel;
4+
use async_std::io;
5+
use async_std::prelude::*;
6+
use async_std::stream::Stream;
7+
use imap_proto::{self, Quota, QuotaRoot, RequestId, Response};
8+
9+
use crate::types::ResponseData;
10+
use crate::types::*;
11+
use crate::{
12+
error::Result,
13+
parse::{filter_sync, handle_unilateral},
14+
};
15+
16+
pub(crate) async fn parse_get_quota<T: Stream<Item = io::Result<ResponseData>> + Unpin>(
17+
stream: &mut T,
18+
unsolicited: channel::Sender<UnsolicitedResponse>,
19+
command_tag: RequestId,
20+
) -> Result<Quota<'_>> {
21+
while let Some(resp) = stream
22+
.take_while(|res| filter_sync(res, &command_tag))
23+
.next()
24+
.await
25+
{
26+
let resp = resp?;
27+
match resp.parsed() {
28+
Response::Quota(q) => {
29+
return Ok(q.clone().into_owned());
30+
}
31+
_ => {
32+
handle_unilateral(resp, unsolicited.clone()).await;
33+
}
34+
}
35+
}
36+
37+
unreachable!(); // TODO, make this better
38+
}
39+
40+
pub(crate) async fn parse_get_quota_root<T: Stream<Item = io::Result<ResponseData>> + Unpin>(
41+
stream: &mut T,
42+
unsolicited: channel::Sender<UnsolicitedResponse>,
43+
command_tag: RequestId,
44+
) -> Result<(Vec<QuotaRoot<'_>>, Vec<Quota<'_>>)> {
45+
let mut roots: Vec<QuotaRoot<'_>> = Vec::new();
46+
let mut quotas: Vec<Quota<'_>> = Vec::new();
47+
48+
while let Some(resp) = stream
49+
.take_while(|res| filter_sync(res, &command_tag))
50+
.next()
51+
.await
52+
{
53+
let resp = resp?;
54+
match resp.parsed() {
55+
Response::QuotaRoot(qr) => {
56+
roots.push(qr.clone().into_owned());
57+
}
58+
Response::Quota(q) => {
59+
quotas.push(q.clone().into_owned());
60+
}
61+
_ => {
62+
handle_unilateral(resp, unsolicited.clone()).await;
63+
}
64+
}
65+
}
66+
67+
Ok((roots, quotas))
68+
}

src/parse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ fn filter(res: &io::Result<ResponseData>, command_tag: &RequestId) -> impl Futur
4646
futures::future::ready(val)
4747
}
4848

49-
fn filter_sync(res: &io::Result<ResponseData>, command_tag: &RequestId) -> bool {
49+
pub(crate) fn filter_sync(res: &io::Result<ResponseData>, command_tag: &RequestId) -> bool {
5050
match res {
5151
Ok(res) => match res.parsed() {
5252
Response::Done { tag, .. } => tag != command_tag,

0 commit comments

Comments
 (0)