diff --git a/.albatros b/.albatros new file mode 100755 index 0000000..6439de6 --- /dev/null +++ b/.albatros @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euxo pipefail + +nix build --print-build-logs .#packages.x86_64-unknown-linux-musl.debug diff --git a/flake.nix b/flake.nix index 55aacd7..3bf91b2 100644 --- a/flake.nix +++ b/flake.nix @@ -80,6 +80,7 @@ echo export NIX_RUST_BUILD_FLAGS="''${NIX_RUST_BUILD_FLAGS} --deny warnings" + export NIX_RUST_LINK_FLAGS="''${NIX_RUST_LINK_FLAGS} --deny warnings" export RUSTC="''${CLIPPY_DRIVER}" ''); rustDebug = pkgs.rustBuilder.makePackageSet({ diff --git a/src/bayou.rs b/src/bayou.rs index 2d83ce3..cbfb414 100644 --- a/src/bayou.rs +++ b/src/bayou.rs @@ -103,9 +103,12 @@ impl Bayou { } else { debug!("(sync) loading checkpoint: {}", key); - let mut gor = GetObjectRequest::default(); - gor.bucket = self.bucket.clone(); - gor.key = key.to_string(); + let gor = GetObjectRequest { + bucket: self.bucket.clone(), + key: key.to_string(), + ..Default::default() + }; + let obj_res = self.s3.get_object(gor).await?; let obj_body = obj_res.body.ok_or(anyhow!("Missing object body"))?; @@ -173,7 +176,7 @@ impl Bayou { } match &val.value[0] { K2vValue::Value(v) => { - let op = open_deserialize::(&v, &self.key)?; + let op = open_deserialize::(v, &self.key)?; debug!("(sync) operation {}: {} {:?}", tsstr, base64::encode(v), op); ops.push((ts, op)); } @@ -381,10 +384,12 @@ impl Bayou { let cryptoblob = seal_serialize(&state_cp, &self.key)?; debug!("(cp) checkpoint body length: {}", cryptoblob.len()); - let mut por = PutObjectRequest::default(); - por.bucket = self.bucket.clone(); - por.key = format!("{}/checkpoint/{}", self.path, ts_cp.to_string()); - por.body = Some(cryptoblob.into()); + let por = PutObjectRequest{ + bucket: self.bucket.clone(), + key: format!("{}/checkpoint/{}", self.path, ts_cp.to_string()), + body: Some(cryptoblob.into()), + ..Default::default() + }; self.s3.put_object(por).await?; // Drop old checkpoints (but keep at least CHECKPOINTS_TO_KEEP of them) @@ -395,9 +400,11 @@ impl Bayou { // Delete blobs for (_ts, key) in existing_checkpoints[..last_to_keep].iter() { debug!("(cp) drop old checkpoint {}", key); - let mut dor = DeleteObjectRequest::default(); - dor.bucket = self.bucket.clone(); - dor.key = key.to_string(); + let dor = DeleteObjectRequest { + bucket: self.bucket.clone(), + key: key.to_string(), + ..Default::default() + }; self.s3.delete_object(dor).await?; } @@ -430,10 +437,12 @@ impl Bayou { async fn list_checkpoints(&self) -> Result> { let prefix = format!("{}/checkpoint/", self.path); - let mut lor = ListObjectsV2Request::default(); - lor.bucket = self.bucket.clone(); - lor.max_keys = Some(1000); - lor.prefix = Some(prefix.clone()); + let lor = ListObjectsV2Request{ + bucket: self.bucket.clone(), + max_keys: Some(1000), + prefix: Some(prefix.clone()), + ..Default::default() + }; let checkpoints_res = self.s3.list_objects_v2(lor).await?; @@ -537,6 +546,8 @@ pub struct Timestamp { } impl Timestamp { + #[allow(dead_code)] + // 2023-05-15 try to make clippy happy and not sure if this fn will be used in the future. pub fn now() -> Self { let mut rng = thread_rng(); Self { @@ -563,7 +574,7 @@ impl ToString for Timestamp { let mut bytes = [0u8; 16]; bytes[0..8].copy_from_slice(&u64::to_be_bytes(self.msec)); bytes[8..16].copy_from_slice(&u64::to_be_bytes(self.rand)); - hex::encode(&bytes) + hex::encode(bytes) } } diff --git a/src/cryptoblob.rs b/src/cryptoblob.rs index 395ae21..327a642 100644 --- a/src/cryptoblob.rs +++ b/src/cryptoblob.rs @@ -36,7 +36,7 @@ pub fn seal(plainblob: &[u8], key: &Key) -> Result> { use secretbox::{gen_nonce, NONCEBYTES}; // Compress data using zstd - let mut reader = &plainblob[..]; + let mut reader = plainblob; let zstdblob = zstd_encode(&mut reader, 0)?; // Encrypt @@ -63,5 +63,5 @@ pub fn seal_serialize(obj: T, key: &Key) -> Result> { .with_string_variants(); obj.serialize(&mut se)?; - Ok(seal(&wr, key)?) + seal(&wr, key) } diff --git a/src/imap/command/anonymous.rs b/src/imap/command/anonymous.rs index 3ac1f20..d258bd3 100644 --- a/src/imap/command/anonymous.rs +++ b/src/imap/command/anonymous.rs @@ -15,7 +15,7 @@ pub struct AnonymousContext<'a> { pub login_provider: Option<&'a ArcLoginProvider>, } -pub async fn dispatch<'a>(ctx: AnonymousContext<'a>) -> Result<(Response, flow::Transition)> { +pub async fn dispatch(ctx: AnonymousContext<'_>) -> Result<(Response, flow::Transition)> { match &ctx.req.command.body { CommandBody::Noop => Ok((Response::ok("Noop completed.")?, flow::Transition::None)), CommandBody::Capability => ctx.capability().await, diff --git a/src/imap/command/authenticated.rs b/src/imap/command/authenticated.rs index dc8b5a8..2deb723 100644 --- a/src/imap/command/authenticated.rs +++ b/src/imap/command/authenticated.rs @@ -25,7 +25,7 @@ pub struct AuthenticatedContext<'a> { pub user: &'a Arc, } -pub async fn dispatch<'a>(ctx: AuthenticatedContext<'a>) -> Result<(Response, flow::Transition)> { +pub async fn dispatch(ctx: AuthenticatedContext<'_>) -> Result<(Response, flow::Transition)> { match &ctx.req.command.body { CommandBody::Create { mailbox } => ctx.create(mailbox).await, CommandBody::Delete { mailbox } => ctx.delete(mailbox).await, @@ -150,9 +150,7 @@ impl<'a> AuthenticatedContext<'a> { for (i, _) in mb.match_indices(MAILBOX_HIERARCHY_DELIMITER) { if i > 0 { let smb = &mb[..i]; - if !vmailboxes.contains_key(&smb) { - vmailboxes.insert(smb, false); - } + vmailboxes.entry(smb).or_insert(false); } } vmailboxes.insert(mb, true); @@ -160,7 +158,7 @@ impl<'a> AuthenticatedContext<'a> { let mut ret = vec![]; for (mb, is_real) in vmailboxes.iter() { - if matches_wildcard(&wildcard, &mb) { + if matches_wildcard(&wildcard, mb) { let mailbox = mb .to_string() .try_into() diff --git a/src/imap/command/examined.rs b/src/imap/command/examined.rs index 8da68f8..1740b39 100644 --- a/src/imap/command/examined.rs +++ b/src/imap/command/examined.rs @@ -23,7 +23,7 @@ pub struct ExaminedContext<'a> { pub mailbox: &'a mut MailboxView, } -pub async fn dispatch<'a>(ctx: ExaminedContext<'a>) -> Result<(Response, flow::Transition)> { +pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response, flow::Transition)> { match &ctx.req.command.body { // CLOSE in examined state is not the same as in selected state // (in selected state it also does an EXPUNGE, here it doesn't) diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs index 0be8ff2..90a00ee 100644 --- a/src/imap/command/selected.rs +++ b/src/imap/command/selected.rs @@ -21,7 +21,7 @@ pub struct SelectedContext<'a> { pub mailbox: &'a mut MailboxView, } -pub async fn dispatch<'a>(ctx: SelectedContext<'a>) -> Result<(Response, flow::Transition)> { +pub async fn dispatch(ctx: SelectedContext<'_>) -> Result<(Response, flow::Transition)> { match &ctx.req.command.body { // Only write commands here, read commands are handled in // `examined.rs` diff --git a/src/imap/mailbox_view.rs b/src/imap/mailbox_view.rs index 028ed09..fbfdb30 100644 --- a/src/imap/mailbox_view.rs +++ b/src/imap/mailbox_view.rs @@ -126,7 +126,7 @@ impl MailboxView { data.push(Body::Data(Data::Fetch { seq_or_uid: NonZeroU32::try_from((i + 1) as u32).unwrap(), attributes: vec![ - MessageAttribute::Uid((*uid).try_into().unwrap()), + MessageAttribute::Uid(*uid), MessageAttribute::Flags( flags.iter().filter_map(|f| string_to_flag(f)).collect(), ), @@ -387,10 +387,8 @@ impl MailboxView { } } FetchAttribute::InternalDate => { - attributes.push(MessageAttribute::InternalDate(MyDateTime( - Utc.fix() - .timestamp(i64::try_from(meta.internaldate / 1000)?, 0), - ))); + let dt = Utc.fix().timestamp_opt(i64::try_from(meta.internaldate / 1000)?, 0).earliest().ok_or(anyhow!("Unable to parse internal date"))?; + attributes.push(MessageAttribute::InternalDate(MyDateTime(dt))); } } } @@ -529,8 +527,7 @@ impl MailboxView { .known_state .idx_by_flag .flags() - .map(|f| string_to_flag(f)) - .flatten() + .filter_map(|f| string_to_flag(f)) .collect(); for f in DEFAULT_FLAGS.iter() { if !flags.contains(f) { @@ -569,7 +566,7 @@ fn string_to_flag(f: &str) -> Option { "\\Deleted" => Some(Flag::Deleted), "\\Draft" => Some(Flag::Draft), "\\Recent" => Some(Flag::Recent), - _ => match Atom::try_from(f.strip_prefix('\\').unwrap().clone()) { + _ => match Atom::try_from(f.strip_prefix('\\').unwrap().to_string()) { Err(_) => { tracing::error!(flag=%f, "Unable to encode flag as IMAP atom"); None @@ -577,7 +574,7 @@ fn string_to_flag(f: &str) -> Option { Ok(a) => Some(Flag::Extension(a)), }, }, - Some(_) => match Atom::try_from(f.clone()) { + Some(_) => match Atom::try_from(f.to_string()) { Err(_) => { tracing::error!(flag=%f, "Unable to encode flag as IMAP atom"); None @@ -623,7 +620,7 @@ fn message_envelope(msg: &mail_parser::Message<'_>) -> Envelope { ), from: from.clone(), sender: convert_addresses(msg.sender()).unwrap_or(from.clone()), - reply_to: convert_addresses(msg.reply_to()).unwrap_or(from.clone()), + reply_to: convert_addresses(msg.reply_to()).unwrap_or(from), to: convert_addresses(msg.to()).unwrap_or(vec![]), cc: convert_addresses(msg.cc()).unwrap_or(vec![]), bcc: convert_addresses(msg.bcc()).unwrap_or(vec![]), @@ -639,7 +636,7 @@ fn convert_addresses(a: &mail_parser::HeaderValue<'_>) -> Option> { match a { mail_parser::HeaderValue::Address(a) => Some(vec![convert_address(a)]), mail_parser::HeaderValue::AddressList(l) => { - Some(l.iter().map(|a| convert_address(a)).collect()) + Some(l.iter().map(convert_address).collect()) } mail_parser::HeaderValue::Empty => None, _ => { @@ -722,7 +719,7 @@ fn build_imap_email_struct<'a>(msg: &Message<'a>, part: &MessagePart<'a>) -> Res }) } PartType::Text(bp) | PartType::Html(bp) => { - let (attrs, mut basic) = headers_to_basic_fields(&part, bp.len())?; + let (attrs, mut basic) = headers_to_basic_fields(part, bp.len())?; // If the charset is not defined, set it to "us-ascii" if attrs.charset.is_none() { @@ -736,10 +733,8 @@ fn build_imap_email_struct<'a>(msg: &Message<'a>, part: &MessagePart<'a>) -> Res // difference between MIME and raw emails, hence raw emails have no subtypes. let subtype = part .content_type() - .map(|h| h.c_subtype.as_ref()) - .flatten() - .map(|st| IString::try_from(st.to_string()).ok()) - .flatten() + .and_then(|h| h.c_subtype.as_ref()) + .and_then(|st| IString::try_from(st.to_string()).ok()) .unwrap_or(unchecked_istring("plain")); let number_of_lines = msg @@ -761,7 +756,7 @@ fn build_imap_email_struct<'a>(msg: &Message<'a>, part: &MessagePart<'a>) -> Res }) } PartType::Binary(bp) | PartType::InlineBinary(bp) => { - let (_, basic) = headers_to_basic_fields(&part, bp.len())?; + let (_, basic) = headers_to_basic_fields(part, bp.len())?; let ct = part .content_type() @@ -790,7 +785,7 @@ fn build_imap_email_struct<'a>(msg: &Message<'a>, part: &MessagePart<'a>) -> Res }) } PartType::Message(inner) => { - let (_, basic) = headers_to_basic_fields(&part, inner.raw_message().len())?; + let (_, basic) = headers_to_basic_fields(part, inner.raw_message().len())?; // We do not count the number of lines but the number of line // feeds to have the same behavior as Dovecot and Cyrus. @@ -803,7 +798,7 @@ fn build_imap_email_struct<'a>(msg: &Message<'a>, part: &MessagePart<'a>) -> Res specific: SpecificFields::Message { envelope: message_envelope(inner), body_structure: Box::new(build_imap_email_struct( - &inner, + inner, inner.root_part(), )?), @@ -850,8 +845,7 @@ fn attrs_to_params<'a>(bp: &impl MimeHeaders<'a>) -> (SpecialAttrs, Vec<(IString // Try to extract Content-Type attributes from headers let attrs = match bp .content_type() - .map(|c| c.attributes.as_ref()) - .flatten() + .and_then(|c| c.attributes.as_ref()) { Some(v) => v, _ => return (SpecialAttrs::default(), vec![]), @@ -896,14 +890,12 @@ fn headers_to_basic_fields<'a>( id: NString( bp.content_id() - .map(|ci| IString::try_from(ci.to_string()).ok()) - .flatten(), + .and_then(|ci| IString::try_from(ci.to_string()).ok()), ), description: NString( bp.content_description() - .map(|cd| IString::try_from(cd.to_string()).ok()) - .flatten(), + .and_then(|cd| IString::try_from(cd.to_string()).ok()), ), /* @@ -913,8 +905,7 @@ fn headers_to_basic_fields<'a>( */ content_transfer_encoding: bp .content_transfer_encoding() - .map(|h| IString::try_from(h.to_string()).ok()) - .flatten() + .and_then(|h| IString::try_from(h.to_string()).ok()) .unwrap_or(unchecked_istring("7bit")), size: u32::try_from(size)?, @@ -1023,7 +1014,7 @@ fn get_message_section<'a>( } } -fn map_subpart_msg<'a, F, R>(msg: &Message<'a>, path: &[NonZeroU32], f: F) -> Result +fn map_subpart_msg(msg: &Message<'_>, path: &[NonZeroU32], f: F) -> Result where F: FnOnce(&Message<'_>) -> Result, { @@ -1035,14 +1026,14 @@ where .get(path[0].get() as usize - 1) .ok_or(anyhow!("No such subpart: {}", path[0]))?; if let PartType::Message(msg_attach) = &part.body { - map_subpart_msg(&msg_attach, &path[1..], f) + map_subpart_msg(msg_attach, &path[1..], f) } else { bail!("Subpart is not a message: {}", path[0]); } } } -fn map_subpart<'a, F, R>(msg: &Message<'a>, path: &[NonZeroU32], f: F) -> Result +fn map_subpart(msg: &Message<'_>, path: &[NonZeroU32], f: F) -> Result where F: FnOnce(&Message<'_>, &MessagePart<'_>) -> Result, { @@ -1055,12 +1046,10 @@ where .ok_or(anyhow!("No such subpart: {}", path[0]))?; if path.len() == 1 { f(msg, part) + } else if let PartType::Message(msg_attach) = &part.body { + map_subpart(msg_attach, &path[1..], f) } else { - if let PartType::Message(msg_attach) = &part.body { - map_subpart(&msg_attach, &path[1..], f) - } else { - bail!("Subpart is not a message: {}", path[0]); - } + bail!("Subpart is not a message: {}", path[0]); } } } diff --git a/src/login/mod.rs b/src/login/mod.rs index 0605d4e..6d2ec71 100644 --- a/src/login/mod.rs +++ b/src/login/mod.rs @@ -190,8 +190,8 @@ impl CryptoKeys { // Write values to storage k2v.insert_batch(&[ - k2v_insert_single_key("keys", "salt", salt_ct, &ident_salt), - k2v_insert_single_key("keys", "public", public_ct, &keys.public), + k2v_insert_single_key("keys", "salt", salt_ct, ident_salt), + k2v_insert_single_key("keys", "public", public_ct, keys.public), k2v_insert_single_key("keys", &password_sortkey, None, &password_blob), ]) .await @@ -223,8 +223,8 @@ impl CryptoKeys { // Write values to storage k2v.insert_batch(&[ - k2v_insert_single_key("keys", "salt", salt_ct, &ident_salt), - k2v_insert_single_key("keys", "public", public_ct, &keys.public), + k2v_insert_single_key("keys", "salt", salt_ct, ident_salt), + k2v_insert_single_key("keys", "public", public_ct, keys.public), ]) .await .context("InsertBatch for salt and public")?; @@ -265,7 +265,7 @@ impl CryptoKeys { // Try to open blob let kdf_salt = &password_blob[..32]; let password_openned = - user_secrets.try_open_encrypted_keys(&kdf_salt, password, &password_blob[32..])?; + user_secrets.try_open_encrypted_keys(kdf_salt, password, &password_blob[32..])?; let keys = Self::deserialize(&password_openned)?; if keys.public != expected_public { @@ -332,7 +332,7 @@ impl CryptoKeys { if entry.value.iter().any(|x| matches!(x, K2vValue::Value(_))) { bail!("password already exists"); } - Some(entry.causality.clone()) + Some(entry.causality) } }; @@ -523,7 +523,7 @@ impl CryptoKeys { impl UserSecrets { fn derive_password_key_with(user_secret: &str, kdf_salt: &[u8], password: &str) -> Result { let tmp = format!("{}\n\n{}", user_secret, password); - Ok(Key::from_slice(&argon2_kdf(&kdf_salt, tmp.as_bytes(), 32)?).unwrap()) + Ok(Key::from_slice(&argon2_kdf(kdf_salt, tmp.as_bytes(), 32)?).unwrap()) } fn derive_password_key(&self, kdf_salt: &[u8], password: &str) -> Result { @@ -579,7 +579,7 @@ pub fn k2v_read_single_key<'a>( tombstones: bool, ) -> BatchReadOp<'a> { BatchReadOp { - partition_key: partition_key, + partition_key, filter: Filter { start: Some(sort_key), end: None, diff --git a/src/login/static_provider.rs b/src/login/static_provider.rs index 5ea765f..b9be5a6 100644 --- a/src/login/static_provider.rs +++ b/src/login/static_provider.rs @@ -151,7 +151,7 @@ pub fn verify_password(password: &str, hash: &str) -> Result { Argon2, }; let parsed_hash = - PasswordHash::new(&hash).map_err(|e| anyhow!("Invalid hashed password: {}", e))?; + PasswordHash::new(hash).map_err(|e| anyhow!("Invalid hashed password: {}", e))?; Ok(Argon2::default() .verify_password(password.as_bytes(), &parsed_hash) .is_ok()) diff --git a/src/mail/incoming.rs b/src/mail/incoming.rs index 26ca52a..290f1b3 100644 --- a/src/mail/incoming.rs +++ b/src/mail/incoming.rs @@ -68,7 +68,7 @@ async fn incoming_mail_watch_process_internal( let wait_new_mail = async { loop { - match k2v_wait_value_changed(&k2v, &INCOMING_PK, &INCOMING_WATCH_SK, &prev_ct) + match k2v_wait_value_changed(&k2v, INCOMING_PK, INCOMING_WATCH_SK, &prev_ct) .await { Ok(cv) => break cv, @@ -104,7 +104,7 @@ async fn incoming_mail_watch_process_internal( info!("User still available"); // If INBOX no longer is same mailbox, open new mailbox - let inbox_id = rx_inbox_id.borrow().clone(); + let inbox_id = *rx_inbox_id.borrow(); if let Some((id, uidvalidity)) = inbox_id { if Some(id) != inbox.as_ref().map(|b| b.id) { match user.open_mailbox_by_id(id, uidvalidity).await { @@ -145,10 +145,12 @@ async fn handle_incoming_mail( inbox: &Arc, lock_held: &watch::Receiver, ) -> Result<()> { - let mut lor = ListObjectsV2Request::default(); - lor.bucket = user.creds.storage.bucket.clone(); - lor.max_keys = Some(1000); - lor.prefix = Some("incoming/".into()); + let lor = ListObjectsV2Request { + bucket: user.creds.storage.bucket.clone(), + max_keys: Some(1000), + prefix: Some("incoming/".into()), + ..Default::default() + }; let mails_res = s3.list_objects_v2(lor).await?; for object in mails_res.contents.unwrap_or_default() { @@ -178,9 +180,11 @@ async fn move_incoming_message( let object_key = format!("incoming/{}", id); // 1. Fetch message from S3 - let mut gor = GetObjectRequest::default(); - gor.bucket = user.creds.storage.bucket.clone(); - gor.key = object_key.clone(); + let gor = GetObjectRequest { + bucket: user.creds.storage.bucket.clone(), + key: object_key.clone(), + ..Default::default() + }; let get_result = s3.get_object(gor).await?; // 1.a decrypt message key from headers @@ -218,9 +222,11 @@ async fn move_incoming_message( .await?; // 3 delete from incoming - let mut dor = DeleteObjectRequest::default(); - dor.bucket = user.creds.storage.bucket.clone(); - dor.key = object_key.clone(); + let dor = DeleteObjectRequest { + bucket: user.creds.storage.bucket.clone(), + key: object_key.clone(), + ..Default::default() + }; s3.delete_object(dor).await?; Ok(()) @@ -441,15 +447,17 @@ impl EncryptedMessage { sodiumoxide::crypto::sealedbox::seal(self.key.as_ref(), &creds.public_key); let key_header = base64::encode(&encrypted_key); - let mut por = PutObjectRequest::default(); - por.bucket = creds.storage.bucket.clone(); - por.key = format!("incoming/{}", gen_ident().to_string()); - por.metadata = Some( + let por = PutObjectRequest { + bucket: creds.storage.bucket.clone(), + key: format!("incoming/{}", gen_ident()), + metadata: Some( [(MESSAGE_KEY.to_string(), key_header)] .into_iter() .collect::>(), - ); - por.body = Some(self.encrypted_body.clone().into()); + ), + body: Some(self.encrypted_body.clone().into()), + ..Default::default() + }; s3_client.put_object(por).await?; // Update watch key to signal new mail diff --git a/src/mail/mailbox.rs b/src/mail/mailbox.rs index a37c9ed..445dcfd 100644 --- a/src/mail/mailbox.rs +++ b/src/mail/mailbox.rs @@ -156,6 +156,7 @@ impl Mailbox { /// Move an email from an other Mailbox to this mailbox /// (use this when possible, as it allows for a certain number of storage optimizations) + #[allow(dead_code)] pub async fn move_from(&self, from: &Mailbox, uuid: UniqueIdent) -> Result<()> { if self.id == from.id { bail!("Cannot copy move same mailbox"); @@ -178,6 +179,8 @@ impl Mailbox { // Non standard but common flags: // https://www.iana.org/assignments/imap-jmap-keywords/imap-jmap-keywords.xhtml struct MailboxInternal { + // 2023-05-15 will probably be used later. + #[allow(dead_code)] id: UniqueIdent, bucket: String, mail_path: String, @@ -256,9 +259,11 @@ impl MailboxInternal { } async fn fetch_full(&self, id: UniqueIdent, message_key: &Key) -> Result> { - let mut gor = GetObjectRequest::default(); - gor.bucket = self.bucket.clone(); - gor.key = format!("{}/{}", self.mail_path, id); + let gor = GetObjectRequest { + bucket: self.bucket.clone(), + key: format!("{}/{}", self.mail_path, id), + ..Default::default() + }; let obj_res = self.s3.get_object(gor).await?; @@ -266,7 +271,7 @@ impl MailboxInternal { let mut buf = Vec::with_capacity(obj_res.content_length.unwrap_or(128) as usize); obj_body.into_async_read().read_to_end(&mut buf).await?; - Ok(cryptoblob::open(&buf, &message_key)?) + cryptoblob::open(&buf, message_key) } // ---- Functions for changing the mailbox ---- @@ -292,17 +297,19 @@ impl MailboxInternal { ident: Option, flags: &[Flag], ) -> Result<(ImapUidvalidity, ImapUid)> { - let ident = ident.unwrap_or_else(|| gen_ident()); + let ident = ident.unwrap_or_else(gen_ident); let message_key = gen_key(); futures::try_join!( async { // Encrypt and save mail body let message_blob = cryptoblob::seal(mail.raw, &message_key)?; - let mut por = PutObjectRequest::default(); - por.bucket = self.bucket.clone(); - por.key = format!("{}/{}", self.mail_path, ident); - por.body = Some(message_blob.into()); + let por = PutObjectRequest { + bucket: self.bucket.clone(), + key: format!("{}/{}", self.mail_path, ident), + body: Some(message_blob.into()), + ..Default::default() + }; self.s3.put_object(por).await?; Ok::<_, anyhow::Error>(()) }, @@ -349,11 +356,13 @@ impl MailboxInternal { futures::try_join!( async { // Copy mail body from previous location - let mut cor = CopyObjectRequest::default(); - cor.bucket = self.bucket.clone(); - cor.key = format!("{}/{}", self.mail_path, ident); - cor.copy_source = format!("{}/{}", self.bucket, s3_key); - cor.metadata_directive = Some("REPLACE".into()); + let cor = CopyObjectRequest { + bucket: self.bucket.clone(), + key: format!("{}/{}", self.mail_path, ident), + copy_source: format!("{}/{}", self.bucket, s3_key), + metadata_directive: Some("REPLACE".into()), + ..Default::default() + }; self.s3.copy_object(cor).await?; Ok::<_, anyhow::Error>(()) }, @@ -393,9 +402,11 @@ impl MailboxInternal { futures::try_join!( async { // Delete mail body from S3 - let mut dor = DeleteObjectRequest::default(); - dor.bucket = self.bucket.clone(); - dor.key = format!("{}/{}", self.mail_path, ident); + let dor = DeleteObjectRequest{ + bucket: self.bucket.clone(), + key: format!("{}/{}", self.mail_path, ident), + ..Default::default() + }; self.s3.delete_object(dor).await?; Ok::<_, anyhow::Error>(()) }, @@ -422,6 +433,8 @@ impl MailboxInternal { Ok(new_id) } + #[allow(dead_code)] + // 2023-05-15 will probably be used later async fn move_from(&mut self, from: &mut MailboxInternal, id: UniqueIdent) -> Result<()> { self.copy_internal(from, id, id).await?; from.delete(id).await?; @@ -450,10 +463,13 @@ impl MailboxInternal { futures::try_join!( async { // Copy mail body from S3 - let mut cor = CopyObjectRequest::default(); - cor.bucket = self.bucket.clone(); - cor.key = format!("{}/{}", self.mail_path, new_id); - cor.copy_source = format!("{}/{}/{}", from.bucket, from.mail_path, source_id); + let cor = CopyObjectRequest{ + bucket: self.bucket.clone(), + key: format!("{}/{}", self.mail_path, new_id), + copy_source: format!("{}/{}/{}", from.bucket, from.mail_path, source_id), + ..Default::default() + }; + self.s3.copy_object(cor).await?; Ok::<_, anyhow::Error>(()) }, @@ -491,7 +507,7 @@ fn dump(uid_index: &Bayou) { s.table.get(ident).cloned().unwrap().1.join(", ") ); } - println!(""); + println!(); } // ---- diff --git a/src/mail/mod.rs b/src/mail/mod.rs index 3b0ae73..80c348a 100644 --- a/src/mail/mod.rs +++ b/src/mail/mod.rs @@ -9,6 +9,8 @@ pub mod user; // Internet Message Format // aka RFC 822 - RFC 2822 - RFC 5322 +// 2023-05-15 don't want to refactor this struct now. +#[allow(clippy::upper_case_acronyms)] pub struct IMF<'a> { raw: &'a [u8], parsed: mail_parser::Message<'a>, diff --git a/src/mail/uidindex.rs b/src/mail/uidindex.rs index 43d6507..956b194 100644 --- a/src/mail/uidindex.rs +++ b/src/mail/uidindex.rs @@ -73,9 +73,9 @@ impl UidIndex { // INTERNAL functions to keep state consistent - fn reg_email(&mut self, ident: UniqueIdent, uid: ImapUid, flags: &Vec) { + fn reg_email(&mut self, ident: UniqueIdent, uid: ImapUid, flags: &[Flag]) { // Insert the email in our table - self.table.insert(ident, (uid, flags.clone())); + self.table.insert(ident, (uid, flags.to_owned())); // Update the indexes/caches self.idx_by_uid.insert(uid, ident); @@ -205,7 +205,7 @@ impl FlagIndex { fn new() -> Self { Self(HashMap::new()) } - fn insert(&mut self, uid: ImapUid, flags: &Vec) { + fn insert(&mut self, uid: ImapUid, flags: &[Flag]) { flags.iter().for_each(|flag| { self.0 .entry(flag.clone()) @@ -213,7 +213,7 @@ impl FlagIndex { .insert(uid); }); } - fn remove(&mut self, uid: ImapUid, flags: &Vec) -> () { + fn remove(&mut self, uid: ImapUid, flags: &[Flag]) { for flag in flags.iter() { if let Some(set) = self.0.get_mut(flag) { set.remove(&uid); diff --git a/src/mail/user.rs b/src/mail/user.rs index d921e6d..44e0081 100644 --- a/src/mail/user.rs +++ b/src/mail/user.rs @@ -257,7 +257,7 @@ impl User { let saved; let (inbox_id, inbox_uidvalidity) = match list.create_mailbox(INBOX) { CreatedMailbox::Created(i, v) => { - self.save_mailbox_list(&list, ct.clone()).await?; + self.save_mailbox_list(list, ct.clone()).await?; saved = true; (i, v) } @@ -334,23 +334,17 @@ impl MailboxList { } fn has_mailbox(&self, name: &str) -> bool { - match self.0.get(name) { - Some(MailboxListEntry { - id_lww: (_, Some(_)), - .. - }) => true, - _ => false, - } + matches!(self.0.get(name), Some(MailboxListEntry { + id_lww: (_, Some(_)), + .. + })) } fn get_mailbox(&self, name: &str) -> Option<(ImapUidvalidity, Option)> { - match self.0.get(name) { - None => None, - Some(MailboxListEntry { - id_lww: (_, mailbox_id), - uidvalidity, - }) => Some((*uidvalidity, *mailbox_id)), - } + self.0.get(name).map(|MailboxListEntry { + id_lww: (_, mailbox_id), + uidvalidity, + }| (*uidvalidity, *mailbox_id)) } /// Ensures mailbox `name` maps to id `id`. diff --git a/src/main.rs b/src/main.rs index 4886679..4ca07d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -121,7 +121,7 @@ async fn main() -> Result<()> { // Abort on panic (same behavior as in Go) std::panic::set_hook(Box::new(|panic_info| { - eprintln!("{}", panic_info.to_string()); + eprintln!("{}", panic_info); eprintln!("{:?}", backtrace::Backtrace::new()); std::process::abort(); })); @@ -292,7 +292,7 @@ fn make_user_secrets(c: UserSecretsArgs) -> UserSecrets { user_secret: c.user_secret, alternate_user_secrets: c .alternate_user_secrets - .split(",") + .split(',') .map(|x| x.trim()) .filter(|x| !x.is_empty()) .map(|x| x.to_string())