/* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ /* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ use crate::{ core::{Session, SessionData}, op::ImapContext, spawn_op, }; use common::network::SessionStream; use imap_proto::{ Command, ResponseCode, StatusResponse, protocol::{ ImapResponse, capability::QuotaResourceName, quota::{Arguments, QuotaItem, QuotaResource, Response}, }, receiver::Request, }; use registry::schema::enums::Permission; use std::time::Instant; impl Session { pub async fn handle_get_quota(&mut self, request: Request) -> trc::Result<()> { // Validate access self.assert_has_permission(Permission::ImapStatus)?; let data = self.state.session_data(); spawn_op!(data, { match request.parse_get_quota() { Ok(argument) => match data.get_quota(argument).await { Ok(response) => { data.write_bytes(response).await?; } Err(error) => { data.write_error(error).await?; } }, Err(err) => data.write_error(err).await?, } Ok(()) }) } pub async fn handle_get_quota_root(&mut self, request: Request) -> trc::Result<()> { // Validate access self.assert_has_permission(Permission::ImapStatus)?; let data = self.state.session_data(); let is_utf8 = self.is_utf8; spawn_op!(data, { match request.parse_get_quota_root(is_utf8) { Ok(argument) => match data.get_quota_root(argument).await { Ok(response) => { data.write_bytes(response).await?; } Err(error) => { data.write_error(error).await?; } }, Err(err) => data.write_error(err).await?, } Ok(()) }) } } impl SessionData { pub async fn get_quota(&self, arguments: Arguments) -> trc::Result> { let op_start = Instant::now(); // Refresh mailboxes self.synchronize_mailboxes(false) .await .imap_ctx(&arguments.tag, trc::location!())?; // Validate quota root let account_id: u32 = arguments .name .strip_prefix("#") .and_then(|id| id.parse().ok()) .filter(|id| self.access_token.is_member(*id)) .ok_or_else(|| { trc::ImapEvent::Error .into_err() .details("Invalid quota root parameter.") .id(arguments.tag.to_string()) })?; // Obtain access token for mailbox let account = self .server .account(account_id) .await .imap_ctx(&arguments.tag, trc::location!())?; let used_quota = self .server .get_used_quota_account(account_id) .await .imap_ctx(&arguments.tag, trc::location!())?; trc::event!( Imap(trc::ImapEvent::GetQuota), SpanId = self.session_id, Id = arguments.name.clone(), Details = vec![ trc::Value::from(used_quota), trc::Value::from(account.disk_quota()) ], Elapsed = op_start.elapsed() ); // Build response let response = Response { quota_root_items: vec![], quota_items: vec![QuotaItem { name: arguments.name, resources: if account.disk_quota() > 0 { vec![QuotaResource { resource: QuotaResourceName::Storage, total: account.disk_quota(), used: used_quota as u64, }] } else { vec![] }, }], }; Ok(StatusResponse::ok("GETQUOTA successful.") .with_tag(arguments.tag) .serialize(response.serialize())) } pub async fn get_quota_root(&self, arguments: Arguments) -> trc::Result> { let op_start = Instant::now(); // Refresh mailboxes self.synchronize_mailboxes(false) .await .imap_ctx(&arguments.tag, trc::location!())?; // Validate mailbox let account_id = if let Some(mailbox) = self.get_mailbox_by_name(&arguments.name) { mailbox.account_id } else { return Err(trc::ImapEvent::Error .into_err() .details("Mailbox does not exist.") .code(ResponseCode::TryCreate) .id(arguments.tag)); }; // Obtain access token for mailbox let account = self .server .account(account_id) .await .imap_ctx(&arguments.tag, trc::location!())?; let used_quota = self .server .get_used_quota_account(account_id) .await .imap_ctx(&arguments.tag, trc::location!())?; trc::event!( Imap(trc::ImapEvent::GetQuota), SpanId = self.session_id, MailboxName = arguments.name.clone(), Details = vec![ trc::Value::from(used_quota), trc::Value::from(account.disk_quota()) ], Elapsed = op_start.elapsed() ); // Build response let response = Response { quota_root_items: vec![arguments.name, format!("#{account_id}")], quota_items: vec![QuotaItem { name: format!("#{account_id}"), resources: if account.disk_quota() > 0 { vec![QuotaResource { resource: QuotaResourceName::Storage, total: account.disk_quota(), used: used_quota as u64, }] } else { vec![] }, }], }; Ok(StatusResponse::ok("GETQUOTAROOT successful.") .with_tag(arguments.tag) .serialize(response.serialize())) } }