diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..0c95a29 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,6 @@ +overflow_delimited_expr = true +unstable_features = true +use_small_heuristics = "Max" +fn_args_density = "Compressed" +max_width = 80 +force_multiline_blocks = true diff --git a/src/database/fs.rs b/src/database/fs.rs index 950d052..3c5a540 100644 --- a/src/database/fs.rs +++ b/src/database/fs.rs @@ -1,16 +1,16 @@ -use std::str; -use std::path::PathBuf; -use std::fs::{File, remove_file, create_dir_all, read_link}; -use std::io::{Write, Read}; +use std::fs::{create_dir_all, read_link, remove_file, File}; +use std::io::{Read, Write}; use std::os::unix::fs::symlink; +use std::path::PathBuf; +use std::str; -use tempfile; use serde_json; +use tempfile; use url; -use database::{Verify, Delete, Database}; -use Result; +use database::{Database, Delete, Verify}; use types::{Email, Fingerprint, KeyID}; +use Result; pub struct Filesystem { base: PathBuf, @@ -28,17 +28,29 @@ impl Filesystem { match meta { Ok(meta) => { if !meta.file_type().is_dir() { - return Err(format!("'{}' exists already and is not a directory", - base.display()).into()); + return Err(format!( + "'{}' exists already and is not a directory", + base.display() + ) + .into()); } if meta.permissions().readonly() { - return Err(format!("Cannot write '{}'", base.display()).into()); + return Err(format!( + "Cannot write '{}'", + base.display() + ) + .into()); } } Err(e) => { - return Err(format!("Cannot read '{}': {}", base.display(),e).into()); + return Err(format!( + "Cannot read '{}': {}", + base.display(), + e + ) + .into()); } } } @@ -52,14 +64,12 @@ impl Filesystem { create_dir_all(base.join("public").join("by-kid"))?; info!("Opened base dir '{}'", base.display()); - Ok(Filesystem{ - base: base, - }) + Ok(Filesystem { base: base }) } fn new_token<'a>(&self, base: &'a str) -> Result<(File, String)> { - use rand::{thread_rng, Rng}; use rand::distributions::Alphanumeric; + use rand::{thread_rng, Rng}; let mut rng = thread_rng(); // samples from [a-zA-Z0-9] @@ -71,7 +81,9 @@ impl Filesystem { Ok((fd, name)) } - fn pop_token<'a>(&self, base: &'a str, token: &'a str) -> Result> { + fn pop_token<'a>( + &self, base: &'a str, token: &'a str, + ) -> Result> { let path = self.base.join(base).join(token); let buf = { let mut fd = File::open(path.clone())?; @@ -101,8 +113,11 @@ impl Database for Filesystem { Ok(name) } - fn compare_and_swap(&self, fpr: &Fingerprint, old: Option<&[u8]>, new: Option<&[u8]>) -> Result { - let target = self.base.join("public").join("by-fpr").join(fpr.to_string()); + fn compare_and_swap( + &self, fpr: &Fingerprint, old: Option<&[u8]>, new: Option<&[u8]>, + ) -> Result { + let target = + self.base.join("public").join("by-fpr").join(fpr.to_string()); let dir = self.base.join("scratch_pad"); match new { @@ -114,15 +129,20 @@ impl Database for Filesystem { tmp.write_all(new)?; if target.is_file() { - if old.is_some() { remove_file(target.clone())?; } - else { return Err(format!("stray file {}", target.display()).into()); } + if old.is_some() { + remove_file(target.clone())?; + } else { + return Err( + format!("stray file {}", target.display()).into() + ); + } } let _ = tmp.persist(&target)?; // fix permissions to 640 if cfg!(unix) { - use std::os::unix::fs::PermissionsExt; use std::fs::{set_permissions, Permissions}; + use std::os::unix::fs::PermissionsExt; let perm = Permissions::from_mode(0o640); set_permissions(target, perm)?; @@ -138,8 +158,11 @@ impl Database for Filesystem { } fn link_email(&self, email: &Email, fpr: &Fingerprint) { - let email = url::form_urlencoded::byte_serialize(email.to_string().as_bytes()).collect::(); - let target = self.base.join("public").join("by-fpr").join(fpr.to_string()); + let email = + url::form_urlencoded::byte_serialize(email.to_string().as_bytes()) + .collect::(); + let target = + self.base.join("public").join("by-fpr").join(fpr.to_string()); let link = self.base.join("public").join("by-email").join(email); if link.exists() { @@ -150,12 +173,18 @@ impl Database for Filesystem { } fn unlink_email(&self, email: &Email, fpr: &Fingerprint) { - let email = url::form_urlencoded::byte_serialize(email.to_string().as_bytes()).collect::(); + let email = + url::form_urlencoded::byte_serialize(email.to_string().as_bytes()) + .collect::(); let link = self.base.join("public").join("by-email").join(email); match read_link(link.clone()) { Ok(target) => { - let expected = self.base.join("public").join("by-fpr").join(fpr.to_string()); + let expected = self + .base + .join("public") + .join("by-fpr") + .join(fpr.to_string()); if target == expected { let _ = remove_file(link); @@ -166,8 +195,10 @@ impl Database for Filesystem { } fn link_kid(&self, kid: &KeyID, fpr: &Fingerprint) { - let target = self.base.join("public").join("by-fpr").join(fpr.to_string()); - let link = self.base.join("public").join("by-kid").join(kid.to_string()); + let target = + self.base.join("public").join("by-fpr").join(fpr.to_string()); + let link = + self.base.join("public").join("by-kid").join(kid.to_string()); if link.exists() { let _ = remove_file(link.clone()); @@ -177,11 +208,16 @@ impl Database for Filesystem { } fn unlink_kid(&self, kid: &KeyID, fpr: &Fingerprint) { - let link = self.base.join("public").join("by-kid").join(kid.to_string()); + let link = + self.base.join("public").join("by-kid").join(kid.to_string()); match read_link(link.clone()) { Ok(target) => { - let expected = self.base.join("public").join("by-fpr").join(fpr.to_string()); + let expected = self + .base + .join("public") + .join("by-fpr") + .join(fpr.to_string()); if target == expected { let _ = remove_file(link); @@ -192,10 +228,14 @@ impl Database for Filesystem { } fn link_fpr(&self, from: &Fingerprint, fpr: &Fingerprint) { - let target = self.base.join("public").join("by-fpr").join(fpr.to_string()); - let link = self.base.join("public").join("by-fpr").join(from.to_string()); + let target = + self.base.join("public").join("by-fpr").join(fpr.to_string()); + let link = + self.base.join("public").join("by-fpr").join(from.to_string()); - if link == target { return; } + if link == target { + return; + } if link.exists() { match link.metadata() { Ok(ref meta) if meta.file_type().is_symlink() => { @@ -209,11 +249,16 @@ impl Database for Filesystem { } fn unlink_fpr(&self, from: &Fingerprint, fpr: &Fingerprint) { - let link = self.base.join("public").join("by-fpr").join(from.to_string()); + let link = + self.base.join("public").join("by-fpr").join(from.to_string()); match read_link(link.clone()) { Ok(target) => { - let expected = self.base.join("public").join("by-fpr").join(fpr.to_string()); + let expected = self + .base + .join("public") + .join("by-fpr") + .join(fpr.to_string()); if target == expected { let _ = remove_file(link); @@ -224,25 +269,26 @@ impl Database for Filesystem { } fn pop_verify_token(&self, token: &str) -> Option { - self.pop_token("verification_tokens", token).ok().and_then(|raw| { - str::from_utf8(&raw).ok().map(|s| s.to_string()) - }).and_then(|s| { - let s = serde_json::from_str(&s); - s.ok() - }) + self.pop_token("verification_tokens", token) + .ok() + .and_then(|raw| str::from_utf8(&raw).ok().map(|s| s.to_string())) + .and_then(|s| { + let s = serde_json::from_str(&s); + s.ok() + }) } fn pop_delete_token(&self, token: &str) -> Option { - self.pop_token("deletion_tokens", token).ok().and_then(|raw| { - str::from_utf8(&raw).ok().map(|s| s.to_string()) - }).and_then(|s| { - serde_json::from_str(&s).ok() - }) + self.pop_token("deletion_tokens", token) + .ok() + .and_then(|raw| str::from_utf8(&raw).ok().map(|s| s.to_string())) + .and_then(|s| serde_json::from_str(&s).ok()) } // XXX: slow fn by_fpr(&self, fpr: &Fingerprint) -> Option> { - let target = self.base.join("public").join("by-fpr").join(fpr.to_string()); + let target = + self.base.join("public").join("by-fpr").join(fpr.to_string()); File::open(target).ok().and_then(|mut fd| { let mut buf = Vec::default(); @@ -258,19 +304,24 @@ impl Database for Filesystem { fn by_email(&self, email: &Email) -> Option> { use std::fs; - let email = url::form_urlencoded::byte_serialize(email.to_string().as_bytes()).collect::(); + let email = + url::form_urlencoded::byte_serialize(email.to_string().as_bytes()) + .collect::(); let path = self.base.join("public").join("by-email").join(email); - fs::canonicalize(path).ok() - .and_then(|p| { - if p.starts_with(&self.base) { - Some(p) - } else { - None - } - }).and_then(|p| { - File::open(p).ok() - }).and_then(|mut fd| { + fs::canonicalize(path) + .ok() + .and_then( + |p| { + if p.starts_with(&self.base) { + Some(p) + } else { + None + } + }, + ) + .and_then(|p| File::open(p).ok()) + .and_then(|mut fd| { let mut buf = Vec::default(); if fd.read_to_end(&mut buf).is_ok() { Some(buf.into_boxed_slice()) @@ -284,18 +335,22 @@ impl Database for Filesystem { fn by_kid(&self, kid: &KeyID) -> Option> { use std::fs; - let path = self.base.join("public").join("by-kid").join(kid.to_string()); + let path = + self.base.join("public").join("by-kid").join(kid.to_string()); - fs::canonicalize(path).ok() - .and_then(|p| { - if p.starts_with(&self.base) { - Some(p) - } else { - None - } - }).and_then(|p| { - File::open(p).ok() - }).and_then(|mut fd| { + fs::canonicalize(path) + .ok() + .and_then( + |p| { + if p.starts_with(&self.base) { + Some(p) + } else { + None + } + }, + ) + .and_then(|p| File::open(p).ok()) + .and_then(|mut fd| { let mut buf = Vec::default(); if fd.read_to_end(&mut buf).is_ok() { Some(buf.into_boxed_slice()) @@ -309,9 +364,9 @@ impl Database for Filesystem { #[cfg(test)] mod tests { use super::*; - use tempfile::TempDir; - use sequoia_openpgp::tpk::TPKBuilder; use database::test; + use sequoia_openpgp::tpk::TPKBuilder; + use tempfile::TempDir; #[test] fn init() { diff --git a/src/database/memory.rs b/src/database/memory.rs index 2c36ef5..e7ad0dc 100644 --- a/src/database/memory.rs +++ b/src/database/memory.rs @@ -1,7 +1,7 @@ -use std::collections::HashMap; use parking_lot::Mutex; +use std::collections::HashMap; -use database::{Verify, Delete, Database}; +use database::{Database, Delete, Verify}; use types::{Email, Fingerprint, KeyID}; use Result; @@ -17,7 +17,7 @@ pub struct Memory { impl Default for Memory { fn default() -> Self { - Memory{ + Memory { fpr: Mutex::new(HashMap::default()), fpr_links: Mutex::new(HashMap::default()), kid: Mutex::new(HashMap::default()), @@ -43,7 +43,9 @@ impl Database for Memory { Ok(token) } - fn compare_and_swap(&self, fpr: &Fingerprint, present: Option<&[u8]>, new: Option<&[u8]>) -> Result { + fn compare_and_swap( + &self, fpr: &Fingerprint, present: Option<&[u8]>, new: Option<&[u8]>, + ) -> Result { let mut fprs = self.fpr.lock(); if fprs.get(fpr).map(|x| &x[..]) == present { @@ -119,8 +121,8 @@ impl Database for Memory { impl Memory { pub fn new_token() -> String { - use rand::{thread_rng, Rng}; use rand::distributions::Alphanumeric; + use rand::{thread_rng, Rng}; let mut rng = thread_rng(); // samples from [a-zA-Z0-9] @@ -132,8 +134,8 @@ impl Memory { #[cfg(test)] mod tests { use super::*; - use sequoia_openpgp::tpk::TPKBuilder; use database::test; + use sequoia_openpgp::tpk::TPKBuilder; #[test] fn new() { diff --git a/src/database/mod.rs b/src/database/mod.rs index 5e1ef52..84dea9b 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -1,12 +1,15 @@ -use std::io::Cursor; use std::convert::TryFrom; +use std::io::Cursor; use std::result; +use sequoia_openpgp::{ + constants::SignatureType, packet::Signature, packet::UserID, parse::Parse, + Packet, PacketPile, TPK, +}; +use serde::{Deserialize, Deserializer, Serializer}; use time; -use sequoia_openpgp::{packet::Signature, TPK, packet::UserID, Packet, PacketPile, constants::SignatureType, parse::Parse}; +use types::{Email, Fingerprint, KeyID}; use Result; -use types::{Fingerprint, Email, KeyID}; -use serde::{Serializer, Deserializer, Deserialize}; mod fs; pub use self::fs::Filesystem; @@ -18,7 +21,7 @@ pub use self::poly::Polymorphic; #[cfg(test)] mod test; -#[derive(Serialize,Deserialize,Clone,Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct Verify { created: i64, #[serde(serialize_with = "as_base64", deserialize_with = "from_base64")] @@ -28,36 +31,45 @@ pub struct Verify { } fn as_base64(d: &Box<[u8]>, serializer: S) -> result::Result - where S: Serializer +where + S: Serializer, { serializer.serialize_str(&base64::encode(&d)) } fn from_base64<'de, D>(deserializer: D) -> result::Result, D::Error> - where D: Deserializer<'de> +where + D: Deserializer<'de>, { use serde::de::Error; String::deserialize(deserializer) - .and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string()))) + .and_then(|string| { + base64::decode(&string) + .map_err(|err| Error::custom(err.to_string())) + }) .map(|bytes| bytes.into_boxed_slice()) } impl Verify { - pub fn new(uid: &UserID, sig: &[&Signature], fpr: Fingerprint) -> Result { + pub fn new( + uid: &UserID, sig: &[&Signature], fpr: Fingerprint, + ) -> Result { use sequoia_openpgp::serialize::Serialize; let mut cur = Cursor::new(Vec::default()); - let res: Result<()> = uid.serialize(&mut cur) + let res: Result<()> = uid + .serialize(&mut cur) .map_err(|e| format!("sequoia_openpgp: {}", e).into()); res?; for s in sig { - let res: Result<()> = s.serialize(&mut cur) + let res: Result<()> = s + .serialize(&mut cur) .map_err(|e| format!("sequoia_openpgp: {}", e).into()); res?; } - Ok(Verify{ + Ok(Verify { created: time::now().to_timespec().sec, packets: cur.into_inner().into(), fpr: fpr, @@ -66,18 +78,15 @@ impl Verify { } } -#[derive(Serialize,Deserialize,Clone,Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct Delete { created: i64, - fpr: Fingerprint + fpr: Fingerprint, } impl Delete { pub fn new(fpr: Fingerprint) -> Self { - Delete{ - created: time::now().to_timespec().sec, - fpr: fpr - } + Delete { created: time::now().to_timespec().sec, fpr: fpr } } } @@ -85,7 +94,9 @@ pub trait Database: Sync + Send { fn new_verify_token(&self, payload: Verify) -> Result; fn new_delete_token(&self, payload: Delete) -> Result; - fn compare_and_swap(&self, fpr: &Fingerprint, present: Option<&[u8]>, new: Option<&[u8]>) -> Result; + fn compare_and_swap( + &self, fpr: &Fingerprint, present: Option<&[u8]>, new: Option<&[u8]>, + ) -> Result; fn link_email(&self, email: &Email, fpr: &Fingerprint); fn unlink_email(&self, email: &Email, fpr: &Fingerprint); @@ -106,30 +117,38 @@ pub trait Database: Sync + Send { fn by_email(&self, email: &Email) -> Option>; fn strip_userids(tpk: TPK) -> Result { - let pile = tpk.to_packet_pile().into_children().filter(|pkt| { - match pkt { - &Packet::PublicKey(_) | &Packet::PublicSubkey(_) => true, - &Packet::Signature(ref sig) => { - sig.sigtype() == SignatureType::DirectKey - || sig.sigtype() == SignatureType::SubkeyBinding + let pile = tpk + .to_packet_pile() + .into_children() + .filter(|pkt| { + match pkt { + &Packet::PublicKey(_) | &Packet::PublicSubkey(_) => true, + &Packet::Signature(ref sig) => { + sig.sigtype() == SignatureType::DirectKey + || sig.sigtype() == SignatureType::SubkeyBinding + } + _ => false, } - _ => false, - } - }).collect::>(); + }) + .collect::>(); TPK::from_packet_pile(PacketPile::from_packets(pile)) .map_err(|e| format!("openpgp: {}", e).into()) } fn tpk_into_bytes(tpk: &TPK) -> Result> { - use std::io::Cursor; use sequoia_openpgp::serialize::Serialize; + use std::io::Cursor; let mut cur = Cursor::new(Vec::default()); - tpk.serialize(&mut cur).map(|_| cur.into_inner()).map_err(|e| format!("{}", e).into()) + tpk.serialize(&mut cur) + .map(|_| cur.into_inner()) + .map_err(|e| format!("{}", e).into()) } - fn link_subkeys(&self, fpr: &Fingerprint, subkeys: Vec) -> Result<()> { + fn link_subkeys( + &self, fpr: &Fingerprint, subkeys: Vec, + ) -> Result<()> { // link (subkey) kid & and subkey fpr self.link_kid(&fpr.clone().into(), &fpr); @@ -164,22 +183,28 @@ pub trait Database: Sync + Send { RevocationStatus::CouldBe(_) | RevocationStatus::Revoked(_) => { revoked_uids.push(email); } - RevocationStatus::NotAsFarAsWeKnow if self.by_email(&email).is_none() => { + RevocationStatus::NotAsFarAsWeKnow + if self.by_email(&email).is_none() => + { let payload = Verify::new( uid.userid(), &uid.selfsigs().collect::>(), - fpr.clone())?; + fpr.clone(), + )?; active_uids.push((email, self.new_verify_token(payload)?)); } _ => {} } } - let subkeys = tpk.subkeys().map(|s| s.subkey().fingerprint()).collect::>(); + let subkeys = + tpk.subkeys().map(|s| s.subkey().fingerprint()).collect::>(); tpk = Self::strip_userids(tpk)?; - for _ in 0..100 /* while cas failed */ { + for _ in 0..100 + /* while cas failed */ + { // merge or update key db match self.by_fpr(&fpr).map(|x| x.to_vec()) { Some(old) => { @@ -206,7 +231,11 @@ pub trait Database: Sync + Send { } } - error!("Compare-and-swap of {} failed {} times in a row. Aborting.", fpr.to_string(), 100); + error!( + "Compare-and-swap of {} failed {} times in a row. Aborting.", + fpr.to_string(), + 100 + ); Err("Database update failed".into()) } @@ -217,19 +246,29 @@ pub trait Database: Sync + Send { // cas(tpk, merged) // } // } - fn verify_token(&self, token: &str) -> Result> { + fn verify_token( + &self, token: &str, + ) -> Result> { match self.pop_verify_token(token) { - Some(Verify{ created, packets, fpr, email }) => { + Some(Verify { created, packets, fpr, email }) => { let now = time::now().to_timespec().sec; - if created > now || now - created > 3 * 3600 { return Ok(None); } + if created > now || now - created > 3 * 3600 { + return Ok(None); + } - loop /* while cas falied */ { + loop + /* while cas falied */ + { match self.by_fpr(&fpr).map(|x| x.to_vec()) { Some(old) => { let mut new = old.clone(); new.extend(packets.into_iter()); - if self.compare_and_swap(&fpr, Some(&old), Some(&new))? { + if self.compare_and_swap( + &fpr, + Some(&old), + Some(&new), + )? { self.link_email(&email, &fpr); return Ok(Some((email.clone(), fpr.clone()))); } @@ -244,7 +283,9 @@ pub trait Database: Sync + Send { } } - fn request_deletion(&self, fpr: Fingerprint) -> Result<(String, Vec)> { + fn request_deletion( + &self, fpr: Fingerprint, + ) -> Result<(String, Vec)> { match self.by_fpr(&fpr) { Some(tpk) => { let payload = Delete::new(fpr); @@ -252,12 +293,17 @@ pub trait Database: Sync + Send { let tpk = match TPK::from_bytes(&tpk) { Ok(tpk) => tpk, Err(e) => { - return Err(format!("Failed to parse TPK: {:?}", e).into()); + return Err( + format!("Failed to parse TPK: {:?}", e).into() + ); } }; - let emails = tpk.userids().filter_map(|uid| { - Email::try_from(uid.userid().clone()).ok() - }).collect::>(); + let emails = tpk + .userids() + .filter_map(|uid| { + Email::try_from(uid.userid().clone()).ok() + }) + .collect::>(); Ok((tok, emails)) } @@ -275,9 +321,11 @@ pub trait Database: Sync + Send { // } fn confirm_deletion(&self, token: &str) -> Result { match self.pop_delete_token(token) { - Some(Delete{ created, fpr }) => { + Some(Delete { created, fpr }) => { let now = time::now().to_timespec().sec; - if created > now || now - created > 3 * 3600 { return Ok(false); } + if created > now || now - created > 3 * 3600 { + return Ok(false); + } loop { match self.by_fpr(&fpr).map(|x| x.to_vec()) { @@ -285,15 +333,26 @@ pub trait Database: Sync + Send { let tpk = match TPK::from_bytes(&old) { Ok(tpk) => tpk, Err(e) => { - return Err(format!("Failed to parse old TPK: {:?}", e).into()); + return Err(format!( + "Failed to parse old TPK: {:?}", + e + ) + .into()); } }; for uid in tpk.userids() { - self.unlink_email(&Email::try_from(uid.userid().clone())?, &fpr); + self.unlink_email( + &Email::try_from(uid.userid().clone())?, + &fpr, + ); } - while !self.compare_and_swap(&fpr, Some(&old), None)? {} + while !self.compare_and_swap( + &fpr, + Some(&old), + None, + )? {} return Ok(true); } None => { diff --git a/src/database/poly.rs b/src/database/poly.rs index 7f3de71..147f040 100644 --- a/src/database/poly.rs +++ b/src/database/poly.rs @@ -1,6 +1,6 @@ +use database::{Database, Delete, Filesystem, Memory, Verify}; use errors::Result; -use database::{Verify, Delete, Database, Filesystem, Memory}; -use types::{Fingerprint, Email, KeyID}; +use types::{Email, Fingerprint, KeyID}; pub enum Polymorphic { Memory(Memory), @@ -22,10 +22,16 @@ impl Database for Polymorphic { } } - fn compare_and_swap(&self, fpr: &Fingerprint, present: Option<&[u8]>, new: Option<&[u8]>) -> Result { + fn compare_and_swap( + &self, fpr: &Fingerprint, present: Option<&[u8]>, new: Option<&[u8]>, + ) -> Result { match self { - &Polymorphic::Memory(ref db) => db.compare_and_swap(fpr, present, new), - &Polymorphic::Filesystem(ref db) => db.compare_and_swap(fpr, present, new), + &Polymorphic::Memory(ref db) => { + db.compare_and_swap(fpr, present, new) + } + &Polymorphic::Filesystem(ref db) => { + db.compare_and_swap(fpr, present, new) + } } } diff --git a/src/database/test.rs b/src/database/test.rs index a40120f..bf9c448 100644 --- a/src/database/test.rs +++ b/src/database/test.rs @@ -9,7 +9,7 @@ // pub & verify // req del one // fetch by uid & fpr -// confirm +// confirm // fetch by uid & fpr // confirm again // fetch by uid & fpr @@ -19,8 +19,11 @@ use std::str::FromStr; use database::Database; use sequoia_openpgp::tpk::{TPKBuilder, UserIDBinding}; -use sequoia_openpgp::{Packet, packet::UserID, TPK, PacketPile, parse::Parse, RevocationStatus, constants::ReasonForRevocation, constants::SignatureType}; -use types::{KeyID, Email, Fingerprint}; +use sequoia_openpgp::{ + constants::ReasonForRevocation, constants::SignatureType, packet::UserID, + parse::Parse, Packet, PacketPile, RevocationStatus, TPK, +}; +use types::{Email, Fingerprint, KeyID}; pub fn test_uid_verification(db: &mut D) { let str_uid1 = "Test A "; @@ -28,7 +31,9 @@ pub fn test_uid_verification(db: &mut D) { let tpk = TPKBuilder::default() .add_userid(str_uid1) .add_userid(str_uid2) - .generate().unwrap().0; + .generate() + .unwrap() + .0; let mut uid1 = UserID::new(); let mut uid2 = UserID::new(); @@ -73,7 +78,9 @@ pub fn test_uid_verification(db: &mut D) { let uid = key.userids().next().unwrap().userid().clone(); assert!((uid == uid1) ^ (uid == uid2)); - let email = Email::from_str(&String::from_utf8(uid.userid().to_vec()).unwrap()).unwrap(); + let email = + Email::from_str(&String::from_utf8(uid.userid().to_vec()).unwrap()) + .unwrap(); assert_eq!(db.by_email(&email).unwrap(), raw); if email1 == email { @@ -100,7 +107,9 @@ pub fn test_uid_verification(db: &mut D) { let uid = key.userids().next().unwrap().userid().clone(); assert!((uid == uid1) ^ (uid == uid2)); - let email = Email::from_str(&String::from_utf8(uid.userid().to_vec()).unwrap()).unwrap(); + let email = + Email::from_str(&String::from_utf8(uid.userid().to_vec()).unwrap()) + .unwrap(); assert_eq!(db.by_email(&email).unwrap(), raw); if email1 == email { @@ -129,27 +138,34 @@ pub fn test_uid_verification(db: &mut D) { assert_eq!(db.by_email(&email1).unwrap(), raw); assert_eq!(db.by_email(&email2).unwrap(), raw); - assert!(((myuid1 == uid1) & (myuid2 == uid2)) ^ ((myuid1 == uid2) & (myuid2 == uid1))); + assert!( + ((myuid1 == uid1) & (myuid2 == uid2)) + ^ ((myuid1 == uid2) & (myuid2 == uid1)) + ); } // upload again - assert_eq!(db.merge_or_publish(tpk.clone()).unwrap(), Vec::<(Email,String)>::default()); + assert_eq!( + db.merge_or_publish(tpk.clone()).unwrap(), + Vec::<(Email, String)>::default() + ); // publish w/ one uid less { - let packets = tpk.clone() - .to_packet_pile() - .into_children() - .filter(|pkt| { + let packets = + tpk.clone().to_packet_pile().into_children().filter(|pkt| { match pkt { Packet::UserID(ref uid) => *uid != uid1, - _ => true + _ => true, } }); let pile = PacketPile::from_packets(packets.collect()); let short_tpk = TPK::from_packet_pile(pile).unwrap(); - assert_eq!(db.merge_or_publish(short_tpk.clone()).unwrap(), Vec::<(Email,String)>::default()); + assert_eq!( + db.merge_or_publish(short_tpk.clone()).unwrap(), + Vec::<(Email, String)>::default() + ); // fetch by fpr let raw = db.by_fpr(&fpr).unwrap(); @@ -164,20 +180,25 @@ pub fn test_uid_verification(db: &mut D) { assert_eq!(db.by_email(&email1).unwrap(), raw); assert_eq!(db.by_email(&email2).unwrap(), raw); - assert!(((myuid1 == uid1) & (myuid2 == uid2)) ^ ((myuid1 == uid2) & (myuid2 == uid1))); + assert!( + ((myuid1 == uid1) & (myuid2 == uid2)) + ^ ((myuid1 == uid2) & (myuid2 == uid1)) + ); } // publish w/one uid more { - let mut packets = tpk.clone() + let mut packets = tpk + .clone() .to_packet_pile() .into_children() .filter(|pkt| { match pkt { Packet::UserID(ref uid) => *uid != uid1, - _ => true + _ => true, } - }).collect::>(); + }) + .collect::>(); let str_uid3 = "Test C "; let mut uid3 = UserID::new(); uid3.set_userid_from_bytes(str_uid3.as_bytes()); @@ -187,7 +208,8 @@ pub fn test_uid_verification(db: &mut D) { let bind = UserIDBinding::new(key, uid3.clone(), key).unwrap(); packets.push(Packet::UserID(uid3.clone())); - packets.push(Packet::Signature(bind.selfsigs().next().unwrap().clone())); + packets + .push(Packet::Signature(bind.selfsigs().next().unwrap().clone())); let pile = PacketPile::from_packets(packets); let ext_tpk = TPK::from_packet_pile(pile).unwrap(); @@ -208,7 +230,10 @@ pub fn test_uid_verification(db: &mut D) { assert_eq!(db.by_email(&email1).unwrap(), raw); assert_eq!(db.by_email(&email2).unwrap(), raw); - assert!(((myuid1 == uid1) & (myuid2 == uid2)) ^ ((myuid1 == uid2) & (myuid2 == uid1))); + assert!( + ((myuid1 == uid1) & (myuid2 == uid2)) + ^ ((myuid1 == uid2) & (myuid2 == uid1)) + ); assert!(db.by_email(&email3).is_none()); } } @@ -219,7 +244,9 @@ pub fn test_uid_deletion(db: &mut D) { let tpk = TPKBuilder::default() .add_userid(str_uid1) .add_userid(str_uid2) - .generate().unwrap().0; + .generate() + .unwrap() + .0; let mut uid1 = UserID::new(); let mut uid2 = UserID::new(); @@ -241,7 +268,7 @@ pub fn test_uid_deletion(db: &mut D) { // req. deletion let del = db.request_deletion(fpr.clone()).unwrap().0; - // check it's still there + // check it's still there { // fetch by fpr let raw = db.by_fpr(&fpr).unwrap(); @@ -256,7 +283,10 @@ pub fn test_uid_deletion(db: &mut D) { assert_eq!(db.by_email(&email1).unwrap(), raw); assert_eq!(db.by_email(&email2).unwrap(), raw); - assert!(((myuid1 == uid1) & (myuid2 == uid2)) ^ ((myuid1 == uid2) & (myuid2 == uid1))); + assert!( + ((myuid1 == uid1) & (myuid2 == uid2)) + ^ ((myuid1 == uid2) & (myuid2 == uid1)) + ); } // confirm deletion @@ -281,13 +311,21 @@ pub fn test_subkey_lookup(db: &mut D) { .add_userid("Testy ") .add_signing_subkey() .add_encryption_subkey() - .generate().unwrap().0; + .generate() + .unwrap() + .0; // upload key let _ = db.merge_or_publish(tpk.clone()).unwrap(); let primary_fpr = Fingerprint::try_from(tpk.fingerprint()).unwrap(); - let sub1_fpr = Fingerprint::try_from(tpk.subkeys().next().map(|x| x.subkey().fingerprint()).unwrap()).unwrap(); - let sub2_fpr = Fingerprint::try_from(tpk.subkeys().skip(1).next().map(|x| x.subkey().fingerprint()).unwrap()).unwrap(); + let sub1_fpr = Fingerprint::try_from( + tpk.subkeys().next().map(|x| x.subkey().fingerprint()).unwrap(), + ) + .unwrap(); + let sub2_fpr = Fingerprint::try_from( + tpk.subkeys().skip(1).next().map(|x| x.subkey().fingerprint()).unwrap(), + ) + .unwrap(); let raw1 = db.by_fpr(&primary_fpr).unwrap(); let raw2 = db.by_fpr(&sub1_fpr).unwrap(); @@ -302,13 +340,21 @@ pub fn test_kid_lookup(db: &mut D) { .add_userid("Testy ") .add_signing_subkey() .add_encryption_subkey() - .generate().unwrap().0; + .generate() + .unwrap() + .0; // upload key let _ = db.merge_or_publish(tpk.clone()).unwrap(); let primary_kid = KeyID::try_from(tpk.fingerprint()).unwrap(); - let sub1_kid = KeyID::try_from(tpk.subkeys().next().map(|x| x.subkey().fingerprint()).unwrap()).unwrap(); - let sub2_kid = KeyID::try_from(tpk.subkeys().skip(1).next().map(|x| x.subkey().fingerprint()).unwrap()).unwrap(); + let sub1_kid = KeyID::try_from( + tpk.subkeys().next().map(|x| x.subkey().fingerprint()).unwrap(), + ) + .unwrap(); + let sub2_kid = KeyID::try_from( + tpk.subkeys().skip(1).next().map(|x| x.subkey().fingerprint()).unwrap(), + ) + .unwrap(); let raw1 = db.by_kid(&primary_kid).unwrap(); let raw2 = db.by_kid(&sub1_kid).unwrap(); @@ -326,7 +372,9 @@ pub fn test_uid_revocation(db: &mut D) { let tpk = TPKBuilder::default() .add_userid(str_uid1) .add_userid(str_uid2) - .generate().unwrap().0; + .generate() + .unwrap() + .0; let mut uid1 = UserID::new(); let mut uid2 = UserID::new(); @@ -356,9 +404,12 @@ pub fn test_uid_revocation(db: &mut D) { assert_eq!(RevocationStatus::NotAsFarAsWeKnow, uid.revoked(None)); let mut keypair = tpk.primary().clone().into_keypair().unwrap(); - uid.revoke(&mut keypair, - ReasonForRevocation::UIDRetired, - b"It was the maid :/").unwrap() + uid.revoke( + &mut keypair, + ReasonForRevocation::UIDRetired, + b"It was the maid :/", + ) + .unwrap() }; assert_eq!(sig.sigtype(), SignatureType::CertificateRevocation); let tpk = tpk.merge_packets(vec![sig.to_packet()]).unwrap(); diff --git a/src/mail.rs b/src/mail.rs index 20ec2ed..f792cbc 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -1,32 +1,37 @@ use handlebars::Handlebars; -use lettre::{SendmailTransport, EmailTransport}; +use lettre::{EmailTransport, SendmailTransport}; use lettre_email::EmailBuilder; use serde::Serialize; -use Result; use types::Email; +use Result; #[derive(Serialize, Clone)] -pub struct Context{ +pub struct Context { pub token: String, pub userid: String, pub domain: String, } -fn send_mail(to: &Email, subject: &str, mail_templates: &Handlebars, - template: &str, from: &str, ctx: T) - -> Result<()> where T: Serialize + Clone +fn send_mail( + to: &Email, subject: &str, mail_templates: &Handlebars, template: &str, + from: &str, ctx: T, +) -> Result<()> +where + T: Serialize + Clone, { let tmpl_html = format!("{}-html", template); let tmpl_txt = format!("{}-txt", template); let (html, txt) = { - if let (Ok(inner_html), Ok(inner_txt)) = - (mail_templates.render(&tmpl_html, &ctx), mail_templates.render(&tmpl_txt, &ctx)) { - (Some(inner_html), Some(inner_txt)) - } else { - (None, None) - } + if let (Ok(inner_html), Ok(inner_txt)) = ( + mail_templates.render(&tmpl_html, &ctx), + mail_templates.render(&tmpl_txt, &ctx), + ) { + (Some(inner_html), Some(inner_txt)) + } else { + (None, None) + } }; let email = EmailBuilder::new() @@ -35,38 +40,52 @@ fn send_mail(to: &Email, subject: &str, mail_templates: &Handlebars, .subject(subject) .alternative( html.ok_or("Email template failed to render")?, - txt.ok_or("Email template failed to render")?) - .build().unwrap(); + txt.ok_or("Email template failed to render")?, + ) + .build() + .unwrap(); let mut sender = SendmailTransport::new(); sender.send(&email)?; Ok(()) } -pub fn send_verification_mail(userid: &Email, token: &str, mail_templates: &Handlebars, - domain: &str, from: &str) --> Result<()> -{ - let ctx = Context{ +pub fn send_verification_mail( + userid: &Email, token: &str, mail_templates: &Handlebars, domain: &str, + from: &str, +) -> Result<()> { + let ctx = Context { token: token.to_string(), userid: userid.to_string(), domain: domain.to_string(), }; - send_mail(userid, "Please verify your email address", mail_templates, - "verify", from, ctx) + send_mail( + userid, + "Please verify your email address", + mail_templates, + "verify", + from, + ctx, + ) } -pub fn send_confirmation_mail(userid: &Email, token: &str, mail_templates: &Handlebars, - domain: &str, from: &str) --> Result<()> -{ - let ctx = Context{ +pub fn send_confirmation_mail( + userid: &Email, token: &str, mail_templates: &Handlebars, domain: &str, + from: &str, +) -> Result<()> { + let ctx = Context { token: token.to_string(), userid: userid.to_string(), domain: domain.to_string(), }; - send_mail(userid, "Please confirm deletion of your key", mail_templates, - "confirm", from, ctx) + send_mail( + userid, + "Please confirm deletion of your key", + mail_templates, + "confirm", + from, + ctx, + ) } diff --git a/src/main.rs b/src/main.rs index 91e7cc8..fed8cda 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,36 +3,40 @@ #![feature(try_from)] extern crate serde; -#[macro_use] extern crate serde_derive; +#[macro_use] +extern crate serde_derive; extern crate serde_json; +extern crate hex; extern crate time; extern crate url; -extern crate hex; -#[macro_use] extern crate rocket; -extern crate rocket_contrib; +#[macro_use] +extern crate rocket; extern crate multipart; +extern crate rocket_contrib; extern crate sequoia_openpgp; -#[macro_use] extern crate error_chain; -#[macro_use] extern crate log; -extern crate rand; -extern crate tempfile; -extern crate parking_lot; -extern crate structopt; +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; +extern crate base64; +extern crate handlebars; extern crate lettre; extern crate lettre_email; -extern crate handlebars; -extern crate base64; +extern crate parking_lot; +extern crate rand; +extern crate structopt; +extern crate tempfile; -mod web; mod database; -mod types; mod mail; +mod types; +mod web; mod errors { - error_chain!{ + error_chain! { foreign_links { Fmt(::std::fmt::Error); Io(::std::io::Error); @@ -52,7 +56,10 @@ use std::path::PathBuf; use structopt::StructOpt; #[derive(Debug, StructOpt)] -#[structopt(name = "garbage", about = "Garbage Pile - The verifying OpenPGP key server.")] +#[structopt( + name = "garbage", + about = "Garbage Pile - The verifying OpenPGP key server." +)] pub struct Opt { /// More verbose output. Disabled when running as daemon. #[structopt(short = "v", long = "verbose")] @@ -69,9 +76,13 @@ pub struct Opt { /// FQDN of the server. Used in templates. #[structopt(short = "D", long = "domain", default_value = "localhost")] domain: String, - #[structopt(short = "F", long = "from", default_value = "noreply@localhost")] + #[structopt( + short = "F", + long = "from", + default_value = "noreply@localhost" + )] from: String, - } +} fn main() { use database::{Filesystem, Polymorphic}; diff --git a/src/types.rs b/src/types.rs index dcd1abb..ea16856 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,12 +1,12 @@ -use std::str::FromStr; use std::convert::TryFrom; use std::result; +use std::str::FromStr; use sequoia_openpgp::{self, packet::UserID}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use {Error, Result}; -use serde::{Serialize, Serializer, Deserializer, Deserialize}; -#[derive(Serialize,Deserialize,Clone,Debug,Hash,PartialEq,Eq)] +#[derive(Serialize, Deserialize, Clone, Debug, Hash, PartialEq, Eq)] pub struct Email(String); impl TryFrom for Email { @@ -20,7 +20,9 @@ impl TryFrom for Email { } impl ToString for Email { - fn to_string(&self) -> String { self.0.clone() } + fn to_string(&self) -> String { + self.0.clone() + } } impl FromStr for Email { @@ -37,7 +39,7 @@ impl FromStr for Email { } } -#[derive(Clone,Debug,Hash,PartialEq,Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct Fingerprint([u8; 20]); impl TryFrom for Fingerprint { @@ -46,7 +48,9 @@ impl TryFrom for Fingerprint { fn try_from(fpr: sequoia_openpgp::Fingerprint) -> Result { match fpr { sequoia_openpgp::Fingerprint::V4(a) => Ok(Fingerprint(a)), - sequoia_openpgp::Fingerprint::Invalid(_) => Err("invalid fingerprint".into()), + sequoia_openpgp::Fingerprint::Invalid(_) => { + Err("invalid fingerprint".into()) + } } } } @@ -59,7 +63,8 @@ impl ToString for Fingerprint { impl Serialize for Fingerprint { fn serialize(&self, serializer: S) -> result::Result - where S: Serializer + where + S: Serializer, { serializer.serialize_str(&self.to_string()) } @@ -67,11 +72,14 @@ impl Serialize for Fingerprint { impl<'de> Deserialize<'de> for Fingerprint { fn deserialize(deserializer: D) -> result::Result - where D: Deserializer<'de> + where + D: Deserializer<'de>, { use serde::de::Error; - String::deserialize(deserializer) - .and_then(|string| Self::from_str(&string).map_err(|err| Error::custom(err.to_string()))) + String::deserialize(deserializer).and_then(|string| { + Self::from_str(&string) + .map_err(|err| Error::custom(err.to_string())) + }) } } @@ -95,7 +103,7 @@ impl FromStr for Fingerprint { } } -#[derive(Serialize,Deserialize,Clone,Debug,Hash,PartialEq,Eq)] +#[derive(Serialize, Deserialize, Clone, Debug, Hash, PartialEq, Eq)] pub struct KeyID([u8; 8]); impl TryFrom for KeyID { @@ -104,7 +112,9 @@ impl TryFrom for KeyID { fn try_from(fpr: sequoia_openpgp::Fingerprint) -> Result { match fpr { sequoia_openpgp::Fingerprint::V4(a) => Ok(Fingerprint(a).into()), - sequoia_openpgp::Fingerprint::Invalid(_) => Err("invalid fingerprint".into()), + sequoia_openpgp::Fingerprint::Invalid(_) => { + Err("invalid fingerprint".into()) + } } } } diff --git a/src/web/mod.rs b/src/web/mod.rs index 4ba85a6..c7883a5 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -1,10 +1,10 @@ use rocket; -use rocket::{State, Outcome}; -use rocket::http::Status; -use rocket::request::{self, Request, FromRequest}; -use rocket::response::status::Custom; -use rocket::response::{Response, NamedFile}; use rocket::fairing::AdHoc; +use rocket::http::Status; +use rocket::request::{self, FromRequest, Request}; +use rocket::response::status::Custom; +use rocket::response::{NamedFile, Response}; +use rocket::{Outcome, State}; use rocket_contrib::templates::Template; use handlebars::Handlebars; @@ -12,21 +12,21 @@ use std::path::{Path, PathBuf}; mod upload; -use database::{Polymorphic, Database}; -use types::{Fingerprint, Email, KeyID}; +use database::{Database, Polymorphic}; use errors::Result; +use types::{Email, Fingerprint, KeyID}; use Opt; -use std::str::FromStr; use std::result; +use std::str::FromStr; mod queries { - use types::{Fingerprint, Email}; + use types::{Email, Fingerprint}; #[derive(Debug)] pub enum Hkp { - Fingerprint{ fpr: Fingerprint, index: bool }, - Email{ email: Email, index: bool }, + Fingerprint { fpr: Fingerprint, index: bool }, + Email { email: Email, index: bool }, } } @@ -58,35 +58,46 @@ pub struct MailTemplates(Handlebars); impl<'a, 'r> FromRequest<'a, 'r> for queries::Hkp { type Error = (); - fn from_request(request: &'a Request<'r>) -> request::Outcome { + fn from_request( + request: &'a Request<'r>, + ) -> request::Outcome { use rocket::request::FormItems; use std::collections::HashMap; let query = request.uri().query().unwrap_or(""); - let fields = FormItems::from(query).map(|item| { + let fields = FormItems::from(query) + .map(|item| { + let (k, v) = item.key_value(); - let (k, v) = item.key_value(); + let key = k.url_decode().unwrap_or_default(); + let value = v.url_decode().unwrap_or_default(); + (key, value) + }) + .collect::>(); - let key = k.url_decode().unwrap_or_default(); - let value = v.url_decode().unwrap_or_default(); - (key, value) - }).collect::>(); - - if fields.len() >= 2 && fields.get("op").map(|x| x == "get" || x == "index").unwrap_or(false) { - let index = fields.get("op").map(|x| x == "index") - .unwrap_or(false); + if fields.len() >= 2 + && fields + .get("op") + .map(|x| x == "get" || x == "index") + .unwrap_or(false) + { + let index = fields.get("op").map(|x| x == "index").unwrap_or(false); let search = fields.get("search").cloned().unwrap_or_default(); let maybe_fpr = Fingerprint::from_str(&search); if let Ok(fpr) = maybe_fpr { - Outcome::Success(queries::Hkp::Fingerprint{ - fpr: fpr, index: index + Outcome::Success(queries::Hkp::Fingerprint { + fpr: fpr, + index: index, }) } else { match Email::from_str(&search) { - Ok(email) => Outcome::Success(queries::Hkp::Email{ - email: email, index: index - }), + Ok(email) => { + Outcome::Success(queries::Hkp::Email { + email: email, + index: index, + }) + } Err(_) => Outcome::Failure((Status::BadRequest, ())), } } @@ -96,11 +107,11 @@ impl<'a, 'r> FromRequest<'a, 'r> for queries::Hkp { } } -fn key_to_response<'a,'b>(bytes: &'a[u8]) -> Response<'b> { - use std::io::Write; - use sequoia_openpgp::armor::{Writer, Kind}; +fn key_to_response<'a, 'b>(bytes: &'a [u8]) -> Response<'b> { + use rocket::http::{ContentType, Status}; + use sequoia_openpgp::armor::{Kind, Writer}; use std::io::Cursor; - use rocket::http::{Status, ContentType}; + use std::io::Write; let key = || -> Result { let mut buffer = Vec::default(); @@ -113,27 +124,27 @@ fn key_to_response<'a,'b>(bytes: &'a[u8]) -> Response<'b> { }(); match key { - Ok(s) => + Ok(s) => { Response::build() - .status(Status::Ok) - .header(ContentType::new("application", "pgp-keys")) - .sized_body(Cursor::new(s)) - .finalize(), - Err(_) => + .status(Status::Ok) + .header(ContentType::new("application", "pgp-keys")) + .sized_body(Cursor::new(s)) + .finalize() + } + Err(_) => { Response::build() - .status(Status::InternalServerError) - .header(ContentType::Plain) - .sized_body(Cursor::new("Failed to ASCII armor key")) - .finalize(), + .status(Status::InternalServerError) + .header(ContentType::Plain) + .sized_body(Cursor::new("Failed to ASCII armor key")) + .finalize() + } } } #[get("/by-fpr/")] -fn by_fpr(db: rocket::State, fpr: String) - -> Response -{ +fn by_fpr(db: rocket::State, fpr: String) -> Response { + use rocket::http::{ContentType, Status}; use std::io::Cursor; - use rocket::http::{Status, ContentType}; let maybe_key = match Fingerprint::from_str(&fpr) { Ok(ref fpr) => db.by_fpr(fpr), @@ -142,21 +153,20 @@ fn by_fpr(db: rocket::State, fpr: String) match maybe_key { Some(ref bytes) => key_to_response(bytes), - None => + None => { Response::build() - .status(Status::NotFound) - .header(ContentType::Plain) - .sized_body(Cursor::new("No such key :-(")) - .finalize(), + .status(Status::NotFound) + .header(ContentType::Plain) + .sized_body(Cursor::new("No such key :-(")) + .finalize() + } } } #[get("/by-email/")] -fn by_email(db: rocket::State, email: String) - -> Response -{ +fn by_email(db: rocket::State, email: String) -> Response { + use rocket::http::{ContentType, Status}; use std::io::Cursor; - use rocket::http::{Status, ContentType}; let maybe_key = match Email::from_str(&email) { Ok(ref email) => db.by_email(email), @@ -165,21 +175,20 @@ fn by_email(db: rocket::State, email: String) match maybe_key { Some(ref bytes) => key_to_response(bytes), - None => + None => { Response::build() - .status(Status::NotFound) - .header(ContentType::Plain) - .sized_body(Cursor::new("No such key :-(")) - .finalize(), + .status(Status::NotFound) + .header(ContentType::Plain) + .sized_body(Cursor::new("No such key :-(")) + .finalize() + } } } #[get("/by-kid/")] -fn by_kid(db: rocket::State, kid: String) - -> Response -{ +fn by_kid(db: rocket::State, kid: String) -> Response { + use rocket::http::{ContentType, Status}; use std::io::Cursor; - use rocket::http::{Status, ContentType}; let maybe_key = match KeyID::from_str(&kid) { Ok(ref key) => db.by_kid(key), @@ -187,26 +196,24 @@ fn by_kid(db: rocket::State, kid: String) }; match maybe_key { - Some(ref bytes) => { - key_to_response(bytes) - } - None => + Some(ref bytes) => key_to_response(bytes), + None => { Response::build() - .status(Status::NotFound) - .header(ContentType::Plain) - .sized_body(Cursor::new("No such key :-(")) - .finalize(), + .status(Status::NotFound) + .header(ContentType::Plain) + .sized_body(Cursor::new("No such key :-(")) + .finalize() + } } - } #[get("/vks/verify/")] -fn verify(db: rocket::State, token: String) - -> result::Result> -{ +fn verify( + db: rocket::State, token: String, +) -> result::Result> { match db.verify_token(&token) { Ok(Some((userid, fpr))) => { - let context = templates::Verify{ + let context = templates::Verify { verified: true, userid: userid.to_string(), fpr: fpr.to_string(), @@ -215,7 +222,7 @@ fn verify(db: rocket::State, token: String) Ok(Template::render("verify", context)) } Ok(None) | Err(_) => { - let context = templates::Verify{ + let context = templates::Verify { verified: false, userid: "".into(), fpr: "".into(), @@ -227,58 +234,56 @@ fn verify(db: rocket::State, token: String) } #[get("/vks/delete/")] -fn delete(db: rocket::State, fpr: String, - tmpl: State, domain: State, from: State) - -> result::Result> -{ +fn delete( + db: rocket::State, fpr: String, tmpl: State, + domain: State, from: State, +) -> result::Result> { use mail::send_confirmation_mail; let fpr = match Fingerprint::from_str(&fpr) { Ok(fpr) => fpr, Err(_) => { - return Err(Custom(Status::BadRequest, - "Invalid fingerprint".to_string())); + return Err(Custom( + Status::BadRequest, + "Invalid fingerprint".to_string(), + )); } }; match db.request_deletion(fpr.clone()) { - Ok((token,uids)) => { - let context = templates::Delete{ + Ok((token, uids)) => { + let context = templates::Delete { fpr: fpr.to_string(), token: token.clone(), }; for uid in uids { - send_confirmation_mail(&uid, &token, &tmpl.0, &domain.0, &from.0) - .map_err(|err| { - Custom(Status::InternalServerError, - format!("{:?}", err)) - })?; + send_confirmation_mail( + &uid, &token, &tmpl.0, &domain.0, &from.0, + ) + .map_err(|err| { + Custom(Status::InternalServerError, format!("{:?}", err)) + })?; } Ok(Template::render("delete", context)) } - Err(e) => Err(Custom(Status::InternalServerError, - format!("{}", e))), + Err(e) => Err(Custom(Status::InternalServerError, format!("{}", e))), } } #[get("/vks/confirm/")] -fn confirm(db: rocket::State, token: String) - -> result::Result> -{ +fn confirm( + db: rocket::State, token: String, +) -> result::Result> { match db.confirm_deletion(&token) { Ok(true) => { - let context = templates::Confirm{ - deleted: true, - }; + let context = templates::Confirm { deleted: true }; Ok(Template::render("confirm", context)) } Ok(false) | Err(_) => { - let context = templates::Confirm{ - deleted: false, - }; + let context = templates::Confirm { deleted: false }; Ok(Template::render("confirm", context)) } @@ -291,22 +296,24 @@ fn files(file: PathBuf, static_dir: State) -> Option { } #[get("/pks/lookup")] -fn lookup(db: rocket::State, key: Option) - -> result::Result> -{ - use std::io::Write; +fn lookup( + db: rocket::State, key: Option, +) -> result::Result> { + use sequoia_openpgp::armor::{Kind, Writer}; use sequoia_openpgp::RevocationStatus; - use sequoia_openpgp::{TPK, parse::Parse}; - use sequoia_openpgp::armor::{Writer, Kind}; + use sequoia_openpgp::{parse::Parse, TPK}; + use std::io::Write; - let (maybe_key,index) = match key { - Some(queries::Hkp::Fingerprint{ ref fpr, index }) => { - (db.by_fpr(fpr),index) + let (maybe_key, index) = match key { + Some(queries::Hkp::Fingerprint { ref fpr, index }) => { + (db.by_fpr(fpr), index) } - Some(queries::Hkp::Email{ ref email, index }) => { - (db.by_email(email),index) + Some(queries::Hkp::Email { ref email, index }) => { + (db.by_email(email), index) + } + None => { + return Ok("nothing to do".to_string()); } - None => { return Ok("nothing to do".to_string()); } }; match maybe_key { @@ -314,8 +321,8 @@ fn lookup(db: rocket::State, key: Option) let key = || -> Result { let mut buffer = Vec::default(); { - let mut writer = Writer::new(&mut buffer, Kind::PublicKey, - &[])?; + let mut writer = + Writer::new(&mut buffer, Kind::PublicKey, &[])?; writer.write_all(&bytes)?; } @@ -324,17 +331,20 @@ fn lookup(db: rocket::State, key: Option) match key { Ok(s) => Ok(s), - Err(_) => - Err(Custom(Status::InternalServerError, - "Failed to ASCII armor key".to_string())), + Err(_) => { + Err(Custom( + Status::InternalServerError, + "Failed to ASCII armor key".to_string(), + )) + } } } None if !index => Ok("No such key :-(".to_string()), Some(ref bytes) if index => { - let tpk = TPK::from_bytes(bytes) - .map_err(|e| Custom(Status::InternalServerError, - format!("{}", e)))?; + let tpk = TPK::from_bytes(bytes).map_err(|e| { + Custom(Status::InternalServerError, format!("{}", e)) + })?; let mut out = String::default(); let p = tpk.primary(); @@ -363,16 +373,21 @@ fn lookup(db: rocket::State, key: Option) let algo: u8 = p.pk_algo().into(); out.push_str("info:1:1\r\n"); - out.push_str(&format!("pub:{}:{}:{}:{}:{}:{}{}\r\n", - p.fingerprint().to_string().replace(" ",""), - algo, - p.mpis().bits(), - ctime,extime,is_exp,is_rev)); + out.push_str(&format!( + "pub:{}:{}:{}:{}:{}:{}{}\r\n", + p.fingerprint().to_string().replace(" ", ""), + algo, + p.mpis().bits(), + ctime, + extime, + is_exp, + is_rev + )); for uid in tpk.userids() { let u = url::form_urlencoded::byte_serialize(uid.userid().userid()) - .fold(String::default(),|acc,x| acc + x); + .fold(String::default(), |acc, x| acc + x); let ctime = uid .binding_signature() .and_then(|x| x.signature_creation_time()) @@ -389,21 +404,22 @@ fn lookup(db: rocket::State, key: Option) if x.signature_expired() { "e" } else { "" }.into() }) .unwrap_or_default(); - let is_rev = - if uid.revoked(None) != RevocationStatus::NotAsFarAsWeKnow { - "r" - } else { - "" - }; + let is_rev = if uid.revoked(None) + != RevocationStatus::NotAsFarAsWeKnow + { + "r" + } else { + "" + }; - out.push_str(&format!("uid:{}:{}:{}:{}{}\r\n", - u,ctime,extime,is_exp,is_rev)); + out.push_str(&format!( + "uid:{}:{}:{}:{}{}\r\n", + u, ctime, extime, is_exp, is_rev + )); } Ok(out) } - None if index => { - Ok("info:1:0\r\n".into()) - } + None if index => Ok("info:1:0\r\n".into()), _ => unreachable!(), } @@ -424,14 +440,14 @@ pub fn serve(opt: &Opt, db: Polymorphic) -> Result<()> { Some(p) => { let addr = opt.listen[0..p].to_string(); let port = if p < opt.listen.len() - 1 { - u16::from_str(&opt.listen[p+1..]).ok().unwrap_or(8080) + u16::from_str(&opt.listen[p + 1..]).ok().unwrap_or(8080) } else { 8080 }; (addr, port) } - None => (opt.listen.to_string(), 8080) + None => (opt.listen.to_string(), 8080), }; let config = Config::build(Environment::Staging) @@ -439,10 +455,17 @@ pub fn serve(opt: &Opt, db: Polymorphic) -> Result<()> { .port(port) .workers(2) .root(opt.base.clone()) - .extra("template_dir", opt.base.join("templates").to_str() - .ok_or("Template path invalid")?) - .extra("static_dir", opt.base.join("public").to_str() - .ok_or("Static path invalid")?) + .extra( + "template_dir", + opt.base + .join("templates") + .to_str() + .ok_or("Template path invalid")?, + ) + .extra( + "static_dir", + opt.base.join("public").to_str().ok_or("Static path invalid")?, + ) .extra("domain", opt.domain.clone()) .extra("from", opt.from.clone()) .finalize()?; @@ -466,31 +489,24 @@ pub fn serve(opt: &Opt, db: Polymorphic) -> Result<()> { rocket::custom(config) .attach(Template::fairing()) .attach(AdHoc::on_attach("static_dir", |rocket| { - let static_dir = rocket.config() - .get_str("static_dir") - .unwrap() - .to_string(); + let static_dir = + rocket.config().get_str("static_dir").unwrap().to_string(); Ok(rocket.manage(StaticDir(static_dir))) })) .attach(AdHoc::on_attach("domain", |rocket| { - let domain = rocket.config() - .get_str("domain") - .unwrap() - .to_string(); + let domain = rocket.config().get_str("domain").unwrap().to_string(); Ok(rocket.manage(Domain(domain))) })) .attach(AdHoc::on_attach("from", |rocket| { - let from = rocket.config() - .get_str("from") - .unwrap() - .to_string(); + let from = rocket.config().get_str("from").unwrap().to_string(); Ok(rocket.manage(From(from))) })) .attach(AdHoc::on_attach("mail_templates", |rocket| { - let dir: PathBuf = rocket.config() + let dir: PathBuf = rocket + .config() .get_str("template_dir") .unwrap() .to_string() @@ -499,16 +515,23 @@ pub fn serve(opt: &Opt, db: Polymorphic) -> Result<()> { let confirm_txt = dir.join("confirm-email-txt.hbs"); let verify_html = dir.join("verify-email-html.hbs"); let verify_txt = dir.join("verify-email-txt.hbs"); - let mut handlebars = Handlebars::new(); + let mut handlebars = Handlebars::new(); - handlebars.register_template_file("confirm-html", confirm_html).unwrap(); - handlebars.register_template_file("confirm-txt", confirm_txt).unwrap(); - handlebars.register_template_file("verify-html", verify_html).unwrap(); - handlebars.register_template_file("verify-txt", verify_txt).unwrap(); + handlebars + .register_template_file("confirm-html", confirm_html) + .unwrap(); + handlebars + .register_template_file("confirm-txt", confirm_txt) + .unwrap(); + handlebars + .register_template_file("verify-html", verify_html) + .unwrap(); + handlebars + .register_template_file("verify-txt", verify_txt) + .unwrap(); Ok(rocket.manage(MailTemplates(handlebars))) })) - .mount("/", routes) .manage(db) .launch(); diff --git a/src/web/upload.rs b/src/web/upload.rs index 0b9ebd5..5369071 100644 --- a/src/web/upload.rs +++ b/src/web/upload.rs @@ -1,18 +1,18 @@ -use multipart::server::Multipart; use multipart::server::save::Entries; use multipart::server::save::SaveResult::*; +use multipart::server::Multipart; -use rocket::{State, Data}; use rocket::http::{ContentType, Status}; use rocket::response::status::Custom; +use rocket::{Data, State}; use rocket_contrib::templates::Template; use handlebars::Handlebars; -use types::Email; -use mail::send_verification_mail; -use web::{From, Domain, MailTemplates}; use database::{Database, Polymorphic}; +use mail::send_verification_mail; +use types::Email; +use web::{Domain, From, MailTemplates}; use std::io::Read; use std::str::FromStr; @@ -32,11 +32,10 @@ mod template { #[post("/pks/add", data = "")] // signature requires the request to have a `Content-Type` -pub fn multipart_upload(db: State, cont_type: &ContentType, - data: Data, tmpl: State, - domain: State, from: State) - -> Result> -{ +pub fn multipart_upload( + db: State, cont_type: &ContentType, data: Data, + tmpl: State, domain: State, from: State, +) -> Result> { if cont_type.is_form_data() { // multipart/form-data let (_, boundary) = cont_type.params().find(|&(k, _)| k == "boundary").ok_or_else( @@ -55,8 +54,11 @@ pub fn multipart_upload(db: State, cont_type: &ContentType, let mut buf = Vec::default(); data.stream_to(&mut buf).or_else(|_| { - Err(Custom(Status::BadRequest, - "`Content-Type: application/x-www-form-urlencoded` not valid".into())) + Err(Custom( + Status::BadRequest, + "`Content-Type: application/x-www-form-urlencoded` not valid" + .into(), + )) })?; for item in FormItems::from(&*String::from_utf8_lossy(&buf)) { @@ -68,8 +70,13 @@ pub fn multipart_upload(db: State, cont_type: &ContentType, match key.as_str() { "keytext" => { - return process_key(Cursor::new(decoded_value.as_bytes()), - &db, &tmpl.0, &domain.0, &from.0); + return process_key( + Cursor::new(decoded_value.as_bytes()), + &db, + &tmpl.0, + &domain.0, + &from.0, + ); } _ => { /* skip */ } } @@ -81,24 +88,28 @@ pub fn multipart_upload(db: State, cont_type: &ContentType, } } -fn process_upload(boundary: &str, data: Data, db: &Polymorphic, mail_templates: &Handlebars, - domain: &str, from: &str) - -> Result> -{ +fn process_upload( + boundary: &str, data: Data, db: &Polymorphic, mail_templates: &Handlebars, + domain: &str, from: &str, +) -> Result> { // saves all fields, any field longer than 10kB goes to a temporary directory // Entries could implement FromData though that would give zero control over // how the files are saved; Multipart would be a good impl candidate though match Multipart::with_body(data.open(), boundary).save().temp() { - Full(entries) => process_multipart(entries, db, mail_templates, domain, from), - Partial(partial, _) => process_multipart(partial.entries, db, mail_templates, domain, from), + Full(entries) => { + process_multipart(entries, db, mail_templates, domain, from) + } + Partial(partial, _) => { + process_multipart(partial.entries, db, mail_templates, domain, from) + } Error(err) => Err(Custom(Status::InternalServerError, err.to_string())), } } -fn process_multipart(entries: Entries, db: &Polymorphic, mail_templates: &Handlebars, - domain: &str, from: &str) - -> Result> -{ +fn process_multipart( + entries: Entries, db: &Polymorphic, mail_templates: &Handlebars, + domain: &str, from: &str, +) -> Result> { match entries.fields.get("keytext") { Some(ent) if ent.len() == 1 => { let reader = ent[0].data.readable().map_err(|err| { @@ -107,48 +118,72 @@ fn process_multipart(entries: Entries, db: &Polymorphic, mail_templates: &Handle process_key(reader, db, mail_templates, domain, from) } - Some(_) | None => - Err(Custom(Status::BadRequest, "Not a PGP public key".into())), + Some(_) | None => { + Err(Custom(Status::BadRequest, "Not a PGP public key".into())) + } } } -fn process_key(reader: R, db: &Polymorphic, mail_templates: &Handlebars, domain: &str, from: &str) - -> Result> where R: Read +fn process_key( + reader: R, db: &Polymorphic, mail_templates: &Handlebars, domain: &str, + from: &str, +) -> Result> +where + R: Read, { - use sequoia_openpgp::TPK; use sequoia_openpgp::parse::Parse; + use sequoia_openpgp::TPK; match TPK::from_reader(reader) { Ok(tpk) => { match db.merge_or_publish(tpk) { Ok(tokens) => { let tokens = tokens - .into_iter().map(|(uid,tok)| { - template::Token{ userid: uid.to_string(), token: tok } - }).collect::>(); + .into_iter() + .map(|(uid, tok)| { + template::Token { + userid: uid.to_string(), + token: tok, + } + }) + .collect::>(); // send out emails for tok in tokens.iter() { - let &template::Token{ ref userid, ref token } = tok; + let &template::Token { ref userid, ref token } = tok; - Email::from_str(userid).and_then(|email| { - send_verification_mail(&email, token, mail_templates, domain, from) - }).map_err(|err| { - Custom(Status::InternalServerError, format!("{:?}", err)) - })?; + Email::from_str(userid) + .and_then(|email| { + send_verification_mail( + &email, + token, + mail_templates, + domain, + from, + ) + }) + .map_err(|err| { + Custom( + Status::InternalServerError, + format!("{:?}", err), + ) + })?; } - let context = template::Verify{ - tokens: tokens - }; + let context = template::Verify { tokens: tokens }; Ok(Template::render("upload", context)) } - Err(err) => - Err(Custom(Status::InternalServerError, - format!("{:?}", err))), + Err(err) => { + Err(Custom( + Status::InternalServerError, + format!("{:?}", err), + )) + } } } - Err(_) => Err(Custom(Status::BadRequest, "Not a PGP public key".into())), + Err(_) => { + Err(Custom(Status::BadRequest, "Not a PGP public key".into())) + } } }